存储持续性、作用域和链接性
除了线程存储持续性,C++使用三种不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。
- 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储持续性是自动的。它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。C++有两种存储持续性为自动的变量。
- 静态存储持续性:在函数定义外定义的变量和使用关键字
static
定义的变量的存储持续性为静态。它们在程序整个运行过程中都存在。C++有三种存储持续性为静态的变量。 - 动态存储持续性:用
new
运算符分配的内存将一直存在,直到使用delete
运算符将其释放或程序结束为止。这种内存的持续性为动态,有时也被成为自由存储(free store)和堆(heap)。
自动存储持续性
在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性。l也就是常见的变量的存储持续性大多为自动,称其为自动变量。
自动变量
C++编译器利用栈来实现自动变量。
寄存器变量
关键字register
最初是由C语言引入的,它建议编译器使用CPU寄存器来存储自动变量,旨在提高访问变量的速度。在C++11中,这种提示作用失去了,关键字register
只是显式地指出变量是自动的。
静态持续变量
C++为静态持续变量提供了3中链接性:外部链接性(可在其它文件中访问)、内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问)。
这3种链接性都在整个程序执行期间存在。由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。编译器将分配固定的内存块存储所有的静态变量,这些变量在整个程序执行期间一直存在。另外,如果没有显式地初始化静态变量,编译将将把它设置为0。在默认情况下,静态数组将每个元素或成员的所有位都设置为0。这种变量被称为零初始化的(zero-initialized)。
存储方式 | 持续性 | 作用域 | 链接性 | 如何声明 |
---|---|---|---|---|
自动 | 自动 | 代码块 | 无 | 在代码块中 |
寄存器 | 自动 | 代码块 | 无 | 在代码块中,使用关键字register |
静态,无链接性 | 静态 | 代码块 | 无 | 在代码块中,使用关键字static |
静态,外部链接性 | 静态 | 文件 | 外部 | 不在任何函数内 |
静态,内部链接性 | 静态 | 文件 | 内部 | 不在任何函数中,只用关键字static |
静态存储持续性、外部链接性
链接性为外部的变量称为外部变量,或常规外部变量,它们的存储持续性为静态,作用域为整个文件。
单定义规则(One Definition Rule, ODR):变量只能有一次定义。为了满足这种需求,C++提供了两种变量声明。一种是定义声明(defining declaration)或简称为定义(definition),它给变量分配内存空间;另一种是引用声明(referencing declaration)或简称为声明(declaration),它不给变量分配内存空间,因为它引用已有的变量。引用声明使用关键字extern
,且不进行初始化;否则,声明变为定义,导致分配内存空间。
double up; // 定义,up是0
extern int blem; // blem在其它地方定义
extern char gr = 'z'; // 因为是初始化了所以是定义
在多个文件中使用外部变量,只需要在一个文件中包含该外部变量的定义(单定义规则),但在使用该变量的其他所有文件中,都必须使用extern声明它。
如果在函数中声明了一个与外部变量同名的变量,这种声明将被视为一个自动变量的定义,当程序执行自动变量所属的函数时,该变量将位于作用域内,还将隐藏同名的全局变量。当然可以使用C++的作用域解析符(::)来访问被隐藏的外部变量,放在变量名前时,该运算符表示使用变量的全局版本。
另外可以使用extern
在函数中重新声明外部变量,该声明是可选的。它指出该函数被设计成使用外部变量。
可使用外部变量在多文件程序的不同部分之间共享数据。
静态存储持续性、内部链接性
将static
限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。链接性为内部的变量只能在其所属的文件中使用。这种变量又被成为静态外部变量。
如果在一个文件中定义了一个静态外部变量,其名称与另一个文件中声明的常规外部变量相同,则在该文件中,静态变量将隐藏常规外部变量。即静态外部变量和常规外部变量同名是不潍坊单定义规则的,不冲突。另一种表述,如果作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。
可使用链接性为内部的静态变量——静态外部变量——在同一个文件中的多个函数之间共享数据。
静态存储持续性、无链接性
使用static
限定符用于在代码块中定义无链接性的局部变量。这意味着,虽然该变量只在该代码库中可用,但它在该代码块不处于活跃状态时仍然存在。因此在两次函数调用之间,静态局部变量的值将保持不变。另外,如果初始化了静态局部变量,则该程序只在启动时进行一次初始化。
再谈const
在默认情况下全局变量的链接性是外部的,但const
全局变量的链接性是内部的。也就是说,在C++看来,全局const
定义,就像是使用了static
说明符一样。
如果全局const
声明的链接性像常规变量那样是外部的,则根据单定义规则,这将出错。也就是说,只能又一个文件可以包含前面的声明,而其它文件必须使用extern
关键字来提供引用声明。且只有未使用extern
关键字的声明才能进行初始化。使用extern
的目的是用其覆盖默认的内部链接性。
在函数或代码块中声明const
时,其作用域为代码块,即仅当程序执行到该代码块中的代码时,该常量才是可用的。这意味着在函数或代码块中创建常量时,不必担心其名称与其他地方定义的常量发生冲突。
函数的链接性
所有函数的存储持续性都是静态的。
在默认情况下,函数的链接性是外部的,即可以在文件间共享。实际上,可以在函数原型中使用关键字extern
来指出函数是在另一个文件中定义的,不过这是可选的。
还可以使用关键字static
将函数的链接性设置为内部的,使之只能在一个文件中使用。必须同时在原型和函数的定义中使用该关键字。这意味着,可以在其他文件中定义同名的函数。和变量一样,在定义静态函数的文件中,静态函数将覆盖外部定义。因此,即使在外部定义了同名的函数,该文件仍将使用静态函数。注意,不可以像外部变量般使用作用域解析符::
访问同名函数的全局版本。
内联函数不受单定义规则约束,这允许程序员能够将内联函数的定义放在头文件中。这样,包含了头文件的每个文件都有内联函数的定义。