extern
关键字适用于变量及函数,并且扩展了他们的可见性,这也就是它被命名为extern
的原因。
首先介绍一下声明(declaration)和定义(definition)的区别:
- 声明一个变量或者函数并没有给它们分配内存,它只是告诉程序它的类型是什么。在函数声明的情况下,它还告诉程序参数、它们的数据类型、这些参数的顺序以及函数的返回类型。
- 定义一个变量或者函数时,除了声明所做的一切之外,他还为该变量或函数分配内存。因此,我们可以把定义看作时声明的超集(或者声明是定义的子集)。
一个变量或者函数可以声明任意次数,但只能定义一次。
当我们在声明或定义函数时,会隐藏extern
关键字。例如,当我们定义如下函数时:
int foo(int arg1, char arg2);
编译器会将其视为
extern int foo(int arg1, char arg2);
由于extend
关键字将函数的可见性扩展到整个程序,因此可以在整个程序的任何文件中调用该函数,前提是这些文件包含该函数的声明。有了函数的声明,编译器就知道函数的定义存在于其他地方,然后继续编译文件。
当我们需要对一个变量声明而不定义的时候,我们会用extern
来修饰该变量。如下所示
extern int var;
这里声明了一个名为var
的整型变量(并没有定义,也就是说没有给var分配内存)。这个声明可以进行多次。但是当我们去掉extern
关键字后:
int var;
在这一行中,声明并定义了一个名为var
的整数类型变量,var被分配内存。
因此当我们声明一个函数时,extern
关键字是隐式的,当我们想声明而不定义变量时,我们需要显式的包含extern
关键字。此外,由于extern
关键字扩展了整个程序的可见性,通过对变量使用extern
关键字,我们可以在程序中任何地方使用该变量,前提是我们包含了该变量在某个地方的声明。
接下来用几个例子来理解extern
关键字:
Example 1:
int var;
int main(void) {
var = 10;
return 0;
}
这个程序是可以成功运行的,因为var
变量是全局变量,并且声明和定义的。
Example 2:
extern int var;
int main(void) {
return 0;
}
这个程序也是可以成功运行的,因为这里虽然只是声明了var
变量,但是var
变量并没有实际使用过,所以不会出现问题。
Example 3:
extern int var;
int main(void) {
var = 10;
return 0;
}
这个程序就会出现问题,会在编译的时候抛出错误。因为var变量只进行了声明,并没有定义(也就是说没有分配内存),程序试图将不存在的变量的值更改为10。
Example 4:
#include "somefile.h"
extern int var;
int main(void) {
var = 10;
return 0;
}
假定somefile.h
包含var
的定义,这个程序是可以成功运行的。
Example 5:
extern int var = 0;
int main(void) {
var = 10;
return 0;
}
如果用extern声明了var变量,并且对该变量进行初始化,那么该变量被认为是已定义的。所以该程序可以顺利编译并运行。
总结
- 声明可以进行任意次,但是定义只能一次
- extern关键字用于扩展变量/函数的可见性
- extern修饰函数是隐式的,函数的声明或定义中默认使用extern修饰
- extern修饰变量时,该变量只被声明,而没有定义
- 当使用extern修饰变量同时进行初始化时,该变量同时也被定义