c++面经知识汇总(编译与底层、结构体内存对齐计算问题)

c++中什么是右值引用,什么是左值引用,有什么区别

c++引用、移动、转发

1.右值与左值:在c++中所有的值必然都属于右值、左值之一,所谓的左值就是在表达式结束以后依然持久存在的值或对象,而右值则是指表达式结束时就不再存在的临时变量,所有具有变量或者对象都是左值,右值并不具名一个可以区分左值和右值的便捷方法:看看能不能对表达式取地址,如果可以,为左值,如果不行,为右值
2. 左值引用、右值引用 :所谓的左值引用就是给某个变量取了一个别名,右值引用关联到右值时,右值被存储到特定位置,右值引用指向该特定位置,也就是说,右值虽然无法获取地址,但是右值引用是可以获取地址的,该地址表示临时对象的存储位置。由于右值引用有一个重要性质,只能绑定到右值(将要销毁的对象上),因此我们可以自由的将一个右值引用的资源移动到另一个对象中

c++源文件从文本到可执行文件经历的过程

对于c++源文件来说,从文本到可执行文件一般需要四个过成

  1. 预处理过程,这个过程将源代码文件中的文件包含关系、预编译语句(宏定义)进行分析和替换,生成预编译文件
  2. 编译阶段:将经过预处理后的预编译文件转换为特定的汇编代码,生成汇编文件
  3. 汇编阶段:将编译阶段生成的汇编文件转化为及其吗,生成可以重定位目标文件
  4. 连接阶段:将多个目标文件所需要的库链接成最终的可执行目标文件
include头文件的顺序以及双引号和尖括号的区别
  1. 顺序 如果在a中声明了一个在b中定义的变量,在不引用b.h的前提下,想要使用这个变量,需要在a.c中先引用b.h,再引用a.h否则会出现变量类型未声明的错误
  2. 尖括号和双引号的区别:主要是在预处理阶段查找头文件的路径不一样
    两者的查找路径都包括了查找编辑器设置的头文件路径、查找系统变量所指定的头文件路径,不同的是对于双引号来书,会优先查找当前头文件目录
malloc的原理,另外brk和mmap系统调用的作用分别是什么

Malloc是应用与动态分配内存的场景的,为了减少内存碎片的产生和系统调用的开销,,malloc采用的是内存池的方式,

  1. 首先申请一块大的内存区域作为堆区,采用隐式链表的结构将堆区分成连续的、大小不一的内存块,以块作为基本单位进行管理,这些块被标记为已分配块和未分配块;
  2. 当用户申请内存的时候,会直接从堆区未分配块中寻找一块合适的空闲的内存块
  3. 当进行内存分配的时候,malloc会通过隐式链表遍历所有的未分配块,选择满足要求的最小内存块进行分配
  4. 当进行内存的合并时,malloc采用的时边界标记法,根据每个块的前后块是否已经分配决定是否进行合并

至于brk系统和mmap系统的调用,有如下的两种情况,
5. 当用户申请的内存空间小于128b时,采用brk函数进行分配,如果大于128b使用mmap分配;

如何判断内存是否泄漏
  1. 通过工具
  2. 通过添加一个统计内存申请和释放次数的功能,如果一致,则没有泄漏
什么时候会发生段错误
  1. 解引用空指针
  2. 访问已经被释放的地址
  3. 栈移出
  4. 使用未吃实话的指针
什么时内存泄漏(memory leak)

内存泄漏可以简单的认为,动态申请的空间与释放的空间不匹配,一般来讲,动态申请的空间通过new/malloc ,释放通过delete/free ,如果在申请了空间之后,没有及时的delte/free,那么就会造成内存泄漏(申请的那块空间不会再被使用)
另一种情况就是:没有将基类的析构函数声明为虚函数,当基类指针指向派生类的对象时,如果基类并没有声明其析构函数是虚函数,那么编译器并不知道具体要释放谁的资源,可以简单的认为 ,在析构的时候,只是释放了派生类里面基类的资源,而没有全部释放,因此造成资源释放的不完整,导致内存泄漏

