6.7 - 外部链接和变量前向声明
Key Takeaway
- 具有外部链接的标识符,可以在定义它们的文件中被使用,也可以通过前向声明在其他文件中使用。
- 链接是标识符的选项,而不是变量的属性,因此函数也有链接属性
- 函数是默认外部链接的,可以用
static
修改为内部链接 - 具有外部链接的全局变量有时候也称为外部变量。使用
extern
关键字可以将全局变量定义为外部变量(使其可以在其他文件中访问)。非常量的全局变量默认是外部变量。 - 前向声明一个定义在其他文件中的外部变量,也需要使用
extern
关键字 - 如果你希望定义一个未初始化的非const全局变量。请不要使用
extern
关键字,否则 C++ 会认为你是在前向声明某个变量 - 在C++中,所有全局变量都具有“文件作用域”,同时链接属性则决定了它们是否可以被其他文件使用。
在前面的课程中 (6.6 - 内部链接),我们讨论了如何使用内部链接将标识符限定在一个单独的文件中。在本节课中,我们会探讨外部链接。
具有外部链接的标识符,可以在定义它们的文件中被使用,也可以在其他文件中被使用(通过前向声明)。从这个意义上来讲,具有外部链接的变量才是真正的*”全局“变量,因为它可以在程序的任何地方被使用。
函数默认具体外部链接属性
在2.8 - 多文件程序中我们介绍过,你可以在一个文件中调用另外一个文件中的函数,这是因为函数默认具有外部链接属性。
为了能够调用其他文件中的函数,必须要在使用该函数的文件中进行前向声明。前向声明告诉编译器函数的存在,然后链接器可以将函数调用关联到实际的定义。
例如:
a.cpp | |
---|---|
1 2 3 4 5 6 |
|
main.cpp | |
---|---|
1 2 3 4 5 6 7 8 |
|
打印结果:
1 |
|
在上面的例子中,sayHi()
被前向声明在 main.cpp
中,这使得 main.cpp
可以访问定义在a.cpp
中的 sayHi()
。前向声明满足了编译器的需要,而链接器也能够将函数调用关联到函数定义。
如果sayHi()
具有内部链接属性,则链接器便不能够将函数调用关联到函数定义,会产生链接错误。
具有外部链接的全局变量
具有外部链接的全局变量有时候也称为外部变量(external variable)。使用 extern
关键字可以将全局变量定义为外部变量(使其可以在其他文件中访问):
1 2 3 4 5 6 7 8 9 |
|
非常量的全局变量默认是外部链接(即使使用 extern
也会被忽略)。
使用 extern 关键字进行变量的前向声明
为了使用定义在其他文件中的全局外部变量,你同样需要在使用它的文件中对其进行前向声明。对于变量来说,创建前向声明还需要使用extern
关键字(不需要初始化值)。
下面是一个前向声明的例子:
a.cpp | |
---|---|
1 2 3 |
|
main.cpp | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
在上面的例子中,a.cpp
和 main.cpp
都引用了一个相同的全局变量 g_x
。因此,即使 g_x
是在 a.cpp
中定义和初始化的,我们仍然可以在 main.cpp
中使用它(需要对g_x
进行前向声明)。
注意,extern
关键字在不同的语境下有不同的含义。有些语境下,extern
表示 “为变量创建外部链接”。而在另外的语句下,extern
表示 “这是一个定义在其他地方的外部变量的前向声明”。的确有点绕,所以我们会在6.11 - 作用域和链接小结中对这些问题进行详细的总结。
注意
如果你希望定义一个未初始化的非const全局变量。请不要使用 extern
关键字,否则 C++ 会认为你是在前向声明某个变量。
注意
尽管 constexpr 变量可以通过 extern
关键字赋予外部链接属性,但是由于它们并不能被前向声明,所以这么做并无意义。
这是因为编译器在编译时就必须指定 constexpr 变量的值。如果该变量被定义在其他文件中,那么编译器将无法知道它的值。
^ce6263
注意,函数的前向声明不需要使用 extern
关键字——编译器可以通过是否有函数体来判断此处是在定义一个新的函数,还是在进行前向声明。变量的前向声明是需要 extern
关键字的,它可以帮助区分变量定义和变量前向声明(看上去可能会完全一样):
1 2 3 4 5 6 7 |
|
文件作用域 vs. 全局作用域
“文件作用域”和“全局作用域”这两个术语可能会令人感到困惑,这主要是因为在非正式场合它们的用法不够严谨。从技术上来讲,在 C++中,所有全局变量都具有“文件作用域”,同时链接属性则决定了它们是否可以被其他文件使用。
考虑下面程序:
global.cpp | |
---|---|
1 2 |
|
main.cpp | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
变量 g_x
具有文件作用域(global.cpp
)—— 它可以被使用的范围从定义开始,到文件结束为止,但是它不能直接在 global.cpp
以外的地方被使用。
在 main.cpp
中,g_x
的前向声明具有文件作用域——它可以被使用的范围从声明开始,到文件结束为止。
不过,我们通常非正式地将“文件作用域”用来描述具有内部链接的变量,而用“全局作用域”来描具有外部链接的变量(因为这类变量可以通过前向声明在整个程序中被使用)。
小结
1 2 3 4 5 6 7 8 9 |
|
我们会在 6.11 - 作用域和链接小结中对相关内容进行更详细的总结。