realloc失败时原指针被覆盖必然导致内存泄漏;必须用临时指针接收返回值,检查是否为NULL后再更新原指针,否则原内存地址丢失且Valgrind会报告definitely lost。

realloc失败时原指针被覆盖导致泄漏
直接用 realloc 赋值给原指针是高危操作:一旦分配失败,realloc 返回 NULL,原指针丢失,内存彻底泄露。这不是“可能漏”,而是“必然漏”——且 Valgrind 会把它标为 definitely lost。
- 错误写法:
p = realloc(p, new_size);—— 失败时p变成NULL,旧地址永远找不回 - 正确做法:必须用临时指针接收返回值,检查是否为
NULL再决定是否更新原指针 - Valgrind 在这种场景下不会报“realloc 错误”,而是安静地把那块没被 free 的内存记为泄露,直到程序退出才在 summary 里甩出
definitely lost
Valgrind 能否检测 realloc 后的指针丢失?
不能直接标记“realloc 使用不当”,但能精准暴露后果:只要原内存块没被后续 free,且无其他指针指向它,Memcheck 就会在退出时报告这块内存为 definitely lost,并给出分配时的调用栈(含 realloc 行号)。
- 关键点:Valgrind 不分析你“怎么写 realloc”,只跟踪“哪块内存最终没被释放”
- 它会显示类似这样的堆栈:
Address 0x12345678 is 0 bytes inside a block of size 1024 alloc'd→ 下一行就是realloc调用位置 - 如果你看到
definitely lost对应的分配函数是realloc,基本可以锁定问题就出在那次调用没做空指针检查
实战修复:带检查的 realloc 模板
别靠记忆,把模式固化下来。以下是最小安全模板,适用于所有 C 场景:
void *tmp = realloc(p, new_size);
if (tmp == NULL) {
// 分配失败:p 仍有效,可继续用或显式 free
fprintf(stderr, "realloc failed\n");
// 此处可选择 abort / return error / fallback 等
goto cleanup; // 或其他错误处理路径
}
p = tmp; // 仅在此处赋值
- 临时变量
tmp是必须的,避免覆盖原指针 - 不要用
!tmp判断,NULL是唯一合法失败信号 - 即使
new_size == 0,realloc也可能返回NULL(行为依赖实现),仍需检查 - 如果原指针
p是NULL,realloc(NULL, size)等价于malloc(size),该模板同样适用
为什么加 -O0 编译再跑 Valgrind?
因为编译器优化可能把看似冗余的指针赋值、条件跳转全删掉,导致 Valgrind 看不到你“本该检查却没检查”的逻辑,甚至让泄露看起来像发生在别处。
- 不加
-O0时,gcc -O2可能把未检查的realloc直接内联或重排,掩盖真实控制流 - Valgrind 需要原始、未扭曲的指令流来准确映射源码行号;
-O0保证它报告的realloc行号就是你写的那一行 - 顺手加上
-g,否则日志里只有地址,没有文件名和行号
最常被忽略的是:realloc 失败后不做任何处理,还继续用原指针读写——这已不是泄漏,而是未定义行为。Valgrind 的 definitely lost 只是冰山一角,底下可能压着越界访问或崩溃。盯住那个分配栈,从那里开始逆向查检查逻辑。


















