C++中的 "extern "一词是一个指定符。本文将解释它在C++中对变量和函数的使用。首先,我们给出了C++中声明和定义的含义。考虑一下下面几行代码。
int it;
char fn(int itg, char ch);
it = 5;
char fn(int itg, char ch) {
char var = 'o';
if (itg == 1 && ch == 'a')
var = 'z';
return var;
}
第一行是一个变量声明。第二行是一个函数签名,如果它没有以分号结束,后面是一个函数体。这第二行以分号结尾是一个函数原型。它也是一个函数声明。第三行为整数变量赋值:这是一个变量初始化,但仍然可以大致看作是一个变量定义。这段代码的其余部分是一个函数定义。它以函数签名开始,接着是函数体。
在C++中,当涉及到声明和定义时,有一个细微的差别。下面的语句是一个变量声明。
int it = 5;
像这样一个完整的变量声明,在其中介绍了变量,然后分配了一个值,仍然是一个变量声明。所以,一个变量声明可以只是单独介绍变量,也可以是带有定义的介绍。
下面的代码(从上面复制的)是一个函数声明。
char fn(int itg, char ch) {
char var = 'o';
if (itg == 1 && ch == 'a')
var = 'z';
return var;
}
像这样一个完整的函数声明,它的签名介绍了函数,然后后面是函数体,仍然是一个函数声明。所以,一个函数声明可以只是单独的函数原型,也可以是函数签名和函数体一起。
所以,一个声明可以有也可以没有定义。定义就像是声明的一个子部分。对于变量,第一次赋值实际上是初始化,并不是真正的定义。当一个变量第一次被声明而没有初始化时,它在内存中的位置已经被提供,但它是空的。在这个位置上装入一个值就是初始化,也就是完成了定义。
传统上,一个简单的C++应用程序有三个文件。它有一个主文件,可以被称为第一个文件。它有一个第二文件和一个头文件。使用extern指定符可以将文件的数量减少到两个(从三个)。这篇文章解释了在变量和函数中使用extern指定符来避免头文件。注意:在C++词汇中,这样的两个文件被称为翻译单元。
文章内容
没有extern的头文件
传统上,一个简单的C++应用程序有三个文件:带有main()函数的主文件,可以调用第一个文件,第二个文件,以及一个头文件。头文件应该有变量和函数的声明,但没有它们的定义。头部声明的定义应该在第二个文件中。在第一个文件的顶部,必须要有。
#include "head.hh"
其中head.hh是头文件的名称,它位于用户主页目录中。include指令没有以分号结尾。在这种情况下,头文件中没有定义的变量声明和没有函数定义的函数原型,不应该在前面加上extern指定符。而应用程序应该可以正常工作。
插图
上面的变量和函数在这里是用来说明的。
在文本编辑器中键入以下代码,并将其保存在用户主页目录中,名称为head.hh。
int it = 5;
char fn(int itg, char ch);
头部只有两条语句。接下来,在文本编辑器的无标题文档中输入以下内容,并保存在用户主页目录中,名称为second.cpp 。
char fn(int itg, char ch) {
char var = 'o';
if (itg == 1 && ch == 'a')
var = 'z';
return var;
}
接下来,在文本编辑器的另一个无标题文档中输入以下代码,并将其保存在用户主页目录下,名称为first.CPP。
#include "head.hh"
#include
using namespace std;
int main()
{
cout << it << endl;
cout << fn(1, 'a') << endl;
return 0;
}
用以下终端命令编译该应用程序。
g++ first.cpp second.cpp -o complete.exe
用以下命令执行该应用程序。
./complete.exe
输出结果是。
5
z
不幸的是,头文件不允许在没有初始化的情况下简单声明一个变量(例如,it)。然而,这个问题可以按照下面的方法解决。
没有头文件的extern
如果适当地使用extern指定符,就可以省去头文件。在这种情况下,会有一个变量和函数的声明,在第一个(主)文件中没有定义。每一个都会在前面加上extern。
示例
在文本编辑器中键入以下代码,并将其保存在用户主页目录中,名称为 first.cpp 。
#include
using namespace std;
extern int it;
extern char fn(int itg, char ch);
int main()
{
cout << it << endl;
cout << fn(1, 'a') << endl;
return 0;
}
接下来,在文本编辑器的无标题文档中输入以下内容,并保存在用户主页目录下,名称为second.cpp。
int it = 5;
char fn(int itg, char ch) {
char var = 'o';
if (itg == 1 && ch == 'a')
var = 'z';
return var;
}
变量和函数的定义已经在第二个文件中发生了。在这里的第一个文件中,它们的声明没有定义。在这个新的应用程序中没有包含头文件。只涉及两个文件。请注意,在第二个文件中,变量已经被完全声明,但没有extern这个词。即使是函数,也是在没有extern这个词的情况下完全声明的。然而,"extern "这个词必须在第一个文件中的部分声明之前出现。
用下面的终端命令编译该程序。
g++ first.cpp second.cpp -o complete.exe
用以下命令运行该应用程序
./complete.exe
输出结果是。
5
z
和以前一样,但没有任何头文件。
所以,extern指定符链接了两个文件之间的声明。一个文件应该做没有定义的声明,并带有extern。另一个文件应该做一个定义,这将是一个完整的声明,但没有extern。
头文件和extern
上面的应用程序有一个问题,就是变量必须在头文件中完全声明。为了让头文件中的变量声明没有定义,该变量必须在前面加上extern。所以,如果有
extern int it;
在头文件中,就会出现
int it = 5;
在第二个文件中,仍然会有
#include "head.hh"
在第一个文件(主文件)的顶部。
常量和extern
在正常情况下,一个常量必须被初始化。比如说
const char ch = 'e';
是允许的,而
const char ch;
是不允许的。
然而,通过extern指定符,一个常量可以在第一和第二文件中不经初始化就被声明。因此,如果在第一个文件中,有
extern const char ch;
在第二个文件中,将有
char ch = 'e';
在第二个文件中没有const。两个文件中的ch是同一个实体。
用以下内容替换第一个.cpp文件并保存。
#include
using namespace std;
extern const char ch;
int main()
{
cout << ch << endl;
return 0;
}
用以下内容替换第二个.cpp文件并保存。
char ch = 'e';
用下面的终端命令编译该应用程序。
g++ first.cpp second.cpp -o complete.exe
用以下命令运行该应用程序
./complete.exe
输出结果应该是,e.
Extern和static
C++中的存储类指定器是static、thread_local、extern、mutable。在一个给定的声明中不能使用多于一个。然而,在少数情况下,thread_local和static可能出现在一个实体声明的前面,或者thread_local和extern可能出现在一个实体声明的前面。所以,extern和static永远不能作为一个声明的指定符出现。
结论
extern指定符链接了同一实体的两个声明,这两个声明在两个不同的文件中。拥有extern指定符的声明不应该被初始化或定义。另一个文件中没有extern指定符的声明应该被初始化或定义。这个方案适用于变量和函数。它消除了为感兴趣的变量和函数编写头文件的需要。它允许在一个文件和另一个文件中声明一个常量而不进行初始化。如果程序员想要一个头文件,那么为了让头文件中的变量不被初始化,程序员必须对头文件中的变量使用extern。