存储类(storage class)决定一个变量的四点:存在哪、活多久、能看到多大范围、默认初值是什么。C 语言有四类:auto、static、register、extern。前面文章里出现的 static 就是其中之一。
一张表看懂四种存储类
| 存储类 | 作用域 | 默认初值 | 存哪 | 生命周期 |
|---|---|---|---|---|
auto | 局部 | 垃圾值 | 内存(RAM) | 到作用域结束 |
static | 局部 | 0 | 内存(RAM) | 到程序结束,跨调用保留 |
register | 局部 | 垃圾值 | CPU 寄存器(可能) | 到作用域结束 |
extern | 全局 | 0 | 内存(RAM) | 到程序结束 |
auto:默认的局部变量
函数/代码块里声明的局部变量默认就是 auto,所以这个关键字几乎没人写:
int main() {
auto int x = 10; /* 这里的 auto 可写可不写 */
printf("%d", x); // 10
return 0;
}它的特点:只在声明的块里可见,默认值是垃圾值,一出作用域就消失。函数不用 auto 修饰(函数本来就不限作用域)。
static:跨调用记住值的局部变量
static 局部变量保留上次的值,不会被重新初始化。经典计数器:
void counter() {
static int count = 0; /* 只初始化一次 */
count++;
printf("Count = %d\n", count);
}
counter(); // Count = 1
counter(); // Count = 2普通局部变量每次进函数都从 0 开始重来,static 变量出了作用域还活着。上一章 static 篇已详细展开,这里把它放进存储类的大框架里看。
register:请求更快的访问
register 请求编译器把变量塞进 CPU 寄存器,访问速度比内存快得多:
register int i;
for (i = 0; i < 5; i++) {
printf("%d ", i);
}注意请求二字:寄存器数量有限,编译器看心情采纳。取不到地址的变量不能加 &,也是 register 的老规矩(现代编译器自己也会优化,这个关键字越来越边缘化,理解就好)。
extern:说这个变量在别处定义
多文件项目里,A 文件定义的全局变量,B 文件想用,就在 B 里用 extern 声明它存在别处:
/* printVar.c */
int globalVar = 100; /* 真正的定义在这 */
/* main.c */
#include <stdio.h>
extern int globalVar; /* 声明:它定义在别的文件 */
int main() {
printf("全局变量是 %d\n", globalVar); // 100
return 0;
}编译时两个文件一起编:
gcc main.c printVar.c -o main别把 extern 当定义,它只是告知,不分配新内存。全局变量默认初值 0,生命周期到程序结束。
一个朴素的分辨办法
问自己两句话:
- 要不要在多个调用之间记住值?→ 用 static(局部)。
- 变量定义在别的文件、这里只想用?→ 用 extern。
- 默认默认,跟着块走、用完就扔?→ auto(什么都不写)。