C/C++教程

关于C++存储的持续性、作用域、链接性

本文主要是介绍关于C++存储的持续性、作用域、链接性,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

参考C++PrimerPlus第九章涉及“内存模型”部分,是比较基础的知识点。书中对变量“是否可见”、“何时可见”的问题进行了细致解释,这里用小篇幅总结一下。

一:“不同的C++存储方式是通过存储的持续性、作用域和链接性来描述的”

1、C++的4种数据存储方案的持续性

C++11有4种不同的方案来存储数据,区别是数据保存在内存中的时间

类型特点持续性特点
自动存储函数内部定义的常规(局部)变量在程序执行其所属代码块时被创建,代码块结束后内存被释放
静态存储函数外定义/static修饰声明的变量在程序执行的整个期间一直存在
动态存储new出来的,内存分配在heap的变量不受程序或函数的生存时间控制
线程存储thread_local修饰声明的变量与所属线程一样长

静态的情况是本文的重点。

2、作用域和链接性

作用域(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只是显式地指出变量是自动的。

以下是本篇文章的重点

三:静态存储的关键点

1、静态存储有3种链接性:

链接性特点创建地点
外部可在其他文件中访问代码块外声明
内部只能在当前文件中访问代码块外声明并使用static限定符
只能在当前函数或代码块中访问代码块内声明并使用static限定符
...
int global = 100;			//外部,作用域文件,其他文件能用
static int one_file = 50;	//内部,作用域文件,这个文件中代码都能用,其他文件不能用
void func1(int n){
	static int count = 0;	//无链接性,作用域代码块,只能在fun1中使用
	int llama = 0;
	...
}
int main(){
	...
}

2、初始化过程

静态变量都要初始化。
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++要求同一函数的所有内联定义都必须相同。

这篇关于关于C++存储的持续性、作用域、链接性的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!