C 语言动态内存管理
更新时间:2026-08-25。本文是
languages/c/主题高手层第 2 篇。动态内存是 C 程序最容易出 bug 的地方——泄漏、悬垂、越界、双重释放,四类错误占据了 C 崩溃问题的绝大部分。本文把它们讲透,并给出工具的定位方法。
本文要回答的问题
malloc/calloc/realloc三者有什么区别?- 内存泄漏、悬垂指针、越界、双重释放,分别是怎么发生的?
- 用什么工具能自动定位这些内存错误?
一、三个分配函数
| 函数 | 作用 | 是否清零 |
|---|---|---|
malloc(n) | 分配 n 字节 | 否(内容是垃圾值) |
calloc(cnt, size) | 分配 cnt×size 字节 | 是(清零) |
realloc(p, n) | 调整 p 的大小为 n | 保留原内容 |
c
int *a = malloc(10 * sizeof(int)); // 分配但未初始化
int *b = calloc(10, sizeof(int)); // 分配并清零,b[0]~b[9] 都是 0
a = realloc(a, 20 * sizeof(int)); // 扩到 20 个 intrealloc 的坑:realloc 可能返回新地址(原地址被释放)。正确写法:
c
int *tmp = realloc(a, new_size);
if (tmp == NULL) { /* 处理失败,a 仍有效 */ }
a = tmp; // 成功才更新 a二、进程内存布局回顾
动态内存来自堆(heap),与栈、数据段、代码段并列:

用 cat /proc/<pid>/maps 可看真实布局(见 proc 内存观测)。
三、四大内存陷阱
3.1 内存泄漏(leak)
c
void leak(void) {
int *p = malloc(100); // 分配后没人 free,函数返回后地址丢失
}- 现象:进程 RSS 不断上涨,长期运行最终 OOM。
- 定位:
valgrind --leak-check=full ./prog,或 ASan(-fsanitize=address)。
3.2 悬垂指针(dangling)
c
int *p = malloc(sizeof(int));
free(p);
*p = 42; // 悬垂!释放后继续使用(use-after-free)- 释放后指针仍保存原地址,再解引用是未定义行为。
- 与泄漏相反:泄漏是"忘了释放",悬垂是"释放后还用"。
3.3 越界访问(out-of-bounds)
c
int a[4];
a[4] = 5; // 越界写
for (int i = 0; i <= 4; i++) a[i] = i; // <= 是经典 off-by-one- 越界写不一定会立刻崩溃,会悄悄破坏相邻内存——最难查。
- 栈越界用 ASan 直接抓;堆越界 Valgrind 也能报。
3.4 双重释放(double free)
c
int *p = malloc(100);
free(p);
free(p); // 双重释放!堆损坏- 两次 free 同一指针会破坏堆结构,可能崩溃或安全漏洞。
- 预防:
free后立即置 NULL(free(p); p = NULL;),free(NULL)是安全的。
四、四大陷阱对照表
| 陷阱 | 示例 | 后果 | 预防 |
|---|---|---|---|
| 内存泄漏 | malloc 不 free | RSS 上涨/OOM | 配对 free、Valgrind |
| 悬垂指针 | free 后使用 | 未定义行为/崩溃 | 置 NULL、ASan |
| 越界写 | a[4]=5 | 破坏相邻内存 | ASan、Valgrind |
| 双重释放 | free 两次 | 堆损坏 | free 后置 NULL |
五、内存检测工具
5.1 ASan(AddressSanitizer,推荐)
编译期插桩,运行时自动检测越界、悬垂、双重释放:
bash
gcc -fsanitize=address -g -O0 program.c -o program
./program # 出错时打印精确的越界位置和调用栈5.2 Valgrind
无需重新编译,直接跑在程序外:
bash
valgrind --leak-check=full ./program两者完整用法见 crash 排查线:ASan/TSan/Valgrind。
六、与本站主线衔接
- 堆分配与性能:频繁 malloc 会成为分配热点,
perf能剖析到malloc调用栈,见 perf 使用指南。 - 缓存与 TLB:堆内存访问的局部性影响缓存命中,见 L3 内存子系统。
- 崩溃排查:四类陷阱的完整排查流程,见 crash 排查线。
一句话总结
动态内存 = 记住 malloc/calloc/realloc 三兄弟 + 四大陷阱(泄漏/悬垂/越界/双重释放),每个陷阱都有工具可查(ASan/Valgrind);掌握它,C 程序 90% 的内存崩溃都有了解法。
上一篇:指针深入 下一篇:文件 IO 与模块化