C++面试
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一、编译内存相关
1、C++ 程序的编译过程
C++的编译过程有四个:编译预处理,编译,汇编,链接。
编译预处理:处理以#开头的指令;
编译:将.cpp的源码翻译成.s的汇编代码文件;
汇编:将汇编代码.s翻译成机器指令.o文件;
链接:汇编程序生成目标文件(.o),这个文件不会立即执行,因为可能出现多文件的一些问题,如.cpp文件中的某个函数引用了另外一个.cpp文件中定义的符号或者调用了某个库函数。链接的目的就是将这些文件关联成一个整体,从而生成.exe文件。
2、C++ 内存管理
C++的内存区域分:栈区、堆区、全局/静态存储区、代码区
栈区:存放函数的局部变量、函数参数、返回地址等,由编译器自动分配和释放。
堆区:程序申请的内存空间,由new、malloc分配的内存块,如果程序结束没有被释放,则操作系统自动回收。
全局区/静态存储区(.bss段和.data段):存放全局变量和静态变量,程序运行结束操作系统自动释放。在C语言中初始化的存放在.data,未初始化的存放在.bss,但是在C++中不再区分,因为在C++中,全局变量和静态变量编译器会给这些变量初始化赋值,使用没区分初始化变量和未初始化变量。
常量存储区(.data段):存放常量,不允许修改,程序结束时系统自动释放。
代码区(.text段):存放函数体的二进制代码,不允许修改,但可以执行。
程序运行时印象:
代码示例:
#include <iostream> using namespace std; int m_data = 0; //m_data在全局区(.data段) char * c_data; //c_data 在全局区(.bss段) int main(){ int m_stack; //m_stack 在栈区 char * c_stack //c_stack 在栈区 char arr[] = "abc"; //arr为数组变量,存放在栈区;"abc"为字符串常量,存放在常量区 char * var = "def"; //var在栈区;“def”在常量区 static int s_data = 10; // s_data是静态变量,在静态存储区(.data段) c_stack = new char[5];//分配的空间在堆区 delete[] c_stack; return 0; }
3、堆区和栈区的区别
1)申请方式:栈是系统自动分配的,堆是程序员申请的。
2)空间连续:栈在内存中是连续的一块空间(向低地址扩展),最大容量是系统预定好的(一般为1-2M),堆在内存中的空间是不连续的(向高地址扩展)。ps:栈为什么要由高地址向低地址扩展?
3)系统响应:
栈申请空间,如果剩余的空间大于需分配的空间则分配成功,否则分配失败(栈溢出);
堆申请空间,系统收到申请空间的请求后,会遍历一个操作系统用于记录空闲地址的链表,当找到一个空间大于所申请空间的堆结点后,就会为该结点从记录内存空间地址的链表中删除,并将该结点的内存分配给程序,然后在这块内存区域的首地址处记录分配的大小,这样在使用delete释放内存的时候,delete才能正确地识别并删除该内存区域的所有变量。另外,所申请的内存空间与堆结点的内存大小可能不相等,这时操作系统会自动将堆结点上多出来的那部分内存空间回收到空闲链表中。
4)碎片问题:栈是后进先出的队列,内存是连续的;而堆则在多次的new和delete后会产生很多碎片。
5)分配效率:栈是系统的底层数据结构,有专门的寄存器放栈的地址,专门指令执行压栈出栈,这就决定了栈的效率比较高;而堆是C++函数库提供的,机制复杂,效率低。
6)分配方式:堆是动态分配,没有静态分配。栈是有静态分配和动态分配的,静态分配由编译器完成,例如局部变量的内存分配;动态分配则由alloca函数分配,不同于堆的手工释放,它的分配完全由编译器自动释放。
4、全局变量、局部变量、静态全局变、静态局部变量的区别
全局变量:全局作用域。只要在一个源文件定义,就可以作用于所有源文件。注意,在其它不包含全局变量定义的源文件需要用 extern 关键字进行声明。
静态全局变量:具有文件作用域。与普通全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件内,不能作用于其它文件,即使是extern也没用。如果两个文件都要同名的静态全局变量那他们就是两个不同的变量,就是说static限制了全局变量的作用域。
局部变量:局部作用域。它是在函数体内定义的变量,是一个自动对象(auto),仅在函数执行期间存在,函数结束后会被销毁,其内存空间也会被操作系统释放。
静态局部变量:局部作用域。它只被初始化一次,从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
从分配内存空间看:
静态存储区:全局变量,静态局部变量,静态全局变量
栈:局部变量
5、全局变量定义在头文件中有什么问题?
会出现重复定义问题,在头文件中只能使用extern声明已经在其它文件中定义的全局变量。
6、内存对齐
内存对齐:编译器将程序中的每个“数据单元”安排在字的整数倍的地址指向的内存之中。
内存对齐的原则:
1)结构体变量的首地址能够被其最宽基本类型成员大小与对齐基数中的较小者整除;
2)结构体每个成员相对于结构体首地址的偏移量(offset)都是该成员大小与对齐基数中的较小者的整数倍,如有需要,编译器会在成员之间填充自己(internal padding);
3)结构体的总大小为结构体最宽基本类型成员大小与对齐基数中的较小者的整数倍,如有需要编译器会在最后一个成员之后加上填充字节(trailing padding)
示例:
struct A{ short var; // 2 字节 int var1; // 8 字节 (内存对齐原则:填充 2 个字节) 2 (short) + 2 (填充) + 4 (int)= 8 long var2; // 12 字节 8 + 4 (long) = 12 char var3; // 16 字节 (内存对齐原则:填充 3 个字节)12 + 1 (char) + 3 (填充) = 16 string s; // 48 字节 16 + 32 (string) = 48 };
int main()
{
short var;
int var1;
long var2;
char var3;
string s;
A ex1;
cout << sizeof(var) << endl; // 2 short
cout << sizeof(var1) << endl; // 4 int
cout << sizeof(var2) << endl; // 4 long
cout << sizeof(var3) << endl; // 1 char
cout << sizeof(s) << endl; // 32 string
cout << sizeof(ex1) << endl; // 48 struct
return 0;
}
----------------------------------------------------------
原文链接:C++面试突袭