牛客网C++面试宝典(一)C/C++基础之语言基础

news2024/10/5 16:26:45

此系列为在学习牛客网C++面试宝典过程中记录的笔记,本篇记录第一章C/C++基础部分的第一节:语言基础。

牛客网C++面试宝典链接:https://www.nowcoder.com/issue/tutorial?tutorialId=93&uuid=a34ed23d58b84da3a707c70371f59c21

文章目录

    • 1.1 简述下C++语言的特点
    • 1.2 说说C语言和C++的区别
    • 1.3 说说 C++中 struct 和 class 的区别
    • 1.4 说说include头文件的顺序以及双引号""和尖括号<>的区别
    • 1.6 导入C函数的关键字是什么,C++编译时和C有什么不同?
    • 1.7 简述C++从代码到可执行二进制文件的过程
    • 1.8 说说 static关键字的作用
    • 1.9 说说数组和指针的区别
    • 1.10 说说什么是函数指针,如何定义函数指针,有什么使用场景
    • 1.11 说说静态变量什么时候初始化?
    • 1.12 nullptr调用成员函数可以吗?为什么?
    • 1.13 说说什么是野指针,怎么产生的,如何避免?
    • 1.14 说说静态局部变量,全局变量,局部变量的特点,以及使用场景
    • 1.15 说说内联函数和宏函数的区别
    • 1.17 说说new和malloc的区别,各自底层实现原理。
    • 1.18 说说const和define的区别。
    • 1.19 说说C++中函数指针和指针函数的区别。
    • 1.20 说说const int *a, int const *a, const int a, int *const a, const int *const a分别是什么,有什么特点。
    • 1.21 说说使用指针需要注意什么?
    • 1.22 说说内联函数和函数的区别,内联函数的作用。
    • 1.23 简述C++有几种传值方式,之间的区别是什么?
    • 1.24 简述const(星号)和(星号)const的区别

1.1 简述下C++语言的特点

参考回答

  • C++在C语言基础上引入了面对对象的机制,同时也兼容C语言。

  • C++有三大特性(1)封装。(2)继承。(3)多态;

  • C++语言编写出的程序结构清晰、易于扩充,程序可读性好。

  • C++生成的代码质量高,运行效率高,仅比汇编语言慢10%~20%;

  • C++更加安全,增加了const常量、引用、四类cast转换(static_cast、dynamic_cast、const_cast、reinterpret_cast)、智能指针、try—catch等等;

  • C++可复用性高,C++引入了模板的概念,后面在此基础上,实现了方便开发的标准模板库STL(Standard Template Library)。

  • 同时,C++是不断在发展的语言。C++后续版本更是发展了不少新特性,如C++11中引入了nullptr、auto变量、Lambda匿名函数、右值引用、智能指针。

1.2 说说C语言和C++的区别

参考回答

  • C语言是C++的子集,C++可以很好兼容C语言。但是C++又有很多新特性,如引用、智能指针、auto变量等。

  • C++是面对对象的编程语言;C语言是面对过程的编程语言。

  • C语言有一些不安全的语言特性,如指针使用的潜在危险、强制转换的不确定性、内存泄露等。而C++对此增加了不少新特性来改善安全性,如const常量、引用、cast转换、智能指针、try—catch等等;

  • C++可复用性高,C++引入了模板的概念,后面在此基础上,实现了方便开发的标准模板库STL。C++的STL库相对于C语言的函数库更灵活、更通用。

1.3 说说 C++中 struct 和 class 的区别

参考回答

  • struct 一般用于描述一个数据结构集合,而 class 是对一个对象数据的封装;

  • struct 中默认的访问控制权限是 public 的,而 class 中默认的访问控制权限是 private 的,例如:

struct A{  int iNum; // 默认访问控制权限是 public } class B{  int iNum; // 默认访问控制权限是 private }
  • 在继承关系中,struct 默认是公有继承,而 class 是私有继承;

  • class 关键字可以用于定义模板参数,就像 typename,而 struct 不能用于定义模板参数,例如:

template<typename T, typename Y> // 可以把typename 换成 class  int Func(const T& t, const Y& y) {      //TODO  }

答案解析

