[C++] 变量和函数的链接性

1. 单独编译

和C语言一样,C++也允许甚至鼓励程序员将组件函数放在独立的文件中。
可以单独编译这些文件,然后将它们链接成可执行的程序。
通常C++编译器既编译程序,也管理链接器。
如果只修改了一个文件,则可以只重新编译该文件,然后将它与其他文件的编译版本链接。
这使得大程序的管理更便捷。

头文件中常包含以下内容:
函数原型,使用#defineconst定义的符号常量,结构声明,类声明,模板声明,内联函数。

注意在包含头文件时,我们使用coordin.h而不是<coordin.h>
如果文件名包含在尖括号中,则C++编译器将在存储标准头文件的主机系统的文件系统中查找,
但如果文件名包含在双引号中,则C++编译器首先查找当前工作目录或源代码目录(或其他目录,这取决于编译器),
如果没有在那里找到头文件,则将在标准位置查找。
因此,在包含自己的头文件时,应使用引号而不是尖括号。

2. 存储持续性

C++使用三种(在C++11中是四种)不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间。

(1)自动存储持续性
在函数定义中声明的变量(包括函数参数)的存储持续性为自动的,
它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放。

(2)静态存储持续性
在函数外定义的变量,和使用关键字static定义的变量的存储持续性都为静态。
它们在程序整个运行过程中都存在。

(3)线程存储持续性(C++11)
当前多核处理器很常见,这些CPU可同时处理多个执行任务。
这让程序能够将计算放在可并行处理的不同线程中。
如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长。

(4)动态存储持续性
new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。
这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)。

注:
在C++11中,关键字auto用于自动类型推断,
但在C语言和以前的C++版本中,auto的含义截然不同,它用于显式的指出变量为自动存储。

3. 作用域

作用域(scope)描述了名称在文件(编译单元)的多大范围内可见。
例如,函数中定义的变量可在该函数中使用,但不能在其他函数中使用。
而在文件中的函数定义之前定义的变量则可在所有函数中使用。

C++变量的作用域有多种,作用域为局部的变量只在定义它的代码块中可用。
代码块是由花括号括起的一系列语句。
作用域为全局(也叫文件作用域)的变量,在定义位置到文件结尾之间都可用。

自动变量的作用域为局部,
静态变量的作用域是全局还是局部取决于它是如何被定义的。
在函数原型作用域中使用的名称只能在包含参数列表的括号内可用(这就是为什么这些名称是什么以及是否出现都不重要的原因)。
在类中声明的成员的作用域为整个类,
在名称空间中声明的变量的作用域为整个名称空间(由于名称空间已经引入到C++语言中,因此全局作用域是名称空间作用域的特例)。

4. 变量的链接性

链接性(linkage)描述了名称如何在不同单元间共享。
链接性为外部的名称,可在文件间共享,链接性为内部的名称,只能由一个文件中的函数共享。
自动变量的名称没有链接性,因为它不能共享。

链接性为外部的变量,通常简称为外部变量,它们的存储持续性为静态,作用域为整个文件。

4.1 extern

一方面,在每个使用外部变量的文件中,都必须声明它,
另一方面,变量只能有一次定义。
C++提供了两种操作,一种称为定义,它给变量分配存储空间。
另一种是引用声明,它不给变量分配存储空间,而是引用已有的变量。

引用声明使用关键字extern,且不进行初始化,否则将变成定义,导致分配空间。

double up;    // definition, up is 0
extern int blem;    // blem defined elsewhere
extern char gr = 'z';    // definition because initialized

extern char gr = 'z';中的extern是可以省略的。

如果在多个文件中使用外部变量,只需要在一个文件中包含该变量的定义,
但在使用该变量的其他所有文件中,都必须使用关键字extern声明它。

注:
省略extern将使变量具有外部链接属性。

4.2 static

(1)用于作用域为整个文件的变量时

static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。
链接性为内部的变量只能在其所属的文件中使用。

如果文件定义了一个静态变量,其名称与另一个文件中声明的常规外部变量相同,
则在该文件中,静态变量将隐藏常规外部变量。

// file1
int errors = 20;    // external declaration

- - -
// file2
static int errors = 5;    // known to file2 only

void froobish(){
    cout << errors;    // uses errors defined in file2
}

可使用外部变量在多文件程序的不同部分之间共享数据,
可使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据。
(名称空间提供了另外一种共享数据的方法)
如果将作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。

(2)用于作用域为整个文件的变量时
static限定符用于在代码块中定义的变量时,将导致局部变量的存储持续性为静态的。
这意味着虽然该变量只在该代码块中可用,但它在该代码块不处于活动状态时仍然存在。
因此,在两次函数调用之间,静态局部变量的值将保持不变。

另外,如果初始化了静态局部变量,则程序只在启动时进行一次初始化。
以后再调用函数时,将不会像自动变量那样再次被初始化。

注:
关键字static被用在作用域为整个文件的声明中时,表示内部链接性,
被用于局部声明中,表示局部变量的存储持续性为静态的。

关键字extern表示引用声明,即声明引用在其他地方定义的变量。
关键字thread_local指出变量的持续性与其所属线程的持续性相同。
thread_local变量之于线程,犹如常规静态变量之于整个程序。

5. 函数的链接性

在默认情况下,函数的链接性为外部的,即可以在文件间共享。
可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的,不过这是可选的。

可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用。
必须同时在原型和函数定义中使用static关键字。

static int fn(double x);

...
static int fn(double x){
    ...
}

这意味着该函数只在这个文件中可见,还意味着可以在其他文件中定义同名的函数。
和变量一样,在定义静态函数的文件中,静态函数将覆盖外部定义,
因此,即使在外部定义了同名的函数,该文件扔将使用静态函数。

单定义规则也适用于非内联函数,因此对于每个非内联函数,程序只能包含一个定义。
对于链接性味外部的函数来说,这意味着在多文件程序中,只能有一个文件包含该函数的定义,
但使用该函数的每个文件都应包含其函数原型。

内联函数不受这种规则的约束,这允许程序员能够将内联函数的定义放在头文件中,
这样包含了头文件的每个文件都有内联函数的定义。
然而,C++要求同一个函数的素有内联定义都必须相同。

注:
在默认情况下,函数的链接性为外部的,即可以在文件间共享。

6. 小结

所有声明,都具有外部连接性。

6.1 具有内部连接性的定义

(1)名字空间(包括全局名字空间)中的静态自由函数,静态友元函数、静态变量定义,const常量定义
(2)enum定义,类的定义,union的定义
(3)inline函数定义(包括自由函数和非自由函数)

6.2 具有外部连接性的定义

(1)非inline的类成员函数,非inline的类静态函数
(2)类静态成员变量
(3)名字空间(包括全局名字空间)中非静态自由函数,非静态友元函数,非静态变量

注:
把一个带有外部连接的定义放在 .h 文件中都会引起错误,
由于类的声明和定义都是内部连接的,一般都放在 .h 文件中。


参考

C++ Primer Plus, 6th - P300~P319
C++ 概念两则:声明和定义,内部连接和外部连接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容