外部变量

科技工作者之家  |   2020-11-17 18:10

外部变量是在函数外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的结尾。在此作用域内,全局变量可为各个函数所引用。编译时将外部变量分配在静态存储区。

定义有时需要在其他文件中使用extern来声明全局变量,以扩展全局变量的作用域。 也可用static声明全局变量,使该变量不能被其他文件引用。

在B语言、C语言和一些其它派生的语言(如C++)中,外部变量外部的变量。这并不是语言规范中直接明确的概念,因此含义可能有歧义。严格地,“外部”可以指变量名具有的外部链接(external linkage),据此外部变量指变量名具有外部链接的变量。其它理解导致外延与之具有一定差异,在下面的例子中注释。

注意“变量”的概念在这些语言中本身即具有一些差异。ISO C中没有作为名词的“变量”(variable)这一术语的正式定义,通常即指对象,而ISO C++规定变量通过对象或不是非静态数据成员的引用的声明引入,其中“对象”的概念和ISO C的基本兼容。1

用extern声明extern可省略不写。

本文件里:在一个文件里,有不止一个函数,外部变量在第一个函数后面定义。若用extern在第一个函数前声明该变量则该变量可以在第一个函数中使用。

多个文件中:在其他文件中若想要使用该文件中已声明的全局变量,则在其他文件头部声明该变量,即可使用该全局变量。1

file1.c file2.cint i,j; //定义全局变量 extern int i,j; //外部变量说明, extern可以省略char c; extern char c;void func() void func1(){ {i = 100; printf(" i = %d, j = %d ", i, j);j = 10;} }例子下面的文件可以作为C或C++(源代码确保符合ISO C11和ISO C++11)的源文件,分别作为一个翻译单元,翻译后链接为一个程序。

文件1:

int GlobalVariable; // 同一个翻译单元中,在文件作用域(C)或全局命名空间作用域(C++)的首次声明的变量名隐式具有外部链接 void SomeFunction(void); // 不是函数定义的函数声明,因为明确指定了具体类型,在C语言中是函数原型 int main() { GlobalVariable = 1; SomeFunction(); }文件2:

extern int GlobalVariable; // 通过关键字extern显式指定被声明的变量名具有外部链接 // static int GlobalVariable; // 若使用static关键字,可显式指定被声明的变量名具有内部链接,这样就不会指称翻译单元中声明的具有外部链接的对象,即使变量名相同 void SomeFunction(void) { // 函数定义 // int GlobalVariable; // 若在这里声明变量,则隐藏块作用域外部的变量名,这个变量没有链接,和块作用域外部的变量名不同 // extern int GlobalVariable; // 若在这里以extern声明同名变量,则重复声明之前的变量,链接取决于之前的声明,可能是或者不是外部链接 ++GlobalVariable; }在每个翻译单元中,标识符都必须先被声明。这个例子里,变量 GlobalVariable 在文件1中被声明,这个声明同时是定义。为了在文件2中使同一个标识符指称相同实体,它必须被声明具有外部链接。

ISO C要求函数或对象的标识符若被使用则有且仅有一个外部定义,或未被使用时可以没有定义或具有一个定义,否则行为未定义。通常除非使用扩展(例如弱符号),实现(链接器)一般会有检查。

ISO C++规定合式(well-formed)必须在符合语法规则、可诊断语义规则的同时遵守One Definition Rule,其中多个翻译单元内的同一个实体必须具有唯一定义,这包括具有外部链接的变量。不管有多少个翻译单元,定义在整个程序中有且仅有一个。对于跨翻译单元的情形,如果违反此规定,通常会产生链接错误。

除了外部链接,“外部”可能被理解为“在块作用域外部”,见例子中SomeFunction函数定义中的注释。这里,“外部”变量不一定具有外部链接(比如,首次声明时显式使用了static关键字指定变量名具有内部链接,或者(C++)声明在具有内部链接的无名命名空间(unnamed namespace)内部)。由于这些微妙的歧义,通常不使用这种理解。

由于C语言中外部链接的对象被习惯性误称为全局变量,“外部”还可能会被误解为“全局”——严格地,这种理解的“外部”着眼于特定的源文件,指非当前翻译单元。但是事实上C语言本身并没有严格约定“全局”的含义。和C语言不同的是,C++存在严格意义上的全局变量,此处“全局”被正式定义为全局命名空间作用域。但是,这个概念的外延实际上和C++的“外部变量”不同:非全局命名作用域的变量的名称可以具有外部链接,这不是全局变量;而全局命名空间作用域中也可以直接使用static声明具有内部链接的全局变量。

此外,ISO C规定没有初值符且没有存储类指示符(storage specifier)或static指定的对象声明是tentative definition(ISO C++则明确禁止),和重复声明一起可能显著加剧上述理解的混乱。以下例子引用自ISO C11 6.9.2:

int i1 = 1; // definition, external linkagestatic int i2 = 2; // definition, internal linkageextern int i3 = 3; // definition, external linkageint i4; // tentative definition, external linkagestatic int i5; // tentative definition, internal linkageint i1; // valid tentative definition, refers to previousint i2; // 6.2.2 renders undefined, linkage disagreementint i3; // valid tentative definition, refers to previousint i4; // valid tentative definition, refers to previousint i5; // 6.2.2 renders undefined, linkage disagreementextern int i1; // refers to previous, whose linkage is externalextern int i2; // refers to previous, whose linkage is internalextern int i3; // refers to previous, whose linkage is externalextern int i4; // refers to previous, whose linkage is externalextern int i5; // refers to previous, whose linkage is internal若外延限制为以上理解外延的交集,即文件作用域(C)/全局命名空间作用域(C++)中首次使用extern或没有存储类指示符的声明引入的变量,则没有歧义。绝大多数情况下,“外部变量”都属于这个范畴。

用static声明外部变量static声明后该外部变量就只能在本文件中使用。

2

本词条内容贡献者为:

李岳阳 - 副教授 - 江南大学