  • C++ 中的 struct 是对 C 中的 struct 进行了扩充,它们在声明时的区别如下:
    在这里插入图片描述
  • 使用时的区别:C 中使用结构体需要加上 struct 关键字,或者对结构体使用 typedef 取别名,而 C++ 中可以省略 struct 关键字直接使用,例如:
struct Student
{ 
  int  iAgeNum; 
  string strName; 
} typedef struct Student Student2; //C中取别名  
struct Student stu1; // C 中正常使用 
Student2 stu2;   // C 中通过取别名的使用 
Student stu3;   // C++ 中使用

1.4 说说include头文件的顺序以及双引号""和尖括号<>的区别

  • 区别:

(1)尖括号<>的头文件是系统文件,双引号""的头文件是自定义文件。

(2)编译器预处理阶段查找头文件的路径不一样。

  • 查找路径:

(1)使用尖括号<>的头文件的查找路径:编译器设置的头文件路径–>系统变量。

(2)使用双引号""的头文件的查找路径:当前头文件目录–>编译器设置的头文件路径–>系统变量。

1.6 导入C函数的关键字是什么,C++编译时和C有什么不同?

参考回答

  • 关键字:在C++中,导入C函数的关键字是extern,表达形式为extern “C”extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。

  • 编译区别:由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

答案解析

//extern示例 
//在C++程序里边声明该函数,会指示编译器这部分代码按C语言的进行编译
 extern "C" int strcmp(const char *s1, const char *s2);  
//在C++程序里边声明该函数
 extern "C"{ #include <string.h>//string.h里边包含了要调用的C函数的声明 }  
 //两种不同的语言,有着不同的编译规则,比如一个函数fun,可能C语言编译的时候为_fun,而C++则是__fun__

1.7 简述C++从代码到可执行二进制文件的过程

参考回答

    C++和C语言类似,一个C++程序从源码到执行文件,有四个过程,预编译、编译、汇编、链接。

答案解析

1、预编译:这个过程主要的处理操作如下:

(1) 将所有的#define删除,并且展开所有的宏定义

(2) 处理所有的条件预编译指令,如#if、#ifdef

(3) 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。

(4) 过滤所有的注释

(5) 添加行号和文件名标识。

2、编译:这个过程主要的处理操作如下:

(1) 词法分析:将源代码的字符序列分割成一系列的记号。

(2) 语法分析:对记号进行语法分析,产生语法树。

(3) 语义分析:判断表达式是否有意义。

(4) 代码优化:

(5) 目标代码生成:生成汇编代码。

(6) 目标代码优化:

3、汇编:这个过程主要是将汇编代码转变成机器可以执行的指令。

4、链接:将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。

链接分为静态链接和动态链接。

静态链接,是在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行;生成的静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。

而动态链接,是在链接的时候没有把调用的函数代码链接进去,而是在执行的过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行程序就不能运行。生成的动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。

1.8 说说 static关键字的作用

参考回答

1、定义全局静态变量和局部静态变量:在变量前面加上static关键字。初始化的静态变量会在数据段分配内存,未初始化的静态变量会在BSS段分配内存。直到程序结束,静态变量始终会维持前值。只不过全局静态变量和局部静态变量的作用域不一样;

2、定义静态函数:在函数返回类型前加上static关键字,函数即被定义为静态函数。静态函数只能在本源文件中使用;

3、在变量类型前加上static关键字,变量即被定义为静态变量。静态变量只能在本源文件中使用;

//示例 static int a; static void func();

4、在c++中,static关键字可以用于定义类中的静态成员变量:使用静态数据成员,它既可以被当成全局变量那样去存储,但又被隐藏在类的内部。类中的static静态数据成员拥有一块单独的存储区,而不管创建了多少个该类的对象。所有这些对象的静态数据成员都共享这一块静态存储空间。

5、在c++中,static关键字可以用于定义类中的静态成员函数:与静态成员变量类似,类里面同样可以定义静态成员函数。只需要在函数前加上关键字static即可。如静态成员函数也是类的一部分,而不是对象的一部分。所有这些对象的静态数据成员都共享这一块静态存储空间。

答案解析

当调用一个对象的非静态成员函数时,系统会把该对象的起始地址赋给成员函数的this指针。而静态成员函数不属于任何一个对象,因此C++规定静态成员函数没有this指针(划重点,面试题常考)。既然它没有指向某一对象,也就无法对一个对象中的非静态成员进行访问。

1.9 说说数组和指针的区别

参考回答

1、概念:

(1)数组:数组是用于储存多个相同类型数据的集合。 数组名是首元素的地址。

(2)指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。 指针名指向了内存的首地址。

2、区别:

(1)赋值:同类型指针变量可以相互赋值;数组不行,只能一个一个元素的赋值或拷贝

(2)存储方式:

数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,数组的存储空间,不是在静态区就是在栈上。

指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。

(3)求sizeof:

数组所占存储空间的内存大小:sizeof(数组名)/sizeof(数据类型)

在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。

(4)初始化:

// 数组 
int a[5] = { 0 };
 char b[] = "Hello"; // 按字符串初始化,大小为6 
 char c[] = { 'H','e','l','l','o','\0' }; // 按字符初始化 
 int* arr = new int[10]; // 动态创建一维数组  
 
 // 指针 
 // 指向对象的指针 
 int* p = new int(0); delete p; 

// 指向数组的指针 int* p1 = new int[10]; delete[] p1; 

// 指向类的指针: string* p2 = new string; delete p2; 

// 指向指针的指针(二级指针) int** pp = &p; **pp = 10;

(5)指针操作:

数组名的指针操作
int a[3][4];   
int (*p)[4];  //该语句是定义一个数组指针,指向含4个元素的一维数组
p = a;    //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] 
p++;      //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]   //所以数组指针也
称指向一维数组的指针,亦称行指针。 
//访问数组中第i行j列的一个元素,有几种操作方式:
 *(p[i]+j)*(*(p+i)+j)(*(p+i))[j]、
 p[i][j]。
 其中,优先级:()>[]>*//这几种操作方式都是合法的。

指针变量的数据操作:

char *str = "hello,douya!";
str[2] = 'a';
*(str+2) = 'b';
//这两种操作方式都是合法的。

1.10 说说什么是函数指针,如何定义函数指针,有什么使用场景

参考回答

概念:函数指针就是指向函数的指针变量。每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。

定义形式如下:

int func(int a);  
int (*f)(int a);  
f = &func;  

函数指针的应用场景回调(callback)。我们调用别人提供的 API函数(Application Programming Interface,应用程序编程接口),称为Call;如果别人的库里面调用我们的函数,就叫Callback。

答案解析

//以库函数qsort排序函数为例,它的原型如下:
void qsort(void *base,//void*类型,代表原始数组
           size_t nmemb, //第二个是size_t类型,代表数据数量
           size_t size, //第三个是size_t类型,代表单个数据占用空间大小
           int(*compar)(const void *,const void *)//第四个参数是函数指针
          );
//第四个参数告诉qsort,应该使用哪个函数来比较元素,即只要我们告诉qsort比较大小的规则,它就可以帮我们对任意数据类型的数组进行排序。在库函数qsort调用我们自定义的比较函数,这就是回调的应用。

//示例
int num[100];
int cmp_int(const void* _a , const void* _b){//参数格式固定
    int* a = (int*)_a;    //强制类型转换
    int* b = (int*)_b;
    return *a - *b;  
}
qsort(num,100,sizeof(num[0]),cmp_int); //回调

1.11 说说静态变量什么时候初始化?

参考回答

对于C语言的全局和静态变量,初始化发生在任何代码执行之前,属于编译期初始化。

而C++标准规定:全局或静态对象当且仅当对象首次用到时才进行构造。

答案解析

1、作用域:C++里作用域可分为6种:全局,局部,类,语句,命名空间和文件作用域。

静态全局变量 :全局作用域+文件作用域,所以无法在其他文件中使用。

静态局部变量 :局部作用域,只被初始化一次,直到程序结束。

类静态成员变量:类作用域。

2、所在空间:都在静态存储区。因为静态变量都在静态存储区,所以下次调用函数的时候还是能取到原来的值。

3、生命周期:静态全局变量、静态局部变量都在静态存储区,直到程序结束才会回收内存。类静态成员变量在静态存储区,当超出类作用域时回收内存。

1.12 nullptr调用成员函数可以吗?为什么?

参考回答

能。

原因:因为在编译时对象就绑定了函数地址,和指针空不空没关系。

答案解析

//给出实例
class animal{
public:
    void sleep(){ cout << "animal sleep" << endl; }
    void breathe(){ cout << "animal breathe haha" << endl; }
};
class fish :public animal{
public:
    void breathe(){ cout << "fish bubble" << endl; }
};
int main(){
    animal *pAn=nullptr;
    pAn->breathe();   // 输出:animal breathe haha
    fish *pFish = nullptr;
    pFish->breathe(); // 输出:fish bubble
    return 0;
}  

原因:因为在编译时对象就绑定了函数地址,和指针空不空没关系。pAn->breathe();编译的时候,函数的地址就和指针pAn绑定了;调用breath(*this), this就等于pAn。由于函数中没有需要解引用this的地方,所以函数运行不会出错,但是若用到this,因为this=nullptr,运行出错。

1.13 说说什么是野指针,怎么产生的,如何避免?

参考回答

1、概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

2、产生原因:释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。这些我们都要注意避免。

3、避免办法:

(1)初始化置NULL

(2)申请内存后判空

(3)指针释放后置NULL

(4)使用智能指针

答案解析

产生原因:释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。这些我们都要注意避免。如:

char *p = (char *)malloc(sizeof(char)*100);  
strcpy(p, "Douya");  
free(p);//p所指向的内存被释放,但是p所指的地址仍然不变  
...  
if (p != NULL){//没有起到防错作用  
    strcpy(p, "hello, Douya!");//出错  
}  

避免办法

(1)初始化置NULL

(2)申请内存后判空

(3)指针释放后置NULL

int *p = NULL; //初始化置NULL
p = (int *)malloc(sizeof(int)*n); //申请n个int内存空间  
assert(p != NULL); //判空,防错设计
p = (int *) realloc(p, 25);//重新分配内存, p 所指向的内存块会被释放并分配一个新的内存地址
free(p);  
p = NULL; //释放后置空

int *p1 = NULL; //初始化置NULL
p1 = (int *)calloc(n, sizeof(int)); //申请n个int内存空间同时初始化为0 
assert(p1 != NULL); //判空,防错设计
free(p1);  
p1 = NULL; //释放后置空

int *p2 = NULL; //初始化置NULL
p2 = new int[n]; //申请n个int内存空间  
assert(p2 != NULL); //判空,防错设计
delete []p2;  
p2 = nullptr; //释放后置空  

1.14 说说静态局部变量,全局变量,局部变量的特点,以及使用场景

参考回答

1、首先从作用域考虑:C++里作用域可分为6种:全局,局部,类,语句,命名空间和文件作用域。

全局变量:全局作用域,可以通过extern作用于其他非定义的源文件。

静态全局变量 :全局作用域+文件作用域,所以无法在其他文件中使用。

局部变量:局部作用域,比如函数的参数,函数内的局部变量等等。

静态局部变量 :局部作用域,只被初始化一次,直到程序结束。

2、从所在空间考虑:除了局部变量在栈上外,其他都在静态存储区。因为静态变量都在静态存储区,所以下次调用函数的时候还是能取到原来的值。

3、生命周期: 局部变量在栈上,出了作用域就回收内存;而全局变量、静态全局变量、静态局部变量都在静态存储区,直到程序结束才会回收内存。

4、使用场景:从它们各自特点就可以看出各自的应用场景,不再赘述。

1.15 说说内联函数和宏函数的区别

参考回答

区别:

1、宏定义不是函数,但是使用起来像函数。预处理器用复制宏代码的方式代替函数的调用,省去了函数压栈退栈过程,提高了效率;而内联函数本质上是一个函数,内联函数一般用于函数体的代码比较简单的函数,不能包含复杂的控制语句,while、switch,并且内联函数本身不能直接调用自身。

2、宏函数是在预编译的时候把所有的宏名用宏体来替换,简单的说就是字符串替换 ;而内联函数则是在编译的时候进行代码插入,编译器会在每处调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数的调用的开销,提高效率

3、宏定义是没有类型检查的,无论对还是错都是直接替换;而内联函数在编译的时候会进行类型的检查,内联函数满足函数的性质,比如有返回值、参数列表等

答案解析

//宏定义示例
#define MAX(a, b) ((a)>(b)?(a):(b))
MAX(a, "Hello"); //错误地比较int和字符串,没有参数类型检查

//内联函数示例
#include <stdio.h>
inline int add(int a, int b) {
    return (a + b);
}
int main(void) {
    int a;
    a = add(1, 2);
    printf("a+b=%d\n", a);
    return 0;
}
//以上a = add(1, 2);处在编译时将被展开为:a = (a + b);

1、使用时的一些注意事项

  • 使用宏定义一定要注意错误情况的出现,比如宏定义函数没有类型检查,可能传进来任意类型,从而带来错误,如举例。还有就是括号的使用,宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性

  • inline函数一般用于比较小的,频繁调用的函数,这样可以减少函数调用带来的开销。只需要在函数返回类型前加上关键字inline,即可将函数指定为inline函数。

  • 同其它函数不同的是,最好将inline函数定义在头文件,而不仅仅是声明,因为编译器在处理inline函数时,需要在调用点内联展开该函数,所以仅需要函数声明是不够的。

2、内联函数使用的条件

  • 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率 的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:

  • (1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。

  • (2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

  • 内联不是什么时候都能展开的,一个好的编译器将会根据函数的定义体,自动地取消不符合要求的内联。

1.16 说说运算符i++和++i的区别
参考回答

先看到实现代码:

#include <stdio.h>
int main(){
       int i = 2;
    int j = 2;
    j += i++; //先赋值后加
    printf("i= %d, j= %d\n",i, j); //i= 3, j= 4
    i = 2;
    j = 2;
    j += ++i; //先加后赋值
    printf("i= %d, j= %d",i, j); //i= 3, j= 5
}

1、赋值顺序不同:++ i 是先加后赋值;i ++ 是先赋值后加;++i和i++都是分两步完成的。

2、效率不同:后置++执行速度比前置的慢。

3、i++ 不能作为左值,而++i 可以

int i = 0;
int* p1 = &(++i);//正确
// int* p2 = &(i++);//错误
++i = 1;//正确
// i++ = 1;//错误

4、两者都不是原子操作。

1.17 说说new和malloc的区别,各自底层实现原理。

参考回答

1、new是操作符,而malloc是函数。

2、new在调用的时候先分配内存,在调用构造函数,释放的时候调用析构函数;而malloc没有构造函数和析构函数。

3、malloc需要给定申请内存的大小,返回的指针需要强转;new会调用构造函数,不用指定内存的大小,返回指针不用强转。

4、new可以被重载;malloc不行

5、new分配内存更直接和安全。

6、new发生错误抛出异常,malloc返回null

答案解析

malloc底层实现:当开辟的空间小于 128K 时,调用 brk()函数;当开辟的空间大于 128K 时,调用mmap()。malloc采用的是内存池的管理方式,以减少内存碎片。先申请大块内存作为堆区,然后将堆区分为多个内存块。当用户申请内存时,直接从堆区分配一块合适的空闲快。采用隐式链表将所有空闲块,每一个空闲块记录了一个未分配的、连续的内存地址。

new底层实现:关键字new在调用构造函数的时候实际上进行了如下的几个步骤:

1、创建一个新的对象

2、将构造函数的作用域赋值给这个新的对象(因此this指向了这个新的对象)

3、执行构造函数中的代码(为这个新对象添加属性)

4、返回新对象

1.18 说说const和define的区别。

参考回答

const用于定义常量;而define用于定义宏,而宏也可以用于定义常量。都用于常量定义时,它们的区别有:

1、const生效于编译的阶段;define生效于预处理阶段。

2、const定义的常量,在C语言中是存储在内存中、需要额外的内存空间的;define定义的常量,运行时是直接的操作数,并不会存放在内存中。

3、const定义的常量是带类型的;define定义的常量不带类型。因此define定义的常量不利于类型检查。

1.19 说说C++中函数指针和指针函数的区别。

参考回答

1、定义不同 指针函数本质是一个函数,其返回值为指针。 函数指针本质是一个指针,其指向一个函数。

2、写法不同

指针函数:int *fun(int x,int y);
函数指针:int (*fun)(int x,int y);

3、用法不同

用法参考答案解析

答案解析

//指针函数示例
typedef struct _Data{
    int a;
    int b;
}Data;
//指针函数
Data* f(int a,int b){
    Data * data = new Data;
    //...
    return data;
}
int main(){
    //调用指针函数
    Data * myData = f(4,5);
    //Data * myData = static_cast<Data*>(f(4,5));
   //...
}

//函数指针示例
int add(int x,int y){
    return x+y;
}
//函数指针
int (*fun)(int x,int y);
//赋值
fun = add;
//调用
cout << "(*fun)(1,2) = " << (*fun)(1,2) ;
//输出结果
//(*fun)(1,2) =  3

1.20 说说const int *a, int const *a, const int a, int *const a, const int *const a分别是什么,有什么特点。

参考回答

1. const int a;     //指的是a是一个常量,不允许修改。
2. const int *a;    //a指针所指向的内存里的值不变,即(*a)不变
3. int const *a;    //同const int *a;
4. int *const a;    //a指针所指向的内存地址不变,即a不变
5. const int *const a;   //都不变,即(*a)不变,a也不变

1.21 说说使用指针需要注意什么?

参考回答

1、定义指针时,先初始化为NULL。

2、用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

3、不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

4、避免数字或指针的下标越界,特别要当心发生“多1”或者“少1”操作

5、动态内存的申请与释放必须配对,防止内存泄漏

6、用free或delete释放了内存之后,立即将指针设置为NULL,防止“野指针”

答案解析

(1)初始化置NULL

(2)申请内存后判空

(3)指针释放后置NULL

int *p = NULL; //初始化置NULL
p = (int *)malloc(sizeof(int)*n); //申请n个int内存空间  
assert(p != NULL); //判空,防错设计
p = (int *) realloc(p, 25);//重新分配内存, p 所指向的内存块会被释放并分配一个新的内存地址
free(p);  
p = NULL; //释放后置空

int *p1 = NULL; //初始化置NULL
p1 = (int *)calloc(n, sizeof(int)); //申请n个int内存空间同时初始化为0 
assert(p1 != NULL); //判空,防错设计
free(p1);  
p1 = NULL; //释放后置空

int *p2 = NULL; //初始化置NULL
p2 = new int[n]; //申请n个int内存空间  
assert(p2 != NULL); //判空,防错设计
delete []p2;  
p2 = nullptr; //释放后置空  

1.22 说说内联函数和函数的区别,内联函数的作用。

参考回答

1、内联函数比普通函数多了关键字inline

2、内联函数避免了函数调用的开销;普通函数有调用的开销

3、普通函数在被调用的时候,需要寻址(函数入口地址);内联函数不需要寻址。

4、内联函数有一定的限制,内联函数体要求代码简单,不能包含复杂的结构控制语句;普通函数没有这个要求。

内联函数的作用:内联函数在调用时,是将调用表达式用内联函数体来替换。避免函数调用的开销。

答案解析

在使用内联函数时,应注意如下几点:

1、在内联函数内不允许用循环语句和开关语句。 如果内联函数有这些语句,则编译将该函数视同普通函数那样产生函数调用代码,递归函数是不能被用来做内联函数的。内联函数只适合于只有1~5行的小函数。对一个含有许多语句的大函数,函数调用和返回的开销相对来说微不足道,所以也没有必要用内联函数实现。

2、内联函数的定义必须出现在内联函数第一次被调用之前。

1.23 简述C++有几种传值方式,之间的区别是什么?

参考回答

传参方式有这三种:值传递引用传递指针传递

1、值传递:形参即使在函数体内值发生变化,也不会影响实参的值;

2、引用传递:形参在函数体内值发生变化,会影响实参的值;

3、指针传递:在指针指向没有发生改变的前提下,形参在函数体内值发生变化,会影响实参的值;

答案解析

值传递用于对象时,整个对象会拷贝一个副本,这样效率低;而引用传递用于对象时,不发生拷贝行为,只是绑定对象,更高效;指针传递同理,但不如引用传递安全。

代码示例

//代码示例
#include <iostream>
using namespace std;

void testfunc(int a, int *b, int &c){//形参a值发生了改变,但是没有影响实参i的值;但形参*b、c的值发生了改变,影响到了实参*j、k的值
    a += 1;
    (*b) += 1;
    c += 1;
    printf("a= %d, b= %d, c= %d\n",a,*b,c);//a= 2, b= 2, c= 2
}
int main(){
       int i = 1;
    int a = 1;
    int *j = &a;
    int k = 1;
    testfunc(i, j, k);
    printf("i= %d, j= %d, k= %d\n",i,*j,k);//i= 1, j= 2, k= 2
    return 0;
}

1.24 简述const(星号)和(星号)const的区别

参考回答

//const* 是常量指针,*const 是指针常量
int const *a;    //a指针所指向的内存里的值不变,即(*a)不变
int *const a;    //a指针所指向的内存地址不变,即a不变

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/598833.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 数据持久化方案详解

目录 一、Docker数据持久化概述 1.1联合文件系统 1.2容器的数据卷 1.2.1 什么是数据卷 1.2.2 数据卷特点 1.2.3 Docker提供三种方式将数据从宿主机挂载到容器 二、 Docker持久化方案 2.1 查看volume 基本命令使用方法 2.2 volume持久化方案 2.2.1volume简介 2.2.2.v…

【JavaSE】Java基础语法(四十一):TCP通信程序

文章目录 1. TCP发送数据2. TCP接收数据【应用】3. TCP程序练习4. TCP程序文件上传练习【应用】 1. TCP发送数据 Java中的TCP通信 Java对基于TCP协议的的网络提供了良好的封装&#xff0c;使用Socket对象来代表两端的通信端口&#xff0c;并通过Socket产生IO流来进行网络通信。…

Android 易忽略小知识

1.设置hint的字体大小 在Android xml文件中并没有直接设置hint字体大小的属性。如果hint文字的大小不希望跟正常字体的大小一样&#xff0c;就只能通过代码的方式来进行处理。 提供两种方式&#xff1a; //设置"用户名"提示文字的大小 EditText etUserName (Ed…

教育硬件“老玩家”进入智能手机新赛道,小度胜算几何?

从5月8日有传言称“百度旗下小度将进军智能手机市场”&#xff0c;到5月17日小度官宣将推出旗下新物种产品——小度青禾学习手机&#xff0c;小度在短短10天时间成为市场关注的焦点。 而5月22日&#xff0c;其也拿出了真正的成果&#xff0c;这部专门为青少年打造的学习手机正…

MySQL——在Linux环境下安装(在线安装)

MySQL的安装&#xff08;在线安装&#xff09; mysql的安装并不是比赛的内容&#xff0c;所以我们用比较方便的在线安装的方法&#xff0c;比起安装&#xff0c;我们更要知道如何去使用&#xff1a; 首先看一下自己有没有安装MySQL的服务&#xff0c;或者自己的服务器上有没有…

application.yml中的配置怎么写

1.问题 application.yml中可以做很多组件的配置,比如redis,mongo, 但是这些的key是什么,value怎么写呢? 2.分析问题 为了搞清楚这个问题,我们需要先了解application.yml中的配置是怎么加载的,以MongoProperties配置加载为例, 在Spring Boot中,可以使用application.y…

【OJ比赛日历】快周末了,不来一场比赛吗? #06.03-06.09 #18场

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息仅供参考&#xff0c;以比赛官网为准 目录 2023-06-03&#xff08;周六&#xff09; #7场比赛2023-06-04…

【数据结构】---二叉树类型部分练习解析让你更深程度了解二叉树

文章目录 前言&#x1f31f;一、第一种&#xff1a;二叉树性质类型&#xff1a;&#x1f30f;1.1 第一题&#xff1a;&#x1f4ab;1.1.1 理论&#xff1a;&#x1f4ab;1.1.2 图解&#xff1a;&#x1f4ab;1.1.3 解析&#xff1a; &#x1f30f;1.2 第二题&#xff1a;&#…

小明给大家分享几个CSDN涨粉小技巧,期待大家多多涨粉

今天和大家一起来聊聊CSDN涨粉相关几个的技巧&#xff0c;希望对热爱技术分享&#xff0c;并且想快速涨粉提升自身曝光度的朋友们提供一些经验&#xff0c;本文都是自己的一些想法&#xff0c;有说的不对的地方希望大家指正&#xff01; 一、个人介绍 我叫小明&#xff0c;我的…

京东国际销售数据查询(京东国际行业/品牌数据分析)

根据京东平台官方数据显示&#xff0c;今年京东国际的贡献力度也高于以往。 京东618开门红5分钟&#xff0c;京东国际成交额已突破去年开门红前两小时的成交额&#xff0c;跨境酒水、跨境手机通讯、跨境箱包皮具等3个品类成交额同比增长超100%。&#xff0c;开门5分钟&#xff…

Unreal5 第三人称射击游戏 角色基础制作2

接上一篇 Unreal5 第三人称射击游戏 角色基础制作1 角色蹲伏效果 上面是需要的操作映射&#xff0c;蹲伏实现&#xff0c;首先要开启相应功能&#xff0c;你需要在角色移动组件上面开启可蹲伏 蹲伏还有一些其它设置&#xff0c;比如蹲下角色高度&#xff0c;蹲下以后行走的…

MySQL基础2

一.常见数据类型 数值类型&#xff1a; TINYINT&#xff1a;占用1字节&#xff0c;默认为有符号 BIT(M)&#xff1a;位类型&#xff1a;M指定位数&#xff0c;默认值为1&#xff0c;范围为1-64&#xff0c;bit类型在显示时&#xff0c;按照ASCII码对应的值进行显示。 FLOAT[(M,…

华为OD机试(41-60)老题库解析Java源码系列连载ing

华为OD机试算法题新老题库练习及源码 41.寻找相同子串42.找出经过特定点的路径长度43.全量和已占用字符集44.密钥格式化45.数字字符串组合倒序 郑重声明&#xff1a; 1.博客中涉及题目为网上搜索而来&#xff0c;若侵权&#xff0c;请联系作者删除。 源码内容为个人原创&#…

git --- git merge用法

1 git merge介绍 在Git中,合并是一个连接分叉历史的过程。它将两个或多个开发历史连接在一起。git merge命令可以帮助你把git分支创建的数据整合到一个分支中。git merge会将一系列的提交关联到一个统一的历史。 在上图中,有两个分支 master 和 feature。我们可以看到,我们…

《priority_queue》

本文主要介绍优先级队列的使用&#xff0c;以及一个TOPK问题的OJ 文章目录 一、priority_queue的介绍二、priority_queue的使用三、[数组中第k个大的元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/) 一、priority_queue的介绍 优先队列是一种容器适配器…

代码创造童话--Python为六一儿童节送专属礼物

前言&#xff1a; Hello大家好&#xff0c;我是Dream。 六一儿童节到啦&#xff0c;祝所有的朋友们六一儿童节快乐&#xff01; 在这个节日里&#xff0c;孩子们可以接受父母、老师、社会各界人士的关爱和祝福&#xff0c;同时也可以享受到各种各样的礼物和活动。Python作为一门…

第十五篇、基于Arduino uno,获取mpu6050三轴加速度、角速度、温度的数据——结果导向

0、结果 说明&#xff1a;先来看看串口调试助手显示的结果&#xff0c;第一个值是温度值&#xff0c;第二个值是X轴的加速度&#xff0c;第三个值是Y轴的加速度&#xff0c;第四个值是Z轴的加速度&#xff0c;第五个值是X轴的角速度&#xff0c;第六个值是Y轴的角速度&#xff…

C++库函数——string类

1. 简介 ①什么是string类 源文档 译&#xff1a; 1. 字符串是表示字符序列的类 2. 标准的字符串类提供了对此类对象的支持&#xff0c;其接口类似于标准字符容器的接口&#xff0c;但添加了专门用于操作单字节字符字符串的设计特性。 3. string 类是使用 char( 即作为它…

【六一】90后的你们还记得年少时的梦吗?还记得你们当初追的奥特曼吗?现在就让我们一起回味我们逝去的曾经吧!

迎面走来的是我们从未正式出过剧集&#xff0c;并附有“反派”盛名的大哥&#xff01;佐~~菲~~&#xff01; 还记得第一个登场&#xff0c;第一个进入我们的视野的那位吗&#xff1f;没错&#xff0c;那就是奥特曼 我的最爱~~~~~赛文&#xff01; 一度以为&#xff0c;曾经的那…

Bellhop 绘制传播损失

文章目录 前言一、相干传播损失&#xff08;TL&#xff09;基本算例1、环境文件2、绘制相干传播损失 二、相干、半相干和非相干传播损失&#xff08;TL&#xff09; 前言 上文我们讲述了 Bellhop 的使用以及使用 bellhop 绘制了声速剖面、声线轨迹及本证声线&#xff0c;本文我…