跳到主要内容

存储类别

作用域

定义

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

int test(float b){
int a = 0;
...
return a;
}

这里的a就具有块作用域。因为这里的整个函数体是一个块,函数中的任意复合语句也是一个块。这些定义在函数体内的变量(包括这里的变量b),都具有块作用域。

文件作用域

编译器源代码文件和所有的头文件都被看成是一个包含信息的单独文件,这个文件被叫做翻译单元。描述一个具有文件作用域的变量时,它的实际可见范围是整个翻译单元。如果程序由多个源代码文件组成,那么该程序也将由多个翻译单元组成。每个翻译单元均对应一个源代码文件和它所包含的文件。而这些翻译单元互相之间是属于不同文件作用域的。也就是说,在其中一个翻译单元中声明的具有文件作用域的变量,在另一个翻译单元中不生效。

链接

外部链接

int test1 = 5; 	// 文件作用域,外部链接

该文件和同一程序的其他文件都可以使用变量test1

内部链接

static int test2 = 3;   // 文件作用域,内部链接

变量test2 属于该文件私有,其他文件中函数不能访问。

无链接

具有块作用域、函数作用域或函数原型作用域的变量都是无链接变量。这也意味着这些变量将在定义它们的块、函数或原型中具有私有的属性。

存储类别

![image-20221104223356038](/Users/cat/Library/Application Support/typora-user-images/image-20221104223356038.png)

自动变量

特性

属于自动存储类别的变量具有自动存储期、块作用域且无链接。默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。也可以使用auto关键字进行修饰。

关键字auto是存储类别说明符(storage-class specifier)。auto关键字在C++中的用法完全不同,如果编写C/C++兼容的程序,最好不要使用auto作为存储类别说明符。

块作用域和无链接意味着只有在变量定义所在的块中才能通过变量名访问该变量。也就是说,另一个函数可以使用同名变量,但是该变量是储存在不同内存位置上的另一个变量。

由于该变量是自动存储期,也就是程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量被回收。原来该变量占用的内存位置现在可做他用。

初始化

自动变量不会初始化,除非显式初始化它。也就是

auto int a = 1;  //auto关键字可以省略

寄存器变量

定义

寄存器变量储存在CPU的寄存器中。但是,具体是不是这样,还得看你的编译器。通常情况下,编译器会为寄存器变量分配最快的可用内存。用register关键字来修饰寄存器变量。但是,register更像是一个请求,具体会不会放在寄存器里还得看编译器。编译器可能会为你分配到寄存器,也可能直接忽略这个关键字。

特性

寄存器变量的特性与自动变量的特性一致。他们的区别仅仅是寄存器变量的访问速度会更快。并且,无论寄存器变量有没有被编译器放在寄存器中,都不能对其取内存地址。

块作用域的静态变量

定义

静态的意思是该变量在内存中原地不动,也就是这个变量是静态存储的。

特性

具有块作用域、无链接,但是具有静态存储期。计算机在多次函数调用之间会记录它们的值。在块中(提供块作用域和无链接)以存储类别说明符static(提供静态存储期)声明这种变量。例如:

static int a = 1;

不能在函数形参里面使用static关键字

int test(static int a); //错误

外部链接的静态变量

外部链接的静态变量具有文件作用域、外部链接和静态存储期。该类别有时称为外部存储类别,属于该类别的变量称为外部变量。把变量的声明声明到任何函数体外面就可以创建一个外部链接的静态变量。同时,为了指出该函数使用了外部变量,可以在函数中用关键字extern再次声明。如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须用extern在该文件中声明该变量。如果省略掉extern关键字,相当于创建了一个自动变量。同时,外部变量具有静态存储期。

初始化外部变量

外部变量与自动变量类似,可以使用extern关键字进行显式初始化。但是,如果我们没有初始化外部变量,编译器会自动将变量初始化为0.这一点与自动变量不同。并且,外部变量(文件作用域变量)只能用常量表达式进行初始化。

内部链接的静态变量

该存储类别的变量具有静态存储期、文件作用域和内部链接。在所有函数外部(这点与外部变量相同),用存储类别说明符static定义的变量具有这种存储类别。例如:

static int a = 0;

该变量只能在当前翻译单元可用。在其他的翻译单元中无法使用。

存储类别说明符

C语言有6个关键字作为存储类别说明符:auto、register、static、extern、_Thread_local和typedef

typedef关键字与任何内存存储类型无关,尤其是,在绝大多数情况下,不能在声明中使用多个存储类别说明符,所以这意味着不能使用多个存储类别说明符作为typedef的一部分。唯一例外的是_Thread_local,它可以和staticextern一起使用。

auto

auto表明变量是自动存储期,只能用于块作用域的变量声明中。由于在块中声明的变量本身就具有自动存储期(缺省情况下默认为auto),所以使用auto主要是为了区别同名的外部变量。也就是强调的作用。

register

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

static

static 说明符创建的对象具有静态存储期,也就是载入程序时创建对象,当程序结束时对象消失。具体情况取决于该对象声明的作用域。块作用域的静态变量无链接。文件作用域的静态变量具有内部链接。

extern

extern 说明符表明声明的变量定义在别处。

函数的存储类别

外部函数

默认情况下,声明的函数都是外部函数。可以被其他文件调用,与外部变量类似

静态函数

以static存储类别说明符创建的函数属于特定模块私有。这样做避免了名称冲突的问题,由于静态函数受限于它所在的文件,所以在其他文件中可以使用与之同名的函数。

内联函数

inline

由于函数调用的过程中会有一定的开销,因此,C99引入了一种新的函数类型,内联函数。在C99和C11标准中叙述的是:“把函数变成内联函数建议尽可能快地调用该函数,其具体效果由实现定义”。把函数变成内联函数,编译器可能会用内联代码替换函数调用,并执行一些其他的优化,但是也可能不起作用。

内联函数使用inline 关键字进行修饰。例如:

inline static void delete_all_objects();

内联函数定义与函数调用必须在同一个文件中

_Noreturn

C11标准中,新增了这个关键字,用于强调在函数调用后不会返回主调函数。例如声明函数exit( ),该函数调用后将不再返回主调函数。这个关键字可以让编译器进一步优化掉一些冗余的代码。