static
在 C 语言中,static 是一个非常“分裂”的关键字。因为它用在不同的地方,意思是完全不一样的。
简单来说,它的核心作用就两个:“长生不老” 和 “与世隔绝”。
我们需要分两种情况来看:
1. 用在函数内部:Static 局部变量 —— “长生不老”
这是 static 最神奇的用法。
普通局部变量 (没有 static)
- 命运: 就像**“住酒店”**。
- 过程: 函数执行时,变量被创建(入住);函数结束时,变量被销毁(退房,房间被清理)。下次再调用这个函数,变量又是新的,之前的数值全丢了。
- 存储位置: 栈 (Stack)。
静态局部变量 (有 static)
- 命运: 就像**“买房子”**。
- 过程:
- 初始化: 只有第一次运行函数时会初始化一次(装修)。
- 存活: 函数运行结束,变量不销毁! 它依然保留着刚才的值,静静地待在内存里。
- 重逢: 下次再调用这个函数时,它直接使用上次留下的值。
- 存储位置: 静态存储区 (.data 或 .bss 段),而不是栈。
代码对比:
C
void count_normal() {
int a = 0; // 普通变量
a++;
printf("普通: %d\n", a);
}
void count_static() {
static int b = 0; // 静态变量(这句话只在第一次执行时有效)
b++;
printf("静态: %d\n", b);
}
int main() {
// 第一次调用
count_normal(); // 输出 1
count_static(); // 输出 1
// 第二次调用
count_normal(); // 输出 1 (a 被重置为 0,再加 1)
count_static(); // 输出 2 (b 记得上次变成了 1,现在加 1 变成 2)
// 第三次调用
count_normal(); // 输出 1
count_static(); // 输出 3 (b 继续累加)
}应用场景:
当你需要一个函数**“记住”**它上一次被调用时的状态(比如记录按键按下的次数、秒表计时),但又不想用全局变量污染代码时,就用 static 局部变量。
2. 用在函数外部:Static 全局变量/函数 —— “与世隔绝”
这时候,static 的作用就变成了控制可见范围 (Scope)。
普通全局变量 (没有 static)
- ***范围:***整个工程可见。
- 如果在
main.c里定义了int key_state = 0;。 - 那么在
delay.c里,只要写一句extern int key_state;,就可以随意读取和修改这个变量。 - 风险: 容易重名冲突,也容易被别的程序员误改。
静态全局变量 (有 static)
- ***范围:***仅当前文件 (.c) 可见。
- 如果在
main.c里定义了static int key_state = 0;。 - 那么这个变量就是
main.c的私有财产。别的.c文件就算用extern也找不到它,甚至别的.c文件也可以定义一个同名的static int key_state,互不干扰。
Static 函数
同理,如果你在函数前加 static(比如 static void Sensor_Init(void)),那么这个函数只能在当前的 .c 文件里被调用,别的 .c 文件看不见它。
应用场景:
这就是面向对象编程中 “封装” (Encapsulation) 的 C 语言版实现。
- 你在写一个模块(比如
motor.c),里面有一些辅助函数和变量,你只希望模块内部自己用,不想让外部(比如main.c)看到或乱动,就全部加上static。
总结一张表
| 类型 | 位置 | 生命周期 | 作用域 (谁能看见) | 存储位置 |
|---|---|---|---|---|
| 普通局部变量 | 函数内 | 函数结束即死 | 仅函数内 | 栈 (Stack) |
| Static 局部变量 | 函数内 | 程序结束才死 | 仅函数内 (但活着) | 静态区 |
| 普通全局变量 | 函数外 | 程序结束才死 | 所有文件 | 静态区 |
| Static 全局变量 | 函数外 | 程序结束才死 | 仅当前文件 | 静态区 |
一句话口诀:
- 函数里面加
static:为了记性好(记住上一次的值)。 - 函数外面加
static:为了私密性(不让别的文件乱动)。