Extern Keyword in C++

Do this at first: Static Keyword in C++

External Linkage

在学习 static 关键字的时候,我们了解到了内部链接性:被 static 修饰的变量和函数仅在定义它们的翻译单元(即当前.cpp文件)内可见。这种封装特性为代码模块化提供了基础。如果不加入 static 关键字,那么所有的全局变量和函数在所有的翻译单元中都可见,这就是所谓外部链接性。(局部变量不具有链接性)

我们用下面的例子来解释,我们有两个文件 a.cppmain.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 存在的意义就是为避免一定程度上的命名冲突。

符号全局唯一

其他情况

编译a.cpp

生成a.o符号表

编译main.cpp

生成main.o符号表

链接器

符号解析

完成链接

链接错误

在现代 C++ 中,我们常用 namespace 来封装全局变量,避免全局符号的精确定义。你也可以用 inline 请求编译器将某个全局符号内联化。

Extern "C"

此外,我们还可能经常看到如下的 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 编译后的代码,链接器会无法识别这些符号。