C++之内存模型 Memory Model
C++使用三种不同方案(C++11是四种)来存储数据:
- 自动存储持续性 在函数定义中声明的变量(包括函数参数)的持续性为自动的。他们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,他们使用的内存将被释放。
- 静态存储持续性 在函数定义外定义的变量和使用关键字static定义的变量存储的持续性都为静态,他们在程序整个运行过程中都存在。
- 线程储存持续性(C++11)多核处理器很常见,如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。
- 动态存储持续性 用new运算符分配的内存将一直存在,知道使用delete运算符将其释放或程序结束位置。这种内存的存储持续性为动态,有时被称为自由存储或堆
作用域描述了名称在文件的多大范围可见 链接性描述了名称如何在不同单元间共享。链接性为外部的名称可在文件间共享,链接性为内存的名称只能由一个文件中的函数共享。自动变量的名称没有链接性,因为它们不能共享。
自动存储持续性
在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。
自动变量和栈
由于自动变量的数目随函数的开始和结束而增减,因此程序常留出一段内存对自动变量进行管理,通常将其视为栈。
栈是后进先出的,这种设计简化了参数传递。函数调用将其参数的值放在栈顶,然后重新设置栈顶指针,被调用的函数根据其形参描述来确定每个参数的地址。
静态持续变量
c++为静态存储持续性变量提供了3种链接性:
- 外部链接性(可在其他文件中访问)、
- 内部链接性(只能在当前文件中访问)
- 无连接性(只能在当前函数或代码中访问)
这三种链接性都在整个程序执行期间存在。
编译器将分配固定的内存块来存储所有的静态变量。主要是.data段里
如果没有显示地初始化静态变量,编译器将把它设置为0.
存储描述 | 持续性 | 作用域 | 链接性 | 如何声明 |
---|---|---|---|---|
自动 | 自动 | 代码块 | 无 | 在代码块中 |
寄存器 | 自动 | 代码块 | 无 | 在代码块中,使用关键字register |
静态,无链接性 | 静态 | 代码块 | 无 | 在代码块中,使用关键字static |
静态,外部链接性 | 静态 | 文件 | 外部 | 不在任何函数内 |
静态,内部链接性 | 静态 | 文件 | 内部 | 不在任何函数内,使用关键字static |
静态持续性、外部链接性
外部变量的存储持续性为静态,作用域为整个文件。外部变量也称全局变量。
单定义规则
变量只能有一次定义。
c++提供两种变量声明。
- 一种是定义声明。简称定义,它给变量分配存储空间
- 一种是引用声明,简称声明,它不给变量分配存储空间,因为它引用已有的变量
引用声明使用关键字extern,且不进行初始化,否则声明为定义,导致分配存储空间
如果要在多个文件中使用外部变量,只需在一个文件中包含该变量的定义,但在使用该变量的其他所有文件中,都必须使用关键字extern声明它
单定义规则并非意味着不能有多个变量的名称相同
静态持续性、内部链接性
将static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部。链接性为内部的变量只能在其所属的文件中使用。
内部链接性使其不必担心该变量名称与其他文件中的作用域为整个文件的变量发生冲突。
静态存储持续性、无链接性
无链接性的局部变量,通过将static限定符用于在代码块中定义的变量。
如果初始化了静态局部变量,则程序只在启动时进行一次初始化。以后在调用,将不会像自动变量那样再次被初始化。
说明符和限定符
- auto
- register
- static
- extern
- thread_local(c++11新增)
- mutable
关键字register用于在声明中指示寄存器存储,而在c++11中,它只是显示地指出变量是自动的。
关键字thread_local之于线程,犹如常规静态变量之于整个程序。
cv-限定符
- const
- volatile
const 表明,内存被初始化后,程序便不能再对它进行修改。
关键字volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生变化。
mutable
mutable表明,即使结构变量为const,其某个成员也可以被修改
const全局变量
const全局变量的链接性为内部的,意味着每个文件都有自己的一组常量,而不是所有文件共享一组常量
如果处于某种原因,希望某个常量的链接性为外部的,则可以使用extern关键字覆盖默认的内部链接性:
extern const int STATE = 1;
这种情况下,必须在所有使用该常量的文件中使用extern关键字来声明它。
在函数或代码块中声明const时,其作用域为代码块。
动态分配
使用运算符new(或c函数malloc)分配的内存,这种内存被称为动态内存。
动态内存作用域由运算符new和delete控制。