生存期
对象的生存期 (Lifetime) 描述“一个对象在何时存在”。它与“存储期” (Storage duration) 密切相关,但二者不是同一个概念。
1. 生存期的开始与结束
在标准层面,一个对象的生存期大体可以概括为:
- 开始:对象获得存储,并完成必要的初始化之后;
- 结束:对象的存储被释放,或对象由于作用域结束而不再存在之后。
当对象的生存期结束后,再通过指向它的指针去访问该对象,通常是未定义行为 (Undefined Behavior, UB)。
2. 四类存储期 (Storage duration)
每个对象都有存储期。C(C11 起)有四类存储期:
- 自动存储期 (Automatic storage duration):块作用域内、未使用
static的对象,通常在进入块时创建、离开块时销毁。 - 静态存储期 (Static storage duration):文件作用域对象、或块作用域内声明为
static的对象,从程序开始到程序结束都存在。 - 线程存储期 (Thread storage duration):以
_Thread_local(C11)或thread_local(C23)声明的对象,在线程开始到线程结束期间存在。 - 分配存储期 (Allocated storage duration):通过动态内存分配获得的存储(
malloc/free等)。
更偏“声明规则”的角度见:9.4.7 存储期 链接。
3. 常见例子
3.1 自动对象:离开作用域即结束
c
int* f(void) {
int x = 1;
return &x; // 返回悬垂指针:离开函数后 x 的生存期结束
}1
2
3
4
2
3
4
3.2 静态对象:整个程序期间存在
c
int* g(void) {
static int x = 1;
return &x; // OK:x 具有静态存储期
}1
2
3
4
2
3
4
3.3 字符串字面量:静态存储期
字符串字面量(例如 "abc")对应的存储具有静态存储期。注意:修改字符串字面量的内容是 UB。
3.4 动态分配:malloc 与 free
c
#include <stdlib.h>
int main(void) {
int* p = malloc(sizeof *p);
if (p == NULL) {
return 1;
}
*p = 42;
free(p);
/* 此后 p 指向的存储已被释放,继续解引用是 UB。 */
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
4. 习题
- 写一个函数返回局部变量地址,并解释为什么这是 UB;再写一个修正版本(例如使用
static或动态分配),并说明两种修正的差异。 - 写一个程序:
- 返回一个指向字符串字面量的指针;
- 尝试修改字符串字面量;
- 解释为什么这会触发 UB。
- 写一个程序:分配一段动态内存,写入数据后
free,再把指针设为NULL。解释“设为NULL”能防止哪一类 bug,防不了哪一类 bug。