亲宝软件园·资讯

展开

C++存储链接性原理详解

码小方 人气:0

链接性

链接性是指名称在不同文件之间能否共享,而作用域是指名称在文件内部哪些范围可见。

这里的文件并非开发时创建的文件,而是将文件的 include 内容全部递归包含进来之后,形成的大文件。

这一点也是与Go等一些语言不同。在Go语言中,文件是按包(package)组织,所有依赖的包都需要直接或间接import进来。也就是从main开始递归import得到的就是我们依赖的所有文件。

但C++不同。C++源文件主要分.h 和 .cpp两种,一般我们都只会include .h文件,而不会include .cpp文件。因此,.cpp文件之间其实没有直接关联,需要通过cmakelist等方式告诉编译器,我们的程序涉及到哪些源文件。

而C++在编译时,会首先将include的文件内容全部递归包含进来,形成一个大的文件,这个大文件是一个编译单元,也就是上面链接性所说的文件。其实,C++的宏定义的可使用范围,也是在这个大文件内。

链接性有三种:

自动类型变量都没有链接性,而静态类型变量可以有三种链接性。那么如何定义这三种链接性的静态变量呢?

外部链接性

链接性为外部的变量也叫外部变量,也称全局变量。外部变量在函数外声明,不加static关键字。外部变量可以在所有文件使用。

对于函数来说,没有加inline和static关键字的函数,都具有外部链接性。

说到外部,可能会想到一个关键字:extern。这个关键字有什么用?实际上它是用来做引用声明。因为如果想使用其他文件中定义的外部变量,不能直接使用,而是要先进行引用声明,表示要引用这个外部变量,这里就需要用到关键字extern。例如:

// file1.cpp
int foo = 1;

// file2.cpp
extern int foo;
// extern int foo = 1; WRONG
cout << foo;

file1.cpp 在函数之外定义了全局变量foo,在file2.cpp中,用extern关键字声明之后,即可使用foo了。注意,extern语句中不能初始化foo,否则这里就变成定义而不是引用声明,导致重复定义全局变量foo,编译错误。

单定义规则

对于外部变量,每个使用它的文件都必须声明它。而C++又有“单定义规则”,即链接性为外部的函数和变量可以有多个声明,但只能有一个定义。这里再明确下这两个术语:“定义声明”,简称“定义”;“引用声明”,简称“声明”。

为了实现单定义规则,编译器要知道这一行代码是在声明还是在定义,那怎么区分声明还是定义呢?

对于函数来说,区分声明和定义很简单,有函数体则是定义,否则为声明。而变量则不同,前面所谓的变量声明,对于编译器来说都是定义,都分配了存储空间。如何声明一个变量而不分配存储空间呢?关键字extern就派上用场了,使用extern关键字且没有进行初始化,则为声明,不会分配存储空间,否则为定义。

C++初学者可能还不太明白为什么C++中都要把函数声明放在.h文件,把函数定义放在.cpp文件中。其实用单定义规则就很好解释了。函数如果没有加inline和static,即具有外部链接性,如果把定义放在.h文件中,这个.h文件会被多个.cpp文件引用,编译时会形成多个副本,相当于被定义了多次。

总之,.h文件中只能放声明,或者没有外部链接性的定义。

可能有小伙伴发现,有些定义在函数外的静态变量没有加static,也会放在.h文件中,为什么可以呢?其实const变量默认会添加static,链接性变为内部。而如果想要声明为外部变量,则需要加上external:

const int foo = 10;  // 链接性为内部
extern const int bar = 10; // 链接性为外部

内部链接性

前面说了,定义在函数外部的变量默认是全局变量,具有外部链接性。但如果加上static说明符,则变成内部链接性。对于函数也一样,加上static则变为内部链接性。

在函数外定义的变量,static的含义与局部变量中static的含义不同。前者表示链接性为内部,后者表示存储持续性为静态。这也可以称为关键字重载,即关键字在不同上下文中有不同含义。

具有内部链接性的变量或函数,可以在不同文件中有多个定义。内部链接性的变量也可以与同名外部变量同时存在,这时内部变量将隐藏外部变量。对函数来说也一样。

无链接性

定义在函数或代码块内的局部变量没有链接性,只能在局部使用。如果加上static修饰则为静态变量,虽然在程序运行期间会一直存在,但只有在代码块内才能使用。

总结

下面总结一下前面提到的不同存储持续性与链接性的变量。

在代码块内定义

在代码块内定义,用static关键字

在代码块外定义,用static关键字 或const修饰

在代码块外定义。引用声明则需要用extern

加载全部内容

相关教程
猜你喜欢
用户评论