在C++中何时使用extern

544

我正在阅读《C++ 编程思想》,并且它介绍了 extern 声明。例如:

extern int x;
extern float y;

我认为我理解了“声明而未定义”的含义,但我想知道它何时会被证明有用。

有人能举个例子吗?


1
我曾多次使用extern提供定义。当另一个源文件中的表仅被定义时,Microsoft工具会产生缺少符号的链接错误。问题在于,该表是const的,而C++编译器将其提升为翻译单元中的static。例如,请参见ariatab.cppkalynatab.cpp - jww
4
我认为Nik的回答是正确的,因为他是唯一一个似乎回答了一个C++问题的人。其他人都已经离题到了C问题上。 - jww
4
自C++17起,使用inline可能是实现你想要通过extern实现的功能的更好方式。使用inline,你可以在头文件中定义全局变量,而不必担心多重定义问题。 - Joshua Jurgensmeier
参见:如何在一个C源代码文件中调用另一个C源代码文件中的静态函数?。我刚刚在这里添加了一个详细的答案,其中展示了extern的许多用法并详细讨论了它。 - undefined
4个回答

722

当你有全局变量时,这会很有用。你在头文件中声明全局变量的存在,以便包含该头文件的每个源文件都知道它的存在,但是你只需要在其中一个源文件中进行一次“定义”。

为了澄清,使用extern int x;告诉编译器,类型为int、名为x的对象存在于某个地方。编译器并不知道它的存在位置,它只需要知道类型和名称,以便知道如何使用它。一旦所有源文件都被编译,链接器将把x的所有引用解析到在编译的源文件中找到的一个定义中。为了使其工作,x变量的定义需要具有所谓的“外部链接”,这基本上意味着它需要在函数之外(通常称为“文件范围”)且没有static关键字进行声明。

头文件:

#ifndef HEADER_H
#define HEADER_H

// any source file that includes this will be able to use "global_x"
extern int global_x;

void print_global_x();

#endif
抱歉,我只能使用英文进行交流和回答。
#include "header.h"

// since global_x still needs to be defined somewhere,
// we define it (for example) in this source file
int global_x;

int main()
{
    //set global_x here:
    global_x = 5;

    print_global_x();
}

source 2:

#include <iostream>
#include "header.h"

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}

26
谢谢。所以,如果我在头文件中声明一个全局变量但没有使用extern关键字,那么包含此头文件的源文件将无法看到它? - Aslan986
38
在头文件中不应该声明全局变量,因为当两个文件都包含同一个头文件时,它们将无法链接(连接器将会发出“重复符号”的错误)。 - kuba
93
不,更糟糕的情况发生了。每个包含这个头文件的源文件将拥有自己的变量,因此每个源文件都可以独立编译,但链接器会抱怨,因为两个源文件将具有相同的全局标识符。 - dreamlax
11
如果你不使用"extern"这个词,那么变量就存在了。当你使用"extern"时,它表示"嘿,这个变量在其他地方"。很抱歉我没有回答这是定义还是声明,因为我总是混淆这两者。 - kuba
5
@include guard只对包含它的源文件起作用,防止同一头文件在同一源文件中被重复包含(以防其他头文件也包含它等)。因此,即使有@include guard,每个包含该头文件的源文件仍将具有自己的定义。 - dreamlax
显示剩余14条评论

254

当你需要在几个模块之间共享一个变量时,这非常有用。你可以在一个模块中定义它,在其他模块中使用extern。

例如:

在file1.cpp文件中:

int global_int = 1;

在 file2.cpp 文件中:

extern int global_int;
//in some function
cout << "global_int = " << global_int;

63
这个回答比被采纳的更正确,因为它没有使用头文件,并且明确说明只有在少数模块之间共享时才有用。对于较大的应用程序,最好使用例如ConfigManager类。 - Zac
2
当涉及到命名空间时,是否有任何需要注意的地方?如果global_int在全局命名空间中,如果我要在file2.cpp中的某个命名空间部分中使用它,我必须正确地进行作用域限定吗?例如:namespace XYZ{ void foo(){ ::global_int++ } }; - jxramos
12
另一方面,如果在头文件中没有声明全局变量,您无意中使确定其实际定义位置变得更加困难。通常,如果在abc.h 中看到声明的全局变量,很可能会在abc.cpp中定义。优秀的IDE工具可以提供帮助,但良好组织的代码始终是更好的解决方案。 - dreamlax
@TomSawyer,你不需要在这种情况下进行包含。file1.cpp和file2.cpp应该是不同的编译单元。将.cpp文件包含进来的做法非常不常见。 - Daniel Bauer
我们可以将这种方法用于共享函数吗? - Md.
显示剩余2条评论

97

重点在于链接(linkage)

之前的回答已经很好地解释了extern

但我想补充一个重要的观点。

你问的是关于 externC++中的用法,而不是C,我不知道为什么没有回答提到当externconst一起使用时的情况。

在C++中,默认情况下,const变量具有内部链接(不同于C)。

因此,这种情况会导致链接错误

源1:

const int global = 255; //wrong way to make a definition of global const variable in C++

来源2:

extern const int global; //declaration

需要像这样:

来源1:

extern const int global = 255; //a definition of global const variable in C++

来源2:

extern const int global; //declaration

3
为什么在C++中,在定义部分不包含 'extern' 的情况下却可以正常工作,这是为什么会出错的原因? - iartist93
1
我在使用Visual Micro的Visual Studio时似乎没有遇到过这个链接错误。我错过了什么? - Craig.Feied
1
@lartist93 @Craig.Feied 我认为你们可能需要仔细检查一下。即使编译器没有提示链接错误,你们也可以检查一下在定义中没有 extern 的情况下两个源文件中的对象是否相同。你可以通过在源文件2中打印出 global 的值来实现这一点。 - Van Tr
5
在MSVS 2018中,如果省略const int global = 255;中的extern,就会出现链接错误。 - Evg
我曾在 C++ 服务器代码中看到 extern 的使用,而客户端是用 C 编写的。我一直认为这样做是为了将 C++ 函数暴露给 C,否则 C 将无法链接到它。这正确吗? - wbg
1
@wbg如果这两个程序通过某种网络协议进行通信,那么使用的语言几乎总是无关紧要的。如果服务器-客户端类似于函数调用,那么很可能会使用extern "C"。你可以参考这篇帖子https://dev59.com/P3NA5IYBdhLWcg3wPLP-。 - yxz

16

当您希望使用全局变量时,此方法非常有用。您可以在某些源文件中定义全局变量,并在头文件中声明它们为extern,以便包含该头文件的任何文件都可以看到相同的全局变量。


无论如何,这听起来并不是很面向对象,我会将它们放入一个单例类中...或者一个返回本地静态值的函数中... - RzR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接