C 语言:作用域、存储期与链接属性
核心概念
- 作用域(Scope):名字在代码中的可见范围。
- 存储期(Storage Duration):对象在程序运行期间存在多久。
- 链接属性(Linkage):不同作用域中的声明是否指向同一实体。
作用域决定“哪里能通过名字访问”,存储期决定“对象存在多久”,链接属性决定“不同声明能否引用同一个实体”。
常见声明对比
| 声明位置与形式 | 作用域 | 存储期 | 链接属性 |
|---|---|---|---|
函数内 int a | 块作用域 | 自动 | 无链接 |
函数内 static int a | 块作用域 | 静态 | 无链接 |
函数外 int a | 文件作用域 | 静态 | 外部链接 |
函数外 static int a | 文件作用域 | 静态 | 内部链接 |
表中讨论的是对象;函数不按上述对象存储期分类。
static 与 extern
- 局部
static:对象整个程序运行期间存在,仅初始化一次,后续调用保留原值;名字仍只在所属块中可见。 - 文件作用域
static:使变量或函数具有内部链接,只能由当前翻译单元内的声明引用。 extern:通常用于声明其他位置定义的对象或函数;不能突破内部链接的限制。
// a.c
static int private_count = 0; // 仅当前翻译单元可链接
int shared_count = 0; // 可供其他翻译单元声明引用
// b.c
extern int shared_count;
两个 .c 文件可以各自定义同名 static 函数,因为它们具有内部链接,不会因此产生跨文件符号冲突。
局部变量地址与生命周期
函数内普通局部变量通常具有自动存储期,离开所属块后生命周期结束。返回它的地址并在之后访问,属于未定义行为;即使原内存中暂时还保留旧值,也不能认为对象依然有效。
局部 static 对象在函数返回后仍存在,可以返回其地址,但多次调用共享的是同一对象,需留意重入及并发问题。
嵌入式开发注意点
static不保证线程安全:共享对象上的count++可能发生读—改—写竞争。volatile约束编译器对对象访问的优化,但不保证原子性、互斥或同步。- RTOS 任务与中断共享数据时,需要结合目标架构的访问原子性与具体场景,选用临界区、原子操作或其他同步机制。
static对象常见于.data、.bss等存储区域,但实际内存布局由实现和链接脚本决定。
速记
作用域看可见范围,存储期看存在时间,链接属性看跨声明引用。
局部 static 保留状态;文件作用域 static 隔离模块;extern 声明共享实体;volatile 不等于线程安全。