存储期
存储期(Storage Duration)决定了变量在内存中存在的时间。C 语言有三种存储期:自动存储期、静态存储期和分配存储期。理解存储期与作用域的区别——作用域决定可见性,存储期决定存在时间——是掌握 C 语言内存管理的基础。
自动存储期(Automatic Storage Duration)
自动存储期的变量在程序执行进入其声明所在的块时创建,离开块时销毁。局部变量(未加 static)默认具有自动存储期。
void func(void)
{
int local = 10; /* 自动存储期 */
{
int inner = 20; /* 自动存储期 */
} /* inner 在这里销毁 */
} /* local 在这里销毁 */
自动变量存储在栈上,创建和销毁的开销很小(通常只是调整栈指针)。
未初始化的自动变量:
void func(void)
{
int x;
printf("%d\n", x); /* 未定义行为!x 的值不确定 */
}
自动变量不会自动初始化,使用前必须显式赋值。
静态存储期(Static Storage Duration)
静态存储期的变量在程序启动时创建,程序结束时销毁。全局变量和 static 局部变量具有静态存储期。
int global = 10; /* 静态存储期,文件作用域 */
void func(void)
{
static int count = 0; /* 静态存储期,块作用域 */
count++;
printf("%d\n", count);
}
/* 第一次调用:count = 1 */
/* 第二次调用:count = 2 */
/* count 在函数调用之间保持值 */
static 局部变量只在第一次进入块时初始化一次,后续调用不再初始化。
静态变量自动初始化为零:
void func(void)
{
static int a; /* 自动初始化为 0 */
static int b = 0; /* 显式初始化为 0 */
int c; /* 自动变量,未初始化,值不确定 */
printf("%d %d %d\n", a, b, c);
}
分配存储期(Allocated Storage Duration)
分配存储期的变量通过 malloc、calloc、realloc 动态分配,通过 free 释放。生命周期由程序员控制。
int *p = malloc(sizeof(int) * 10); /* 分配 */
if (p != NULL) {
/* 使用 p */
free(p); /* 释放 */
}
分配存储期的变量没有作用域限制(只要持有指针就可以访问),但生命周期需要手动管理。
存储期对比
| 存储期 | 创建时机 | 销毁时机 | 默认初始化 | 存储位置 |
|---|---|---|---|---|
| 自动 | 进入块 | 离开块 | 不初始化 | 栈 |
| 静态 | 程序启动 | 程序结束 | 0 | 数据段 |
| 分配 | malloc 调用 | free 调用 | 不初始化 | 堆 |
static 关键字的双重含义
static 在不同上下文中有不同含义:
局部变量:改变存储期为静态,不改变作用域
void func(void)
{
static int count = 0; /* 静态存储期,块作用域 */
count++;
}
全局变量/函数:改变链接属性为内部链接,不改变作用域或存储期
static int internal = 0; /* 文件作用域,静态存储期,内部链接 */
static void helper(void) /* 内部链接:只在当前文件可见 */
{
/* ... */
}
常见错误
返回自动变量地址:
int *get_value(void)
{
int local = 10; /* 自动存储期 */
return &local; /* 危险:函数返回后 local 销毁 */
}
/* 正确:返回静态变量地址 */
int *get_value(void)
{
static int local = 10; /* 静态存储期 */
return &local; /* 安全 */
}
假设自动变量自动初始化:
void func(void)
{
int sum; /* 未初始化! */
for (int i = 0; i < 10; i++)
sum += i; /* 未定义行为 */
}
混淆 static 局部变量和全局变量:
static int count = 0; /* 文件作用域 */
void func(void)
{
static int count = 0; /* 块作用域,遮蔽全局 count */
count++; /* 修改的是局部 count */
}
最佳实践
- 自动变量使用前始终初始化
- 需要跨调用保持状态的变量用
static(如计数器、缓存) - 全局变量默认用
static限制为文件作用域 - 动态分配的内存在同一作用域内释放
- 不要返回自动变量的地址
- 理解
static的双重含义(存储期 vs 链接属性)