C++面试问题(更新中......)
-
结构体内存对齐
- 意义是减少cpu读取的次数,提高效率。
-
指针与引用的区别?
- 定义和性质不同
- 指针是一个变量,存储的是一个地址,指向内存的一个存储单元
- 引用时变量的一个别名,与原来的变量实质上时一个东西
- 指针可以是多级的,引用只有一级
- 引用在定义的时候必须初始化,指针可以不初始化
- 指针初始化之后还可以在改变,引用不可以
- 自增运算意义不同
- 指针和引用作为函数参数时,指针需要检查是否为空,引用不需要
- 定义和性质不同
-
堆和栈的区别?
堆和栈实际上是操作系统对进程占用空间的两种管理方式。
- 管理方式不同
- 栈由操作系统自动分配释放,无需手动控制;
- 堆的申请与释放工作由程序员控制,容易产生内存泄漏
- 空间大小不同:每个进程拥有的栈大小远远小于堆大小
- 生长方式不同
- 堆的生长方向向上 内存地址由低到高
- 栈的生长方向向下 内存地址由高到低
- 分配方式不同
- 堆是动态分配,没有静态分配的堆。频繁的malloc/free造成内存空间的不连续,会产生碎片
- 栈有两种分配方式:静态|动态。静态分配是由操作系统完成的,比如局部变量的分配。动态分配有alloca()函数分配,但于堆的动态分配是不同的,栈的动态分配是由操作系统进行释放的。
- 效率
- 栈是机器系统提供的数据结构,计算机会在底层对栈提供支持(有专门的寄存器存放栈的地址,压栈出栈都有专门的机器指令执行),这决定了栈的效率比较高。
- 堆是C/C++库函数提供的,机制复杂。例如分配一块内存,堆会按照一定的算法,在堆内存中搜索可用的足够大小的空间,如果没有(可能是由于碎片太多),就可能调用系统功能去增加程序数据段的内存,然后进行返回。效率比栈慢很多。
- 存放内容不同。
- 栈存放的内容,函数返回地址,相关参数,局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址(扩展指针寄存器的内容EIP),然后是当前栈帧的底部地址,即扩展基地址指针寄存器内容(EBP),再然后时被调用函数的实参等,一般清空是按照从右往左的顺,注意静态变量是存放在数据段或者BSS段,不入栈。
- 堆一般情况堆顶使用一个字节大空间存放堆大小,堆中具体存放内容由程序员来填充。
- 管理方式不同
-
基类的虚函数表存放在内存的什么区,虚表指针vptr的初始化时间
- 虚函数表位于常量区,虚函数位于代码区。
- 虚表指针在对类进行实例化时,在构造函数执行时会对vptr进行初始化,并存放在对象内存布局的最前面。
-
new/delete与malloc/free的异同
异:
- new/delete是c++的运算符,编译时需要添加参数;malloc/free是c的库函数,编译需要头文件支持。
- new返回指针类型的指针,并可以自动计算所需大小,malloc需要计算字节数,病区再返回后进行强转位实际类型的指针。
- malloc只管分配内存,并不能对内存进行初始化,而new可以对所得到的内存进行初始化。
- new再分配内存失败时会抛出异常,malloc会返回NULL
- new会调用构造函数new和delete是如何实现的
- delete回调用析构函数
- new/delete都可以被重载。
-
new和delete是如何实现的
- new的原理:
- 调用operator new函数申请空间
- 在申请的空间上执行构造函数,完成对象的构造
- delete的原理:
- 在空间上执行析构函数,完成对象中资源的清理工作在空间上执行析构函数,完成对象中资源的清理工作
- new的原理:
-
宏定义与函数的区别 宏定义,编译预处理指令,在编译预处理时即进行简单的字符替换。
-
宏定义与typedef的区别
-
变量的声明与定义的区别
-
什么情况下必须用到初始化成员列表
- 需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化)
- 需要初始化const修饰的类成员或初始化引用成员数据
- 子类初始化父类的私有成员
-
strlen与sizeof的区别
- sizeof是关键字,strlen是函数
- sizeof求的是字节长度,strlen求的是实际长度
- sizeof求的是分配过来的长度,而strlen求的却是实际使用的长度
- sizeof是在编译时计算的,strlen是在运行时计算的
-
顶层const与底层const
- 顶层const指对象本身就是一个常量,底层const指对象(指针,引用等)指向的是一个常量。
- 由此观之,对象本身是不是常量和对象指向的是不是常量是两个互相独立的问题,因此引出了我们熟知的指针常量个常量指针。
-
常量指针与指针常量的区别
- 指针常量
- 落点在常量上,说明指针常量是一个常量,类型是指针类型。例 int *const p = #
- 从右往左看,离p最近的是const,首先说明p是一个常量。在这 const是一个顶层const因为它本身是一个常量,我们初始化的时候将p指向num,完成了初始化,所以常量p不能在指向其他的变量(或是常量),也就是不能再写p=&num2.
- 常量指针
- 落点是指针,说明常量指针是一个指针,不同的是他指向的是一个常量。
const int a = 5; const int *p = &a;
- 从右往左看首先是一个*(指针),首先说明p是一个指针,在向左看,是一个指向常量的指针,所以p指向的是一个常量,因此不能改变*p的值,但是p本身不是一个常量所以可以改变p指向的变量。
- 指针常量
-
野指针和悬空指针
- 野指针(wild pointer)就是没有被初始化过的指针。
- 悬空指针(dangling poniter) 指针最初指向的内存被释放了的一种指针。
- 危害:无论是野指针还是悬空指针,都是指向无效内存区域("不安全不可控")的指针,访问这些指针,任何可能都会发生。要么编译失败,要么执行的不正确或是悄无声息,也有可能会正确的出现想要的结果。
- 如何避免:
- 野指针:养成定义指针后在使用前初始化的习惯。
- 悬空指针:利用智能指针,间接避免。智能指针本质是使用引用计数来延迟对指针的释放
-
迭代器失效的情况
- 序列式容器
- 链表式容器
- 关联式容器
- 容器调用erase方法后,当前位置到容器末尾元素的所有迭代器全部失效。
- 容器调用insert方法后,当前位置到容器末尾元素的所有迭代器全部失效。
- 如果容器扩容,在其他地方重新又开辟了一块内存。原来容器底层的内存上所保存的迭代器全都失效了。
-
c和c++的区别
-
c++中class与struct区别
- class 默认成员函数与成员变量是私有的(private);
- struct 默认是公有的(public);
-
const和static的作用
- static
- static局部变量:将一个变量声明为函数的局部变量,那该变量在函数执行完成之后不会被释放,而是继续保存到内存中。
- static全局变量:在当前文件的全局内可访问。
- static函数:表示一个函数只能在当前文件被访问。
- static类成员变量:这个成员为整个类所共有。
- static类成员函数:函数为整个类所共有,只能访问静态成员变量。
- const
- const常量:定义是就初始化,之后不能更改.
- const形参:func(const int a){};该形参在函数内不能被修改。
- const修饰类成员函数:该类对成员函数只能进行读操作。
- static关键字作用
- 函数内static变量的作用域为该函数体,内存之别分配一次,因此其值在下此调用是让然维持上次的值。
- 在模块内的static全局变量和函数可以被模块内的函数访问,但不能被模块外其他函数访问。
- 在类中的static成员变量为整个类所拥有,对类的所有对象只有一份拷贝。
- 在类中的static成员函数为整个类所拥有,这个函数不接收this指针,因此只能访问类的static成员变量。
- const关键字作用
- 阻止一个变量被改变
- const修饰形参, 表示它是一个输入参数,函数内部不能改变;
- 对于类的成员函数,指定为const类型,表明为常函数,不能修改类的成员变量
-
a和&a的区别 a代表数组首元素的地址即a[0]的地址 &a代表数组的首地址 a+1 表示a[1]的地址 &a+1 表示数组尾地址的下一个地址。
-
类的对象存储空间
-
虚函数 1.应用在派生类中重新定义的成员函数。 2.虚函数确保为该对象调用正确的函数。 3.多态的动态实现方式
-
虚函数可以是内联函数吗
-
智能指针
-
同步异步
-
宏定义MAX(a,b) 这里要考虑两点:
1.运算符优先级:#define MAX(a,b) ((a) > (b) ? (a) :(b)) 但在这种情况下依然会出现问题:max(a,b++) 展开后就是(a) > (b++)> ? (a) : (b++); 明显b多加了一次; 这种写法也无法进行类型检查。 2.数据类型检查: #define MAX(a,b) ({\ typeof(a) _a = a; \ typeof(b) _b = b; \ (void)(&_a == &_b); \ _a > _b ? a : b;}) typeof 是gcc中取变量类型的操作符。
-
用户态和内核态区别: 内核态:运行操作系统程序,操作硬件
-
系统调用,中断,XX(todo)区别