目录
四、include头文件的顺序及双引号""和尖括号<>的区别
[十九、const int *a, int const *a, const int a, int *const a, const int *const a分别是什么](#十九、const int *a, int const *a, const int a, int *const a, const int *const a分别是什么)
一、C++的特点
1、C++在C语言基础上引入了面向对象 的机制,同时也兼容C语言。
2、C++的三大特性:封装、继承、多态。
3、C++语言编写出的程序结构清晰 、易于扩充 ,程序可读性好。
4、C++生成的代码质量高 ,运行效率高。
5、C++增加了const常量、引用、四类cast转换(static_cast、dynamic_cast、const_cast、reinterpret_cast)、智能指针、try-catch等,更加安全。
6、C++可复用性高 ,引入了模板概念,标准模板库STL(Standard Template Library)。
二、C++和C的区别
1、C++语言可以很好的兼容C语言,C语言相当于是C++的子集。C++有许多新特性,如引用、智能指针、auto变量等。
2、C++是面向对象的语言,C语言是面向过程的语言。
3、C语言存在如指针使用的潜在危险、强制转换的不确定性、内存泄漏等不安全特性,C++对此增加了const常量、引用、cast转换、try-catch等新特性来改善安全性。
4、C++引用模板 的概念,可复用性高 ,C++的STL库相对于C语言的函数库更加灵活和通用。
三、C++中struct和class的区别
1、struct一般用于描述一个数据结构集合 ,class是对一个对象数据的封装。
2、struct中默认的访问控制权限 是public,class中默认的访问控制权限是private。
3、在继承关系 中,struct默认是公有继承,class是私有继承。
4、class关键字可以用于定义模板参数,就像typename,而struct不能用于定义模板参数。
四、include头文件的顺序及双引号""和尖括号<>的区别
1、尖括号的头文件是系统文件 ,双引号的头文件是自定义文件。
2、编译器预处理阶段查找头文件的路径不一样。
①使用尖括号的头文件查找路径:编译器设置的头文件路径-->系统变量;
②使用双引号的头文件查找路径:当前头文件目录-->编译器设置的头文件路径-->相同变量。
五、C++结构体和C结构体的区别
1、C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许该函数是虚函数。
2、C的结构体对内部成员变量的访问权限只能是public,C++允许public、protected、private三种。
3、C的结构体是不可以继承的,C++的结构体可以继承。
4、C使用结构体需要加上struct关键字,C++中可以省略struct关键字。
区别如下表:
|------|----------------|--------------------------|
| | C | C++ |
| 成员函数 | 不能有 | 可以有 |
| 静态成员 | 不能有 | 可以有 |
| 访问控制 | 默认public,不可以修改 | public、protected、private |
| 继承关系 | 不可以继承 | 可从类或其它结构体继承 |
| 初始化 | 不可以直接初始化数据成员 | 可以直接初始化数据成员 |
[struct的使用区别]
六、导入C函数的关键字,C++编译时与C有何不同
1、关键字
C++中导入C函数的关键字是extern ,表达形式是extern "C" 。该关键字的主要作用是为了能够正确实现C++代码调用其它C语言代码。加上**extern "C"**后,编译器在编译时会按C语言的进行编译,而不是C++的。
2、编译区别
由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型 也加到编译后的代码中,而不仅仅是函数名。C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
七、C++代码到可执行文件的过程
C++和C语言的编译执行过程类似,共经历四个过程:预编译 ,编译 ,汇编 ,链接。
①预编译。主要的操作为:
将所有的#define删除,展开所有的宏定义;
处理所有条件预编译指令,如#if、#ifdef;
处理#include预编译指令,将被包含的文件插入到该预编译指令的位置;
过滤所有的注释;
添加行号和文件名标识。
②编译。主要的操作为:
词法分析:将源代码的字符序列分割成一系列的记号;
语法分析:对记号进行语法分析,产生语法树;
语义分析:判断表达式是否有意义;
代码优化;
目标代码生成:生成汇编代码;
目标代码优化。
③汇编。主要是将汇编代码转变为机器可以执行的指令。
④链接。主要是将不同的源文件产生的目标文件进行链接,形成一个可以执行的程序。
链接分为静态链接和动态链接。
静态链接是在链接的时候将要调用的函数或者过程链接到生成的可执行文件中,即使删除静态库也不会影响可执行程序的运行。生成的静态链接库,Windows系统中以.lib为后缀,Linux系统中以.a为后缀。
动态链接是在链接的时候没有把调用的函数代码链接进去,而是在执行过程中再去找需要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息。删除动态库后,可执行程序不能运行。生成的动态链接库,Windows系统中以.dll为后缀,Linux系统中以.so为后缀。
八、关键字static的作用
1、定义全局静态变量和局部静态变量。初始化的静态变量会在数据段分配内存,未初始化的静态变量会在BSS段分配内存。直到程序结束,静态变量始终会维持前值。
2、定义静态函数。静态函数只能在本源文件中使用,如 static void func()。
3、定义静态变量。静态变量只能在本源文件中使用。
4、定义类中的静态成员变量 。使用静态数据成员,它既可以被当成全局变量那样去存储,但又被隐藏在类的内部。类中的static静态数据成员拥有一块单独的存储区,而不管创建了多少个该类的对象。所有这些对象的静态数据成员都共享这一块静态存储空间。
5、定义类中的静态成员函数 。如静态成员函数也是类的一部分,而不是对象的一部分。所有这些对象的静态数据成员都共享这一块静态存储空间。
当调用一个对象的非静态成员函数时,系统会把该对象的起始地址赋给成员函数的this指针,而静态成员函数不属于任何一个对象,因此C++规定静态成员函数没有this指针。既然它没有指向某一对象,也就无法对一个对象中的非静态成员进行访问。
九、数组和指针的区别
1、概念
数组:用于储存多个相同类型数据的集合。数组名是首元素的地址。
指针:相当于一个变量,存放的是其它变量在内存中的地址。指针名指向内存的首地址。
2、区别
①赋值。同类型指针变量可以相互赋值,数组不可以,只能一个一个的赋值或拷贝。
②存储方式 。数组在内存中是连续存放的,数组的存储空间不是在静态区就是在栈上。指针很灵活,可以指向任意类型的数据,指针的类型说明了它指向地址空间的内存,由于指针本身是一个变量,再加上它存放的也是变量,所以指针的存储空间是不确定的。
③sizeof。数组所占存储空间的内存大小=sizeof(数组名)/sizeof(数据类型)。在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,sizeof(指针名)都是8。
十、函数指针
1、概念 。函数指针就是指向函数的指针变量。每一个函数都有一个入口地址,该地址就是函数指针所指向的地址。
2、定义。如下:
cpp
int func(int a);
int (*f)(int a);
f=&func;
3、应用场景 。回调(callback),调用其它API函数(Application Programming Interface,应用程序编程接口),称为call。如果别人的库里面调用我们的函数,称为callback。
十一、静态变量什么时候初始化
对于C语言的全局和静态变量,初始化发生在任何代码执行之前,属于编译期初始化。C++标准规定:全局或静态对象当且仅当对象首次用到时才进行构造。
十二、nullptr是否可以调用成员函数
可以。因为在编译时对象就绑定了函数地址,和指针是否为空无关。
十三、野指针
1、概念。野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
2、产生原因。释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。
3、避免方法。初始化为NULL;申请内存后判空;指针释放后置NULL;使用智能指针。
十四、静态变量、全局变量、局部变量的特点
1、作用域。C++里作用域可分为6种:全局,局部,类,语句,命名空间和文件作用域。
全局变量:全局作用域,可以通过extern作用于其他非定义的源文件。
静态全局变量:全局作用域+文件作用域,所以无法在其他文件中使用。
局部变量:局部作用域,比如函数的参数,函数内的局部变量等等。
静态局部变量:局部作用域,只被初始化一次,直到程序结束。
2、所在空间。除了局部变量在栈上外,其他都在静态存储区。因为静态变量都在静态存储区, 所以下次调用函数的时候还是能取到原来的值。
3、生命周期。局部变量在栈上,出了作用域就回收内存。全局变量、静态全局变量、静态局部变量都在静态存储区,直到程序结束才会回收内存。
十五、内联函数和宏函数的区别
1、宏定义不是函数,但使用时像函数 。预处理器用复制宏代码的方式代替函数的调用,省去了函数压栈退栈过程,提高了效率。内联函数本质上是一个函数,内联函数一般用于函数体的代码比较简单的函数,不能包含复杂的控制语句,while、switch,并且内联函数本身不能直接调用自 身。
2、宏函数是在预编译的时候把所有的宏名用宏体来替换,简单的说就是字符串替换。内联函数则是在编译的时候进行代码插入,编译器会在每处调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数的调用的开销,提高效率。
3、宏定义是没有类型检查的。无论对还是错都是直接替换。内联函数在编译的时候会进行类型的检查,内联函数满足函数的性质,比如有返回值、参数列表等。
内联函数使用条件:
1、内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:
①如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
②如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
2、内联不是什么时候都能展开的,一个好的编译器将会根据函数的定义体,自动地取消不符合要求的内联。
十六、new和malloc
1、new是操作符,malloc是函数。
2、new在调用时是先分配内存,再调用构造函数,释放的时候调用析构函数。malloc没有构造函数和析构函数。
3、new会调用析构函数,不用指定内存大小,返回指针不用强转。malloc需要给定申请内存的大小,返回的指针需要强转。
4、new可以被重载,malloc不可以。
5、new分配内存更直接和安全。
6、new发生错误抛出异常,malloc返回null。
malloc底层实现原理:当开辟的空间小于128K时,调用 brk()函数;当开辟的空间大于128K时,调用mmap()。malloc采用的是内存池的管理方式,以减少内存碎片。先申请大块内存作为堆区,然后将堆区分为多个内存块。当用户申请内存时,直接从堆区分配一块合适的空闲块。采用隐式链表将所有空闲块,每一个空闲块记录了一个未分配的、连续的内存地址。
new底层实现原理:关键字new在调用构造函数的时候实际上进行了如下的几个步骤:
①创建一个新的对象;
②将构造函数的作用域赋值给这个新的对象(this指向了这个新的对象);
③执行构造函数中的代码(为这个新对象添加数属性);
④返回新对象。
十七、const和define
const常用于定义常量,define常用于定义宏,而宏也可以用于定义常量。如果都用于定义常量,区别为:
1、const生效于编译的阶段,define生效于预处理阶段。
2、const定义的常量,在C语言中是存储在内存中、需要额外的内存空间 的;define定义的常量,运行时是直接的操作数,并不会存放在内存中。
3、const定义的常量是带类型的 ,define定义的常量不带类型。因此define定义的常量不利于类型检查。
十八、C++中的函数指针和指针函数的区别
1、定义
指针函数本质是一个函数,其返回值为指针;
函数指针本质是一个指针,其指向一个函数。
2、写法
cpp
指针函数:int *func(int x, int y);
函数指针:int (*fun)(int x, int y);
十九、const int *a, int const *a, const int a, int *const a, const int *const a分别是什么
cpp
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、定义指针时,先初始化为NULL。
2、用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
3、不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
4、避免数字或指针的下标越界,特别要当心发生"多1"或者"少1"操作。
5、动态内存的申请与释放必须配对,防止内存泄漏。
6、用free或delete释放了内存之后,立即将指针设置为NULL,防止"野指针"。
二十一、内联函数和函数的区别
1、内联函数比普通函数多了关键字inline。
2、内联函数避免了函数调用的开销,普通函数有调用的开销。
3、普通函数在被调用的时候,需要寻址(函数入口地址),内联函数不需要寻址。
4、内联函数有一定的限制,内联函数体要求代码简单,不能包含复杂的结构控制语句;普通函数没有这个要求。
内联函数的作用:内联函数在调用时,是将调用表达式用内联函数体来替换。避免函数调用的开销。
二十二、C++的传值方式
三种方式:值传递 、引用传递、指针传递。
1、值传递。形参即使在函数体内值发生变化,也不会影响实参的值。
2、引用传递。形参在函数体内值发生变化,会影响实参的值。
3、指针传递。在指针指向没有发生改变的前提下,形参在函数体内值发生变化,会影响实参的值。
值传递用于对象时,整个对象会拷贝一个副本,这样效率低;而引用传递用于对象时,不发生拷贝行为,只是绑定对象,更高效;指针传递同理,但不如引用传递安全。