为了方便开发者管理与修改,实现某个功能的C++程序可能由多个文件构成。常用的文件架构为:
1.头文件:存放原型,符号常量,内联函数实现;
2.函数源文件:存放有关函数的实现;
3.主程序源文件:利用函数和类实现某些功能;
在将以上多个文件编译成可执行文件时,需要将上述多个文件组合成一个可执行文件,那么需要有关规则来规定,文件中名称的存在时间、可见范围(文件内与文件之间),从而正确完成编译。

1.名词内涵
1.1存储持续性
定义:数据在内存中保留时间的长短,也即数据在程序中的生命周期,分为以下四种:
自动存储持续性:由系统自动管理。在执行名称所属的函数或者代码块时被创建,在执行完成后自动释放;
静态存储持续性:在程序运行期间都存在;
动态存储持续性:由用户自行管理分配和释放。(new 和 delete运算符的作用)。也被称为自由存储(free store)或堆(heap)
线程存储持续性:在多线程编程中会涉及,不同线程中的变量生命周期不同;
1.2作用域
定义:名称在一个文件内的可见范围,分为以下两种:
局部:只在定义名称的代码块中可见;
全局:在整个文件内都可见,也被成为文件作用域;
1.3链接性
定义:名称在不同文件之间的可见性,或共享性,分为以下两种:
内部:只能在文件内部的函数间共享;
外部:可以在不同文件之间共享;
无链接性:只能在代码块或函数中访问
2.各种名称/变量的特性
变量的存储持续性、作用域以及链接性通常是存在联系的
2.1自动变量
所谓自动变量,即在函数中声明的函数参数和无限定符的变量;
存储持续性:自动存储持续性;作用域:局部;链接性:无链接性;
1)在函数原型的括号中出现的变量名,只在函数参数列表内(也就是括号内)可见,所以变量名叫什么以及是否出现都不重要;
2)如果一个代码块内嵌套了另一个代码块(所谓代码块,即由大括号括起来的部分),嵌套代码块中的变量会屏蔽掉外层代码块的同名变量,包括全局变量;
3)编译器使用存储区中开辟了一个栈来维护自动变量;
2.2静态变量
静态变量的存储持续性是静态存储持续性,但是其作用域和链接性取决于变量声明的位置
外部链接性、全局:在代码块外部,即文件中声明,无修饰符
内部链接性、全局:在代码块外部,即文件中声明,并且使用关键字static修饰;
无链接性、局部:在代码块内部声明变量,并且使用关键字static修饰;
1)静态变量在程序运行开始就存在,直到程序结束后释放;
2)如果没有显式初始化,则静态变量默认初始化为0。如果初始化时没有足够的信息(例如调用了函数),则需要在链接完成后动态初始化;
3)编译器在存储区中开辟了一个静态存储区存放所有静态变量;
4)单定义规则(One Definition Rule):
变量只能有一次定义。定义意味着初始化,系统会为其分配内存;
![]()
图2 五种变量存储方式[1]
2.2.1外部链接性、全局变量
1)需要在一个文件定义该变量,变量被定义后,可以在这个文件中该变量后面任何位置使用它;
2)如果其他文件需要使用该变量,则需要声明该变量,声明时用extern关键字修饰;
将1)、2)总结为一个定义,多个声明;
3)初始化也可以使用extern关键字修饰,但是效果与不使用相当;
4)为了防止内部同名变量屏蔽全局变量,可以使用作用域解析符来指出变量;
2.2.2内部链接性、全局变量
使用static修饰的内部链接性、全局变量,会屏蔽同名的外部链接性、全局变量;
2.2.3无链接性、局部变量
这种类型的变量通常用于函数内部中,不想每次进入函数都被初始化的变量。可以实现某些特定功能,比如,记录函数被调用的次数。
3.限定符
3.1volatile限定符
该限定符说明变量在程序没有对其进行修改的情况下,内存中的数据也可能会发生改变。上述情况通常发生在内存地址为某硬件输入端口时。
在不使用volatile限定符时,如果程序在没有对变量修改的情况下,多次使用该变量,编译器会进行优化操作,不会重复读取内存,而是直接将数据放入寄存器。
使用volatile限定符,则向编译器表明不需要此优化,每次使用该变量都向内存读取。
3.2mutable限定符
在类或者结构声明中使用,表明某个变量是可以修改的。即使在初始化结构或类时,使用了const关键字。
3.3const限定符
1)使用const限定符修饰变量,表明该变量是不允许被修改,等价于常量;
2)在文件中所有代码块外,只有const限定符,没有extern限定符修饰的全局变量,链接性是内部的,其对于其他文件不可见。此举可以方便在头文件中使用例如 const int pi = 3.14 的语句,然后在其他文件都包含该头文件是不会出错;
3)同时有extern 和 const 修饰的变量,其链接性为外部;
4.名称空间
除了利用以上C++作用域规则外,C++还提供了名称空间工具,来解决程序中可能出现的名称相同而导致的冲突问题。
1)使用关键字 namespace 可以创建一个名称空间,不同命名空间里相同的名称不会冲突;
2)命名空间可以嵌套,但是不能位于代码块中;
3)全局命名空间:对应于全局变量所在的空间;
4)可以将变量加入到已有的命名空间中;
5)使用 using 声明,如 using std::cout,将 命名空间 std 中的 名称 cout 暴露在当前代码
块,或是当前文件中,作用域范围取决于 using 声明的位置;
6)使用using namespace 编译指令,使命名空间中的所有名称暴露;
7)使用using namespace 编译指令,如果当前作用域内有存在同名,那么 using 命名空间中的名称将被隐藏;
8)不能使用using 声明来暴露一个在当前代码块中已经使用过的名称;
参考文献
[1] Prata S. C++ Primer Plus[M]. 2002