12-存储类别说明符
12.1.10 存储类别说明符
读者可能已经注意到了,关键字 static 和 extern 的含义取决于上下文。C语言有6个关键字作为存储类别说明符: auto 、 register 、 static 、 extern 、 _Thread_local 和 typedef 。 typedef 关键字与任何内存存储无关,把它归于此类有一些语法上的原因。尤其是,在绝大多数情况下,不能在声明中使用多个存储类别说明符,所以这意味着不能使用多个存储类别说明符作为 typedef 的一部分。唯一例外的是 _Thread_local ,它可以和 static 或 extern 一起使用。
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() 函数报告 total 和 subtotal 的值,并在报告后重置 subtotal 为 0 。由于 parta.c 调用了 accumulate() 函数,所以必须包含 accumulate() 函数的原型。而 partb.c 只包含了 accumulate() 函数的定义,并未在文件中调用该函数,所以其原型为可选(即省略原型也不影响使用)。该函数使用了外部变量 count 统计 main() 中的 while 循环迭代的次数(顺带一提,对于该程序,没必要使用外部变量把 parta.c 和 partb.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