C

C 知识量:16 - 74 - 317

12.1 存储类别基本概念><

作用域- 12.1.1 -

作用域用于描述程序可访问标识符的区域。一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域。

1、块作用域。

块是用一对花括号括起来的代码区域。定义在块中的变量具有块作用域,其变量的可见范围是从定义处到包含该定义的块的末尾。

此外,函数的形参虽然声明在函数左花括号之前,但它也具有块作用域,属于函数体这个块。

注意:声明在内层块中的变量,其作用域仅局限于该声明所在的块。例如:

void add(int a) {
    for (int i = 1; i <= a; i++) {
        int b = 0;
        b += i;
    }
}

在以上代码中,变量a的块作用域从函数add的左花括号到右花括号。而变量i和变量b的块作用域仅限于for循环体内,一旦程序离开for循环,i和b就不能访问。

2、函数作用域。

函数作用域仅用于goto语句的标签。这意味着即使一个标签首次出现在函数的内层块中,它的作用域也延伸到整个函数。

3、函数原型作用域。

函数原型作用域用于函数原型中的形参名。函数原型作用域的范围是从形参定义处到原型声明结束。因此,编译器在处理函数原型中的形参时,只关心它的类型,而形参名通常不重要,即使没有形参名称也没关系,也不要求与函数定义中的形参同名。例如:

void add(int abc);

以上代码中的形参abc,其作用域范围仅限于该声明,其名称与函数定义中也不一致。

需要注意:在变长数组中,形参名是重要的,[]中的变量名称需要与已定义的一致。

4、文件作用域。

定义在函数外面的变量具有文件作用域。其范围从它定义处到该定义所在文件的末尾。例如:

#include <stdio.h>
int a = 5;
int main(void) {
    for (int i = 1; i <= a; i++) {
        int b = 0;
        b += i;
    }
    system("pause");
    return 0;
}

以上代码中,变量a就具有文件作用域,在整个文件中都有效。这样的变量又称全局变量。

链接- 12.1.2 -

C的变量有3种链接属性,分别是:

  • 外部链接

  • 内部链接

  • 无链接

具有块作用域、函数作用域或函数原型作用域的变量都是无链接变量。这些变量属于定义它们的块、函数或原型私有。

具有文件作用域的变量可以是外部链接,也可以是内部链接。外部链接变量可以在多文件程序中使用,而内部链接变量只能在一个翻译单元中使用。翻译单元即一个源代码文件和它所包含的头文件。

可以通过查看定义中是否使用了存储类别说明符static来辨别文件作用域变量是内部链接还是外部链接,例如:

#include <stdio.h>

int a = 100;         //文件作用域,外部链接
static int b = 200;  //文件作用域,内部链接

int main(void) {
    ...
}

以上代码中,变量a可以被该文件和同一程序的其他文件使用,而变量b属于该文件私有,该文件中的任意函数都可以使用它。

存储期- 12.1.3 -

作用域和链接描述了标识符的可见性。存储期用于描述通过这些标识符访问的对象的生存期。

C语言有4种存储期:

  • 静态存储期

  • 线程存储期

  • 自动存储期

  • 动态分配存储期

1、静态存储期。

如果对象具有静态存储期,那么它在程序执行期间一直存在。文件作用域变量具有静态存储期。

2、线程存储期。

线程存储期用于并发程序设计,具有线程存储期的对象,从被声明到线程结束一直存在。

3、自动存储期。

块作用域的变量通常具有自动存储期。当程序进入定义这些变量的块时,为这些变量分配内存;当程序退出这个块时,释放这些变量占用的内存。

对于变长数组来说有些不同,它的存储期从声明处到块的末尾,而不是从块的开始处到块的末尾。

块作用域的变量也可以具有静态存储期,创建这样的变量需要在声明前加上关键字static。

主要的存储类别如下表:

存储类别 存储期 作用域 链接 声明方式
自动 自动 块内
寄存器 自动 块内,使用关键字register
静态外部链接 静态 文件 外部 所有函数外
静态内部链接 静态 文件 内部 所有函数外,使用关键字static
静态无链接 静态 块内,使用关键字static