存储期与链接
本节从“声明规则”的角度,系统整理对象的存储期 (Storage duration) 与标识符的链接 (Linkage)。
它们分别回答两个问题:
- 存储期:对象的存储持续多久?(影响生存期)
- 链接:同名标识符在不同作用域/翻译单元中,是否指向同一个实体?
1. 存储期 (Storage duration)
(C11 起)C 有四类存储期:
- 自动存储期:块作用域内、未使用
static的对象,进入块创建,离开块销毁。 - 静态存储期:文件作用域对象,以及块作用域内用
static声明的对象,从程序开始到结束存在。 - 线程存储期:用
_Thread_local(C11)或thread_local(C23)声明的对象,从线程开始到线程结束存在。 - 分配存储期:动态分配获得的存储(
malloc/free)。
2. 链接 (Linkage)
链接决定“同名标识符”是否表示同一个实体。标准区分三类链接:
- 外部链接 (External linkage):可被其他翻译单元引用。
- 内部链接 (Internal linkage):只在当前翻译单元内可见。
- 无链接 (No linkage):只在其作用域内有效(例如函数内的局部变量名)。
重要
若同一翻译单元中,同一标识符同时具有内部链接与外部链接,行为是未定义的。
3. 常见声明组合(速查)
3.1 文件作用域(全局)
c
int g; /* 静态存储期 + 外部链接(也是“暂定定义”之一,见 9.4.8) */
static int s; /* 静态存储期 + 内部链接 */
extern int e; /* 仅声明:链接为外部;是否定义取决于别处 */1
2
3
2
3
3.2 块作用域(函数内)
c
void f(void) {
int a = 0; /* 自动存储期 + 无链接 */
static int b = 0; /* 静态存储期 + 无链接(名字仅在该函数体内可见) */
(void)a;
(void)b;
}1
2
3
4
5
6
2
3
4
5
6
3.3 线程存储期
线程存储期通常与 static/extern 组合使用,决定链接与可见性:
c
thread_local int tls1; /* 线程存储期 + 外部链接(C23) */
static thread_local int tls2; /* 线程存储期 + 内部链接(C23) */1
2
2
4. 习题
- 写出并解释:同一个名字在下面三种位置出现时,它们的链接分别是什么?
- 文件作用域:
int x; - 文件作用域:
static int x; - 函数体内:
static int x;
- 文件作用域:
- 写一个最小示例(分成两个
.c文件):在一个文件里定义static int x;,在另一个文件里尝试extern int x;并使用它。说明会发生什么,为什么。 - 解释:为什么“把
static变量写在头文件”通常是错误的库设计?会造成什么行为?