tags:
- Cpp
Extern Keyword in C++
Do this at first: Static Keyword in C++
在学习 static
关键字的时候,我们了解到了内部链接性:被 static
修饰的变量和函数仅在定义它们的翻译单元(即当前.cpp文件)内可见。这种封装特性为代码模块化提供了基础。如果不加入 static
关键字,那么所有的全局变量和函数在所有的翻译单元中都可见,这就是所谓外部链接性。(局部变量不具有链接性)
我们用下面的例子来解释,我们有两个文件 a.cpp
和 main.cpp
,也就是说我们有两个翻译单元:
// a.cpp
int global_var_a = 1; // external definition with external linkage
static int static_var_a = 0; // internal linkage
int global_fun_a(void) // external definition with external linkage
{
int local_a = 1; // non-external
b += local_a;
return b;
}
static void staitc_func_a() // internal linkage
{
}
在 a.cpp
中,我们定义了一个全局变量 global_var_a
、一个静态变量 static_var_a
并用 extern
声明了一个变量 b
。后面,我们还定义了一个全局的函数和静态的函数。
我们上面所定义的所有全局的变量、函数都具备外部链接性。所有其他的 .cpp
文件都可以看到并使用这些全局符号,如在下面的 main.cpp
中所作的一样。
// main.cpp
extern int global_var_a; // external declaration with external linkage
extern int global_fun_a; // external declaration with external linkage
int main(){
global_var_a = 100;
global_fun_a();
return 0;
}
但为什么在使用前,我用 extern
声明了这些全局变量?extern
的作用是告诉链接器这些符号是其他翻译单元的。因为链接器需要确保每个符号的单一定义规则。如果你不加 extern
链接器可能会误以为你在 main.cpp
里定义了一个未初始化的全局变量。extern
存在的意义就是为避免一定程度上的命名冲突。
在现代 C++ 中,我们常用 namespace
来封装全局变量,避免全局符号的精确定义。你也可以用 inline
请求编译器将某个全局符号内联化。
此外,我们还可能经常看到如下的 extern
用法:
// example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
#ifdef __cplusplus
extern "C" {
#endif
void my_c_function();
#ifdef __cplusplus
}
#endif
#endif // EXAMPLE_H
通常我们在 C++ 中调用 C 的函数库时会用到 extern "C"
。这是因为 C++ 会对函数名进行名词修饰(name mangling)。简单的来说,就是 C++ 支持函数重载,而 C 语言并不支持。这就导致 C++ 编译器生成的符号和 C 编译器生成的符号有差别,进而导致链接错误。
比如我们有下面的函数:
int add(int i, int j){}
使用 C++ 编译器编译后生成的符号名是 _Z3addii
而使用 C 编译器所生成的汇编函数名直接就是 add
。你看到 C++ 编译器添加了许多字符来修饰函数,这是 C++ 实现静态多态的基石。如果直接链接 C 编译后的代码,链接器会无法识别这些符号。