存储类(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(什么都不写)。