参考C++PrimerPlus第九章涉及“内存模型”部分,是比较基础的知识点。书中对变量“是否可见”、“何时可见”的问题进行了细致解释,这里用小篇幅总结一下。
C++11有4种不同的方案来存储数据,区别是数据保存在内存中的时间。
类型 | 特点 | 持续性特点 |
---|---|---|
自动存储 | 函数内部定义的常规(局部)变量 | 在程序执行其所属代码块时被创建,代码块结束后内存被释放 |
静态存储 | 函数外定义/static修饰声明的变量 | 在程序执行的整个期间一直存在 |
动态存储 | new出来的,内存分配在heap的变量 | 不受程序或函数的生存时间控制 |
线程存储 | thread_local修饰声明的变量 | 与所属线程一样长 |
静态的情况是本文的重点。
作用域(scope):描述了名称在文件(翻译单元)的多大范围内可见。
链接性(linkage):描述了名称如何在不同单元间共享。链接性为外部的名称可以在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。
默认情况下,在函数中声明的参数和变量的持续性为自动,作用域为局部,没有链接性。
执行到代码块时,为局部变量分配内存,但其作用域的起点为其声明位置。
1、函数重名怎么办?
函数调用或者进入函数中{code…}代码块时,里边的变量重名,新出现的将“覆盖”旧的,直到新的过期被释放。其中为新的变量分配内存,从而使他们可见,旧的变量仍留在内存中,但不可见。
2,关键字auto
C中和以前的C++中,关键字auto的用法和C++11截然不同。
以前的主要用途是指出当前的变量为局部变量:
int func(int n){ auto float ford; //ford has automatic storage }
在C++11中,这种做法不再合法。
3、关键字register
register建议编译器使用CPU寄存器来存储自动变量,旨在提高访问速度。C++11中,register只是显式地指出变量是自动的。
以下是本篇文章的重点
链接性 | 特点 | 创建地点 |
---|---|---|
外部 | 可在其他文件中访问 | 代码块外声明 |
内部 | 只能在当前文件中访问 | 代码块外声明并使用static限定符 |
无 | 只能在当前函数或代码块中访问 | 代码块内声明并使用static限定符 |
... int global = 100; //外部,作用域文件,其他文件能用 static int one_file = 50; //内部,作用域文件,这个文件中代码都能用,其他文件不能用 void func1(int n){ static int count = 0; //无链接性,作用域代码块,只能在fun1中使用 int llama = 0; ... } int main(){ ... }
静态变量都要初始化。
1、初始化方式:零初始化、常量表达式初始化、动态初始化。其中前2个统称静态初始化。未被初始化的静态变量的所有位都被设置成0(对标量类型,0将被强制转换为合适的类型,如指针),这种变量被称为零初始化的(zero_initialized).
2、初始化过程:
1)所有静态变量都被零初始化,不管程序员是否显式地初始化它;
2)如果使用常量表达式进行初始化,且编译器仅根据文件内容就能计算出,就进行常量表达式初始化;
3)没有足够信息的常量将被动态初始化。
//例子 # include <cmath> int x; // 零初始化 int y = 10; // 常量表达式初始化 int z = 13 * 13; // 常量表达式初始化 const double pi = 4.0 * atan(1.0); // 动态初始化,必须等到函数被链接且程序执行时
链接性为外部的变量通常成为外部变量,也称全局变量,存储持续性为静态,作用域整个文件。
1、单定义规则和extern
单定义规则(One Definition Rule):指出变量只能有一次定义。
为满足改规则,C++提供两种变量声明方式:
定义声明(defining declaration)或简称为定义(definition):它给变量分配存储空间;
引用声明(referencing declaration)或简称为声明(declaration):它不给变量分配存储空间,因为它引用已有变量。
引用声明使用关键字extern且不初始化,否则为定义。
double up; //定义,初始化为0 extern int blem; //声明 extern char gr = 'z'; //定义,因为初始化了
2、与局部变量重名时
使用作用域解析运算符::,放在变量名前边时,表示使用变量的全局版本。
# include <iostream> extern double warming; //声明,使用别的文件定义的warming = 0.4 void func1(){ extern double warming; //重新声明了warming,表示通过这个名称使用外部定义的变量,此声明省略的效果是一样的 warming += 1; } void func2(){ double warming = 0.8; cout << "局部warming:" << warming << endl ; //0.8 cout << "全局warming:" << ::warming << endl; //0.4 }
链接性为内部的变量只能在其所属文件中使用,但是常规外部变量都具有外部链接性,如果要在其他文件中使用相同的名称来表示其他变量,应该怎么处理?
1,只省略extern 是不够的
//file1 int error = 10; //外部,静态 ... //file2 int error = 20; //外部,静态 ...
错误,违反了单定义规则,这个程序包含了2个error的定义。
2,使用static将其定义为内部链接性
//file1 int error = 10; //外部,静态 ... //file2 static int error = 20; //内部,静态,只对file2有效 ...
3,const的类似效果
在C++中(不是C语言),const限定符对默认存储类型稍有影响。默认情况下,全局变量的链接性是外部,但是加上const修饰之后,将会变成内部链接性。
const int fingers = 10; //类似static的效果 int main (){ cout << fingers; }
如果处于某些原因,程序员想要某个常量有外部链接性,可以加上extern关键字来覆盖内部链接性:
extern const int fingers = 10; //变成外部链接性,其他文件可见
补充:内部链接性还意味着,每个文件都有自己的一组常量,而不是所有文件共享一组。每个定义都是其所属文件私有的,这就是能够将常量定义放在头文件中的原因。
1、存储性:
C++不允许在函数中定义函数,所以所有函数的存储持续性都是静态的,即在程序运行期间都一直存在。
2、链接性:
默认情况下,函数的链接性为外部,即可以在其他文件共享,所以可以用extern指出是函数是在其他文件中定义的。还可以用static将函数的链接性设置成内部,使之只能在本文件中使用,其他文件可以定义相同名称函数,此时内部函数会覆盖外部函数。
必须在函数声明和定义中使用static。
3、内联函数不同:
允许程序员将内联函数的定义放在头文件,包含了头文件的每个文件内都有内联函数的定义。然而,C++要求同一函数的所有内联定义都必须相同。