# C++ 中 extern 作用

面试高频指数:★★★★☆

一般而言,C++全局变量的作用范围仅限于当前的文件,但同时C++也支持分离式编译,允许将程序分割为若干个文件被独立编译。

于是就需要在文件间共享变量数据,这里extern就发挥了作用。

extern 用于指示变量或函数的定义在另一个源文件中,并在当前源文件中声明。 说明该符号具有外部链接(external linkage)属性。

也就是告诉编译器: 这个符号在别处定义了,你先编译,到时候链接器会去别的地方找这个符号定义的地址。

# 1. 符号的声明与定义

首先明白 C/C++ 中变量的声明和定义是两个不同的概念。 声明是指告诉编译器某个符号的存在,在程序变量表中记录类型和名字,而定义则是指为该符号分配内存空间或实现其代码逻辑。

凡是没有带extern的声明同时也都是定义。 而对函数而言,带有{}是定义,否则是声明。如果想声明一个变量而非定义它,就在变量名前添加关键字extern,且不要显式的初始化变量。

# 1.1 变量的声明与定义

// 声明
extern int global_var;

// 定义
int global_var = 42;

在上面的示例中,global_var 变量的声明使用 extern 关键字告诉编译器它的定义在其他源文件中,而定义则是为变量分配内存空间并初始化为 42。

# 1.2 函数的声明和定义:

// 声明
int sum(int a, int b);

// 定义
int sum(int a, int b) {
  return a + b;
}

在上面的示例中,sum 函数的声明告诉编译器该函数的存在及其参数和返回值类型,而定义则是实现函数的代码逻辑。

# 2. C/C++ 中链接属性

要彻底理解 extern 如何起作用的,需要了解一下 C/C++ 程序的 编译、链接过程,如果想要深入学习的话,请阅读 《程序员自我修养》 ,其中第二章开始就有讲到编译和链接

PDF 版本获取指南:系统编程相关书籍下载 (opens new window)

在 C++ 中,链接属性是指程序在编译、链接和执行阶段如何处理符号(变量、函数、类等)的可见性和重复定义。 C++ 语言规定有以下链接属性:

# 2.1. 外部链接(External Linkage)

外部链接的符号可以在不同的源文件之间共享,并且在整个程序执行期间可见。全局变量和函数都具有外部链接。

# 2.2 内部链接(Internal Linkage)

内部链接的符号只能在当前源文件内部使用,不能被其他源文件访问。用 static 修饰的全局变量和函数具有内部链接。

# 2.3 无链接(No Linkage)

无链接的符号只能在当前代码块(函数或代码块)内部使用,不能被其他函数或代码块访问。用 constconstexpr 修饰的常量具有无链接属性( 通常情况下编译器是不会为const对象分配内存,也就无法链接)。

# 2.4 外部 C 链接(External C Linkage)

外部 C 链接的符号与外部链接类似,可以在不同的源文件之间共享,并且在整个程序执行期间可见。

它们具有 C 语言的名称和调用约定,可以与 C 语言编写的代码进行交互。

在 C++ 中,可以用 extern "C" 关键字来指定外部 C 链接,从而使用一些 C 的静态库。

这些链接属性可以通过关键字 externstaticconstextern "C" 来显式地指定。

在实际的开发中,正确地理解和处理链接属性对于编写可重用、高效、可维护的代码非常重要。

# 3. extern 作用

# 3.1 声明变量但不定义

声明变量或函数的存在,但不进行定义,让编译器在链接时在其他源文件中查找定义。

这使得不同的源文件可以共享相同的变量或函数。

当链接器在一个全局变量声明前看到 extern 关键字,它会尝试在其他文件中寻找这个变量的定义。

这里强调全局且非常量的原因是,全局非常量的变量默认是外部链接的。

//fileA.cpp
int i = 1;         //声明并定义全局变量i

//fileB.cpp
extern int i;    //声明i,链接全局变量

//fileC.cpp
extern int i = 2;        //错误,多重定义
int i;                    //错误,这是一个定义,导致多重定义
main()
{
    extern int i;        //正确
    int i = 5;            //正确,新的局部变量i;
}

# 3.2 常量全局变量的外部链接

全局常量默认是内部链接的,所以想要在文件间传递全局常量量需要在定义时指明extern,如下所示:

//fileA.cpp
extern const int i = 1;        //定义

//fileB.cpp                    //声明
extern const int i;

而下面这种用法则会报链接错误,找不到 i 的定义:

//fileA.cpp
const int i = 1;        //定义 (不用 extern 修饰)

//fileB.cpp                    //声明
extern const int i;

# 3.3 编译和链接过程

编译链接过程中,extern 的作用如下:

  1. 编译期extern 用于告诉编译器某个变量或函数的定义在其他源文件中,编译器会为它生成一个符号表项,并在当前源文件中建立一个对该符号的引用。

这个引用是一个未定义的符号,编译器在后续的链接过程中会在其他源文件中查找这个符号的定义。

  1. 链接期,链接器将多个目标文件合并成一个可执行文件,并且在当前源文件中声明的符号,会在其它源文件中找到对应的定义,并将它们链接起来。

下面是一个使用 extern 声明全局变量的示例:

// file1.cpp
#include <iostream>
extern int global_var;

int main() {
  std::cout << global_var << std::endl;
  return 0;
}

// file2.cpp
int global_var = 42;

在上面的示例中,file1.cpp 文件中的 main 函数使用了全局变量 global_var,但是 global_var 的定义是在 file2.cpp 中的,因此在 file1.cpp 中需要使用 extern 声明该变量。

在编译时,编译器会为 global_var 生成一个符号表项,并在 file1.cpp 中建立一个对该符号的引用。

在链接时,链接器会在其他源文件中查找 global_var 的定义,并将其链接起来。

最新原创的文章都先发布在公众号,欢迎关注哦~,
扫描下方二维码回复「CS」可以获得我汇总整理的计算机学习资料~

编程指北图片
@2021-2024 编程指北 版权所有 粤ICP备2021169086号-2