C

C 知识量:16 - 74 - 317

12.2 存储类别分类><

自动变量- 12.2.1 -

属于自动存储类别的变量具有自动存储期、块作用域且无链接。通常,声明在块或函数头中的任何变量都属于自动存储类别。可以显式的使用关键字auto来表明一个变量属于自动存储类别,例如:

auto int a = 5;

关键字auto是存储类别说明符。由于auto在C与C++中用法存在差异,如果要编写C/C++兼容的程序,就不应使用auto关键字。

只有在自动变量定义所在的块中才能通过变量名访问该变量,程序在进入该变量声明所在的块时变量存在,而在程序退出该块时变量消失,其原占用的内存可作他用。

在嵌套块中声明的变量仅限于该块及其包含的块使用,示例如下:

#include <stdio.h>

int main(void) {
    int a = 5;
    int b = 0;
    int i = 0;
    for (int i = 1; i <= 5; i++) {
        if (i == 2) {
            int b = 100;
            printf("part1:\na=%d\nb=%d\ni=%d\n", a, b, i);
        }
        if (i == 5)
            printf("part2:\na=%d\nb=%d\ni=%d\n", a, b, i);
        a++;
    }
    printf("part3:\na=%d\nb=%d\ni=%d\n", a, b, i);
    system("pause");
    return 0;
}

运行结果为:

part1:
a=6
b=100
i=2
part2:
a=9
b=0
i=5
part3:
a=10
b=0
i=0

以上代码中:

  • for循环外的变量a、b、i是文件作用域变量即全局变量,在整个文件中都有效。

  • for循环内的变量i和b与for循环外的变量i和b是不同的变量,for循环内的变量i和b是自动变量。

  • for循环内的变量会暂时覆盖for循环外的同名变量,也就是说块内的变量会暂时覆盖块外的同名变量。

  • 在for循环内,变量i在整个for循环体内有效,而变量b只在if语句块内有效。

  • if块没有花括号,但是紧跟if之后的语句仍被视为if块的组成部分。

需要注意的是:自动变量不会初始化,除非显式初始化它。也就是说如果不初始化一个自动变量,不用指望它会是0。

寄存器变量- 12.2.2 -

寄存器变量存储在CPU寄存器中,与普通变量相比,访问和处理寄存器变量速度更快。

因为寄存器变量存储在寄存器而非内存中,所以无法获取寄存器变量的地址。除此之外,寄存器变量与自动变量都一样,也就是说寄存器变量是块作用域、无链接和自动存储期。

使用存储类别说明符register便可以声明寄存器变量,例如:

register int x;

需要注意的是,即使像上面那样声明了寄存器变量,该变量也不一定会存储到寄存器中,这个声明更像是请求,编译器会根据寄存器数量等情况来决定是否满足请求。即使不能真的存储到寄存器中,也不能对该变量使用地址运算符。

块作用域的静态变量- 12.2.3 -

静态变量中静态的意思是指该变量在内存中原地不动,并非值不变。

具有文件作用域的变量具有静态存储期,此外还可以创建具有静态存储期、块作用域的局部变量,即块作用域的静态变量。

块作用域的静态变量与自动变量一样,具有相同的作用域,但是程序离开它们所在的函数后,这些变量依然存在。以下是一个示例:

#include <stdio.h>
void box(void);

int main(void) {
    int i;
    for (int i = 1; i <= 5; i++) {
        printf("No.%d is :\n", i);
        box();
    }
    system("pause");
    return 0;
}

void box(void) {
    int a = 1;
    static b = 1; //创建块作用域的静态变量
    printf("a = %d and b = %d\n", a, b++);
}

运行结果为:

No.1 is :
a = 1 and b = 1
No.2 is :
a = 1 and b = 2
No.3 is :
a = 1 and b = 3
No.4 is :
a = 1 and b = 4
No.5 is :
a = 1 and b = 5

在函数box()中,变量a是自动变量,而变量b是具有块作用域的静态变量,从运行结果可以看出,即使在程序每次循环退出了函数box,仍然保留了变量b的值,下次调用时,在上次结果的基础上再加1。

注意:不能在函数的形参中使用static。

外部链接的静态变量- 12.2.4 -

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别的变量也称为外部变量。

把变量的定义性声明放在所有函数(包括main()函数)外面便创建了外部变量。可以在函数内部使用关键字extern再次声明已定义的外部变量,这样做通常用于表示将会在该函数内使用该外部变量。下面是使用外部变量的示例:

#include <stdio.h>
void box(void);

int a;          //外部定义的变量
double b[10];   //外部定义的数组
extern float c; //如果c被定义在另一个文件中,则必须这样声明。

int main(void) {
    extern int a;      //可选
    extern double b[]; //可选,已经声明数组大小,不必再次填写。
    ...
}

void box(void) {
    ...
}

在函数main()中不一定要再次声明外部变量,但是如果要声明,则必须使用extern关键字,否则会另外创建自动变量。

外部变量只能初始化一次,且必须在定义该变量时进行。如果没有初始化,它们会自动初始化为0,这与自动变量完全不同。

外部变量在所有文件和函数中都可见,可以在任何地方直接访问。

内部链接的静态变量- 12.2.5 -

内部链接的静态变量具有静态存储期、文件作用域和内部链接。使用时,在所有函数外部,用存储类别说明符static来定义,例如:

#include <stdio.h>
void box(void);

int a;             //外部链接的静态变量
static double b;   //内部链接的静态变量

int main(void) {
    extern int a;      //可选
    extern double b;   //可选
    ...
}

void box(void) {
    ...
}

以上代码中,变量b通过static关键字来定义为内部链接的静态变量。

普通外部变量可以用于同一程序中的任意文件中的函数,而内部链接的静态变量只用用于同一个文件中的函数。

多文件- 12.2.6 -

当程序由多个翻译单元组成时,就会体现出内部链接与外部链接的区别。复杂的程序通常由多个单独的源代码文件组成,需要共享一个外部变量时,C通过在一个文件中进行定义式声明,然后在其他文件中进行引用式声明来实现共享。

需要注意的是,如果外部变量定义在一个文件中,其他文件在使用该变量之前,必须先用extern关键字声明它,在此声明之前,是不能直接使用该外部变量的。

存储类别说明符- 12.2.7 -

C语言有6个关键字作为存储类别说明符,分别是:auto、register、static、extern、_Thread_local和typedef。在绝大多数情况下,不能在声明中使用多个存储类别说明符,唯一的例外是_Thread_local,它可以和static或extern一起使用。

  • auto说明符。 表明变量是自动存储期,只能用于块作用域的变量声明。

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

  • static说明符。 其创建的对象具有静态存储期,在载入程序时创建对象,当程序结束时对象消失。如果用于文件作用域声明,作用域受限于该文件;如果用于块作用域声明,作用域受限于该块。

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

存储类别与函数- 12.2.8 -

函数也有存储类别,可以是外部函数(默认),也可以是静态函数或内联函数。外部函数可以被其他文件的函数访问,而静态函数只能用于其定义所在的文件,例如:

void box(void);
static int count(int, int);

以上代码中,box()是外部函数,count()是静态函数,其他文件中的函数可以调用box()函数,但是不能调用count()函数。

存储类别的选择- 12.2.9 -

保护性程序设计的黄金法则是:“按需知道”原则。尽量在函数内部解决该函数的任务,只共享那些需要共享的变量。因此,自动存储类别是最常用的,使用其他类别前需要考虑好是否有必要这样做。