说一说结构体内存对齐了解多少

原则1. 数据成员对齐规则:结构(或联合)的数据成员,第一个数据成员放在offet为0的地方,以后每个数据成员的对齐按照#pragma pack指定的数值和这个数据成员自身生长长度中比较小的那个进行

原则2. 结构(或联合)的整体对齐规则:在数据成员完成各自对齐之后,结构(或联合)自身也要进行对齐,对齐将按照#pragma pack指定的数值和结构(或联合)的最大数据成员长度中比较小的那个进行

原则3.结构体作为成员:如果一个结构里又某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始

linux 默认的对齐值是 #pragma pack(4)
window 默认#pragma pack(8)

可以通过预编译命令#pragma pack(n),n = 1,2,4,8,16 来改变这一系数,其中n就是指定的“对齐系数”

#pragma pack(1)
struct AA {
 int a; 
 char b; 
 short c; 
 char d; 
};
#pragma pack()

按照原则1,int长度为4,设置的对齐系数为1,4>1,按照1对齐,偏移量为0,因此存放区间为【0,3】
长度1 =1 按照1对齐,偏移量为4,存放空间为【4】
长度为2>1 ,按照1 对齐,偏移量为5,存放空间为【5,6】
长度为1 = 1,按照1对齐,偏移量为6,存放空间为【7】
数据成员字对齐结束,按照原则2,进行结构对齐,按照对齐参数与自身最大成员中最小的对齐 ,也就是1 ,因此需要8个字节存放在【0,7】min((max(int,short,char), 1) = 1

#pragma pack(2)
struct AA {
 int a; //长度4 > 2 按2对齐;偏移量为0;存放位置区间[0,3]
 char b; //长度1 < 2 按1对齐;偏移量为4;存放位置区间[4]
 short c; //长度2 = 2 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
 char d; //长度1 < 2 按1对齐;偏移量为7;存放位置区间[8];共九个字节
};
#pragma pack()

整体对齐系数 = min((max(int,short,char), 2) = 2,将9提升到2的倍数,则为10.所以最终结果为10个字节。

#pragma pack(4)
struct AA {
 int a; //长度4 = 4 按4对齐;偏移量为0;存放位置区间[0,3]
 char b; //长度1 < 4 按1对齐;偏移量为4;存放位置区间[4]
 short c; //长度2 < 4 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
 char d; //长度1 < 4 按1对齐;偏移量为7;存放位置区间[8];总大小为9
};
#pragma pack()

整体对齐系数 = min((max(int,short,char), 4) = 4,将9提升到4的倍数,则为12.所以最终结果为12个字节。

#pragma pack(8)
struct AA {
 int a; //长度4 < 8 按4对齐;偏移量为0;存放位置区间[0,3]
 char b; //长度1 < 8 按1对齐;偏移量为4;存放位置区间[4]
 short c; //长度2 < 8 按2对齐;偏移量要提升到2的倍数6;存放位置区间[6,7]
 char d; //长度1 < 8 按1对齐;偏移量为7;存放位置区间[8],总大小为9
};
#pragma pack()

整体对齐系数 = min((max(int,short,char), 8) = 4,将9提升到4的倍数,则为12.所以最终结果为12个字节

struct B {
 char e[2]; //长度1 < 8 按2对齐;偏移量为0;存放位置区间[0,1]
 short h; //长度2 < 8 按2对齐;偏移量为2;存放位置区间[2,3]
 //结构体内部最大元素为double,偏移量为4,提升到8,所以从8开始存放接下来的struct A
 struct A {
 int a; //长度4 < 8 按4对齐;偏移量为8;存放位置区间[8,11]
 double b; //长度8 = 8 按8对齐;偏移量为12,提升到16;存放位置区间16,23]
 float c; //长度4 < 8,按4对齐;偏移量为24,存放位置区间[24,27]
 };

};

整体对齐系数 = min((max(int,double,float), 8) = 8,将内存大小由28补齐到8的整数倍32

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值