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

12-存储类别说明符

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

12.1.10 存储类别说明符

读者可能已经注意到了,关键字 staticextern 的含义取决于上下文。C语言有6个关键字作为存储类别说明符: autoregisterstaticextern_Thread_localtypedeftypedef 关键字与任何内存存储无关,把它归于此类有一些语法上的原因。尤其是,在绝大多数情况下,不能在声明中使用多个存储类别说明符,所以这意味着不能使用多个存储类别说明符作为 typedef 的一部分。唯一例外的是 _Thread_local ,它可以和 staticextern 一起使用。

auto 说明符表明变量是自动存储期,只能用于块作用域的变量声明中。由于在块中声明的变量本身就具有自动存储期,所以使用 auto 主要是为了明确表达要使用与外部变量同名的局部变量的意图。

register 说明符也只用于块作用域的变量,它把变量归为寄存器存储类别,请求最快速度访问该变量。同时,还保护了该变量的地址不被获取。

static 说明符创建的对象具有静态存储期,载入程序时创建对象,当程序结束时对象消失。如果 static 用于文件作用域声明,作用域受限于该文件。如果 static 用于块作用域声明,作用域则受限于该块。因此,只要程序在运行对象就存在并保留其值,但是只有在执行块内的代码时,才能通过标识符访问。块作用域的静态变量无链接。文件作用域的静态变量具有内部链接。

extern 说明符表明声明的变量定义在别处。如果包含 extern 的声明具有文件作用域,则引用的变量必须具有外部链接。如果包含 extern 的声明具有块作用域,则引用的变量可能具有外部链接或内部链接,这接取决于该变量的定义式声明。

小结:存储类别

自动变量具有块作用域、无链接、自动存储期。它们是局部变量,属于其定义所在块(通常指函数)私有。寄存器变量的属性和自动变量相同,但是编译器会使用更快的内存或寄存器存储它们。不能获取寄存器变量的地址。

具有静态存储期的变量可以具有外部链接、内部链接或无链接。在同一个文件所有函数的外部声明的变量是外部变量,具有文件作用域、外部链接和静态存储期。如果在这种声明前面加上关键字 static ,那么其声明的变量具有文件作用域、内部链接和静态存储期。如果在函数中用 static 声明一个变量,则该变量具有块作用域、无链接、静态存储期。

具有自动存储期的变量,程序在进入该变量的声明所在块时才为其分配内存,在退出该块时释放之前分配的内存。如果未初始化,自动变量中是垃圾值。程序在编译时为具有静态存储期的变量分配内存,并在程序的运行过程中一直保留这块内存。如果未初始化,这样的变量会被设置为 0

具有块作用域的变量是局部的,属于包含该声明的块私有。具有文件作用域的变量对文件(或翻译单元)中位于其声明后面的所有函数可见。具有外部链接的文件作用域变量,可用于该程序的其他翻译单元。具有内部链接的文件作用域变量,只能用于其声明所在的文件内。

下面用一个简短的程序使用了5种存储类别。该程序包含两个文件(程序清单12.5和程序清单12.6),所以必须使用多文件编译(参见第9章或参看编译器的指导手册)。该示例仅为了让读者熟悉5种存储类别的用法,并不是提供设计模型,好的设计可以不需要使用文件作用域变量。

程序清单12.5  parta.c 程序

// parta.c --- 不同的存储类别
// 与 partb.c 一起编译
#include <stdio.h>
void report_count();
void accumulate(int k);
int count = 0;        // 文件作用域,外部链接
int main(void)
{
     int value;        // 自动变量
     register int i;   // 寄存器变量
     printf("Enter a positive integer (0 to quit): ");
     while (scanf("%d", &value) == 1 && value > 0)
     {
          ++count;      // 使用文件作用域变量
          for (i = value; i >= 0; i--)
               accumulate(i);
          printf("Enter a positive integer (0 to quit): ");
     }
     report_count();
     return 0;
}
void report_count()
{
     printf("Loop executed %d times\n", count);
}

程序清单12.6  partb.c 程序

// partb.c -- 程序的其余部分
// 与 parta.c 一起编译
#include <stdio.h>
extern int count;          // 引用式声明,外部链接
static int total = 0;      // 静态定义,内部链接
void accumulate(int k);    // 函数原型
void accumulate(int k)     // k 具有块作用域,无链接
{
     static int subtotal = 0;    // 静态,无链接
     if (k <= 0)
     {
          printf("loop cycle: %d\n", count);
          printf("subtotal: %d; total: %d\n", subtotal, total);
          subtotal = 0;
     }
     else
     {
          subtotal += k;
          total += k;
     }
}

在该程序中,块作用域的静态变量 subtotal 统计每次 while 循环传入 accumulate() 函数的总数,具有文件作用域、内部链接的变量 total 统计所有传入 accumulate() 函数的总数。当传入负值时, accumulate() 函数报告 totalsubtotal 的值,并在报告后重置 subtotal0 。由于 parta.c 调用了 accumulate() 函数,所以必须包含 accumulate() 函数的原型。而 partb.c 只包含了 accumulate() 函数的定义,并未在文件中调用该函数,所以其原型为可选(即省略原型也不影响使用)。该函数使用了外部变量 count 统计 main() 中的 while 循环迭代的次数(顺带一提,对于该程序,没必要使用外部变量把 parta.cpartb.c 的代码弄得这么复杂)。在 parta.c 中, main()report_count() 共享 count

下面是程序的运行示例:

Enter a positive integer (0 to quit): 5
loop cycle: 1
subtotal: 15; total: 15
Enter a positive integer (0 to quit): 10
loop cycle: 2
subtotal: 55; total: 70
Enter a positive integer (0 to quit): 2
loop cycle: 3
subtotal: 3; total: 73
Enter a positive integer (0 to quit): 0
Loop executed 3 times