当前位置:嗨网首页>书籍在线阅读

09-外部链接的静态变量

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

12.1.7 外部链接的静态变量

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别(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 声明完全可以省略,因为外部变量具有文件作用域,所以 ErruptUp 从声明处到文件结尾都可见。它们出现在那里,仅为了说明 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() 可用。外部变量 Hocusmain()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个示例演示了外部变量的作用域是:从声明处到文件结尾。除此之外,还说明了外部变量的生命期。外部变量 HocusPocus 在程序运行中一直存在,因为它们不受限于任何函数,不会在某个函数返回后就消失。

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 声明在这两个函数的上面,如程序清单12.4所示(注意:该例的目的是演示外部变量的工作原理,并非它的典型用法)。

程序清单12.4  global.c 程序

/* global.c  -- 使用外部变量 */
#include <stdio.h>
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