C++八股
静态变量和全局变量、局部变量的区别、在内存上是怎么分布的 首先需要说明的是变量属性的三个维度: 作用域:可以分为块作用域和命名空间作用域。块作用域就是{}中的内容;命名空间作用域则是在整个命名空间范围内有效,全局变量在全局命名空间内可见; 生命周期:变量可以分为自动变量、静态变量、动态变量。自动变量在进入块作用域时创建,离开时销毁;静态变量在程序启动或首次执行到声明语句时初始化,随程序结束而销毁;动态变量由new/delete或malloc/free管理,存在于堆上; 链接性(linkage):分为外部链接、内部链接和无链接。外部链接的变量可以在其他翻译单元中通过extern声明来访问;内部链接只能在当前翻译单元(编译单元/.cpp文件)内使用;无链接则是像局部变量这样,仅在块作用域内可见; 全局变量 在全局命名空间内可见,默认具有外部链接; 初始化发生在main()之前,分两阶段:先进行零初始化,再进行动态初始化(如有构造函数或非常量初始化表达式); 加上static后唯一改变的是链接性,从外部链接变为内部链接,仅当前翻译单元可见; 局部变量 仅在块作用域内可见,无链接; 作为自动变量,随所在块的进入而在栈上创建,块结束时销毁; 加上static成为静态局部变量:作用域和链接性不变,但生命周期改变——首次执行到声明语句时初始化(C++11起保证线程安全),之后不再随块结束而销毁,直到程序结束才销毁; 内存布局 内存大致分为代码段(.text)、数据段、堆、栈: .data段:存放已初始化的全局变量和静态变量; .bss段:存放未初始化(或初始化为零)的全局变量和静态变量,在可执行文件中不占实际空间,程序加载时由操作系统清零; 栈:局部变量(自动变量)随函数调用在栈上分配,块作用域结束时销毁; 堆:动态分配的内存(new/malloc); 指针和引用的区别 简单来说,引用是一个已有变量的别名;指针是一个独立的对象,存放了一个地址。 初始化:引用声明时必须绑定到一个已存在的对象,不能为空;指针可以声明为空指针(nullptr),也可以先声明后赋值; 操作语义:引用是别名,所有对引用的操作都穿透到其绑定的对象上;指针本身是一个独立对象,可以进行++、--等指针算术运算,改变的是指针自身指向的地址; 换绑:引用一旦绑定就不能更换目标,对引用赋值实际上是对绑定对象赋值;指针可以随时改为指向另一个地址; sizeof:sizeof(引用)得到的是绑定对象的大小;sizeof(指针)得到的是指针本身的大小(32位系统4字节,64位系统8字节); 多级间接:没有"引用的引用"(int& &不合法),但可以有多级指针(int**); 底层实现:编译器通常将引用实现为一个不可变的指针(即 T* const),但这是实现细节,语言标准不要求引用占据存储空间; C++内存分区 C++标准只规定了四种存储期(storage duration):自动、静态、动态、线程局部; 而实践中操作系统会将一个C++程序的虚拟地址空间大致分为以下几个区域: 代码段(.text):存放编译后的机器指令,通常为只读; 数据段: .rodata:存放只读常量(如字符串字面量、const全局常量等); .data:存放已初始化(非零)的全局变量和静态变量; .bss:存放零初始化和未初始化的全局变量和静态变量,可执行文件中不占实际空间,加载时由OS清零; 堆(heap):存放由new/delete、malloc/free管理的动态分配内存,向高地址增长; 栈(stack):存放函数调用帧(返回地址、参数)和局部变量(自动变量),向低地址增长; static关键字和const关键字的作用 static static主要用于改变变量、函数的存储期和链接性: 修饰局部变量:存储期从自动变为静态,首次执行到声明时初始化,直到程序结束才销毁;作用域不变,仍为块作用域; 修饰全局变量:将链接性从外部链接改为内部链接,仅当前翻译单元可见; 修饰普通函数:效果与修饰全局变量类似,将函数的链接性改为内部链接,避免跨翻译单元的符号冲突; 修饰类的成员变量:该变量不属于任何实例而属于整个类,所有实例共享;必须在类外定义(C++17起可用inline在类内初始化); 修饰类的成员函数:没有this指针,因此不能访问非静态成员变量和非静态成员函数,只能访问静态成员; const const主要用于表达"不可修改"的语义: 修饰普通变量:变量不可修改;修饰全局变量时,在C++中默认链接性变为内部链接(与C不同); 修饰指针: const int* p(pointer to const):指向常量,不能通过该指针修改所指对象; int* const p(const pointer):指针本身为常量,不能改变指向; const int* const p:两者皆不可修改; 修饰函数参数:表明函数承诺不修改该参数,常用于引用参数(const T&)避免拷贝的同时保证不修改; 修饰函数返回值:返回值不可被修改,常用于返回引用或指针时防止调用方意外修改; 修饰类的成员函数(放在参数列表后面):承诺该函数不会修改对象的任何非mutable成员,this的类型变为const T*;const对象只能调用const成员函数; 常量指针和指针常量之间有什么区别 const pointer(指针常量):int* const p——指针本身是常量,指向的地址不可改变,但可以通过该指针修改所指对象的值; pointer to const(常量指针):const int* p(等价于int const* p)——指针指向的对象被视为常量,不能通过该指针修改所指对象,但指针本身可以改为指向其他地址; const pointer to const:const int* const p——指针本身不可变,也不能通过该指针修改所指对象; 记忆技巧:const修饰的是它左边的东西(如果左边没有则修饰右边)。const int*中const修饰int,所以指向的值不可变;int* const中const修饰*(即指针本身),所以指针不可变。 ...