C/C++外部链接的静态变量

外部链接的静态变量具有文件作用域、外部链接和静态存储期。

该类别有时称为外部存储类别(external storage class),属于该类别的变量称为外 部变量(external variable)。把变量的定义性声明(defining declaration)放 在在所有函数的外面便创建了外部变量。
当然,为了指出该函数使用了外部变量,可以在函数中用关键字extern再次声明。如果一个源代码文件使用的 外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变 量。如下所示:

int Errupt;        /* 外部定义的变量 */
double Up[100];      /* 外部定义的数组 */
extern char Coal;     /* 如果Coal被定义在另一个文件, */
/*则必须这样声明*/
void next(void);
int main(void)
{
extern int Errupt;   /* 可选的声明*/
extern double Up[];  /* 可选的声明*/
...

}
void next(void)
{
...
}

注意,在main()中声明Up数组时(这是可选的声明)不用指明数组大小,因为第1次声明已经提供了数组大小信息。main()中的两条 extern 声明完全可以省略,因为外部变量具有文件作用域,所以Errupt和Up从声明处到文件结尾都可见。
它们出现在那里,仅为了说明main()函数要使用这两个变量。
如果省略掉函数中的extern关键字,相当于创建了一个自动变量。去掉 下面声明中的extern:

extern int Errupt;

便成为:

int Errupt;

这使得编译器在 main()中创建了一个名为 Errupt 的自动变量。它是一个独立的局部变量,与原来的外部变量Errupt不同。该局部变量仅main()中可 见,但是外部变量Errupt对于该文件的其他函数(如 next())也可见。简而 言之,在执行块中的语句时,块作用域中的变量将“隐藏”文件作用域中的同名变量。
如果不得已要使用与外部变量同名的局部变量,可以在局部变量的 声明中使用 auto 存储类别说明符明确表达这种意图。
外部变量具有静态存储期。因此,无论程序执行到main()、next()还是其 他函数,数组Up及其值都一直存在。

下面 3 个示例演示了外部和自动变量的一些使用情况。示例 1 中有一个 外部变量 Hocus。该变量对main()和magic()均可见。

/* 示例1 */
int Hocus;
int magic();
int main(void)
{
extern int Hocus; // Hocus 之前已声明为外部变量
...
}
int magic()
{
extern int Hocus; // 与上面的Hocus 是同一个变量
...
}

示例2中有一个外部变量Hocus,对两个函数均可见。这次,在默认情况下对magic()可见。

/*示例2 */
int Hocus;
int magic();
int main(void)
{
extern int Hocus; // Hocus之前已声明为外部变量
...
}
int magic()
{
//并未在该函数中声明Hocus,但是仍可使用该变量
...
}

在示例3中,创建了4个独立的变量。main()中的Hocus变量默认是自动 变量,属于main()私有。magic()中的Hocus变量被显式声明为自动,只有 magic()可用。外部变量Houcus对main()和magic()均不可见,但是对该文件中 未创建局部Hocus变量的其他函数可见。最后,Pocus是外部变量,magic()可 见,但是main()不可见,因为Pocus被声明在main()后面。

/* 示例 3 */
int Hocus;
int magic();
int main(void)
{
int Hocus; // 声明Hocus,默认是自动变量
...
}
int Pocus;
int magic()
{
auto int Hocus; //把局部变量Hocus显式声明为自动变量
...
}

这 3 个示例演示了外部变量的作用域是:从声明处到文件结尾。
除此之外,还说明了外部变量的生命期。外部变量Hocus和Pocus在程序运行中一直存在,因为它们不受限于任何函数,不会在某个函数返回后就消失。

  1. 初始化外部变量
    外部变量和自动变量类似,也可以被显式初始化。与自动变量不同的 是,如果未初始化外部变量,它们会被自动初始化为 0。这一原则也适用于 外部定义的数组元素。与自动变量的情况不同,只能使用常量表达式初始化 文件作用域变量:
    int x = 10;          // 没问题,10是常量
    int y = 3 + 20;       // 没问题,用于初始化的是常量表达式
    size_t z = sizeof(int);   //没问题,用于初始化的是常量表达式
    int x2 = 2 * x;      // 不行,x是变量
    (只要不是变长数组,sizeof表达式可被视为常量表达式。)
  2. 使用外部变量
    下面来看一个使用外部变量的示例。假设有两个函数main()和critic(), 它们都要访问变量units。可以把units声明在这两个函数的上面,如程序所示(注意:该例的目的是演示外部变量的工作原理,并非它的典型用法)。
/* global.c -- 使用外部变量 */
#include 
int units = 0;    /* 外部变量 */
void critic(void);
int main(void)
{
extern int units; /* 可选的重复声明 */
printf("How many pounds to a firkin of butter?\n");
scanf("%d", &units);
while (units != 56)
critic();
printf("You must have looked it up!\n");
return 0;
}
void critic(void)
{
/* 删除了可选的重复声明 */
printf("No luck, my friend. Try again.\n");
scanf("%d", &units);
}
下面是该程序的输出示例:
How many pounds to a firkin of butter?
14
No luck, my friend. Try again.
56
You must have looked it up!

注意,critic()是如何读取 units的第2个值的。当while循环结束时, main()也知道units的新值。所以main()函数和critic()都可以通过标识符units访问相同的变量。用C的术语来描述是, units具有文件作用域、外部链接和静态存储期。
把units定义在所有函数定义外面(即外部),units便是一个外部变量, 对units定义下面的所有函数均可见。因此,critics()可以直接使用units变量。
类似地,main()也可直接访问units。但是,main()中确实有如下声明:

extern int units;

本例中,以上声明主要是为了指出该函数要使用这个外部变量。存储类别说明符extern告诉编译器,该函数中任何使用units的地方都引用同一个定义在函数外部的变量。再次强调,main()和critic()使用的都是外部定义的 units。

3.外部名称
C99和C11标准都要求编译器识别局部标识符的前63个字符和外部标识 符的前31个字符。这修订了以前的标准,即编译器识别局部标识符前31个字 符和外部标识符前6个字符。你所用的编译器可能还执行以前的规则。外部 变量名比局部变量名的规则严格,是因为外部变量名还要遵循局部环境规 则,所受的限制更多。
4.定义和声明
下面进一步介绍定义变量和声明变量的区别。考虑下面的例子:

int tern = 1; /* tern被定义 */
main()
{
extern int tern; /* 使用在别处定义的tern */

这里,tern被声明了两次。第1次声明为变量预留了存储空间,该声明构 成了变量的定义。第2次声明只告诉编译器使用之前已创建的tern变量,所以 这不是定义。第1次声明被称为定义式声明(defining declaration),第2次声 明被称为引用式声明(referencing declaration)。关键字extern表明该声明不是定义,因为它指示编译器去别处查询其定义。
假设这样写:

extern int tern;
int main(void)
{

编译器会假设 tern 实际的定义在该程序的别处,也许在别的文件中。该 声明并不会引起分配存储空间。
因此,不要用关键字extern创建外部定义, 只用它来引用现有的外部定义。
外部变量只能初始化一次,且必须在定义该变量时进行。假设有下面的 代码:

// file_one.c
char permis = 'N';
...
// file_two.c
extern char permis = 'Y'; /* 错误 */

file_two中的声明是错误的,因为file_one.c中的定义式声明已经创建并初始化了permis。

你可能感兴趣的:(计算机学习)