Memcheck 能直接报出 use-after-free 错误,通过标记 free 后内存为不可访问并捕获后续读写;必须用 -g 编译才能显示源码行号定位问题,-O0 推荐以避免优化掩盖错误。

Memcheck 能直接报出 use-after-free 错误
Valgrind 的 memcheck 工具在运行时会为每块堆内存维护“可访问性状态”,一旦调用 free,对应内存区域立刻被标记为“不可访问”。后续任何读或写操作都会被立即捕获并报告,不需要额外参数——--leak-check 对这类错误完全无关。
必须用 -g 编译,否则看不到源码行号
没有调试符号,valgrind 只能显示汇编地址或函数名(如 malloc、printf),无法定位到哪一行代码在 free 后又写了指针。实际排查中,90% 的 use-after-free 问题靠行号才能快速确认逻辑错误点。
-
gcc -g -O0 test.c -o test:必须加-g,-O0推荐但非强制(开启优化可能让指针值被复用或提前置空,掩盖问题) - 如果用
g++,同样要加-g;C++ 中delete后使用指针也归此类 - 静态链接(
-static)会导致部分系统调用栈丢失,尽量避免
典型错误输出长这样,重点看 “Invalid read/write” 行
下面是一个真实触发场景的简化输出:
==12345== Invalid write of size 4 ==12345== at 0x40057A: main (test.c:12) ==12345== Address 0x5204040 is 0 bytes inside a block of size 16 free'd ==12345== at 0x4A06AEE: free (vg_replace_malloc.c:530) ==12345== by 0x40056B: main (test.c:9) ==12345== Block was alloc'd at ==12345== at 0x4A06D6E: malloc (vg_replace_malloc.c:299) ==12345== by 0x40055F: main (test.c:6)
关键信息有三行:
-
Invalid write of size 4:明确告诉你这是非法写入(read 则是非法读) -
Address ... is ... free'd:指出该地址已在前序某处被free - 两个
main (test.c:N)行:分别标出free和非法访问发生的源码位置
容易忽略的边界情况:指针未置 NULL + 条件分支绕过检查
很多人以为只要 free(p) 后加一句 p = NULL 就安全了,但实际常见坑是:
- 多个指针指向同一块内存(如
q = p),只置空p,q仍危险 - 释放后进入 if 分支,但判断条件没检查指针有效性(比如只判
if (flag),没判if (p)) - C++ 中虚函数调用、析构函数里访问已
delete的成员指针,valgrind同样能抓到,但堆栈可能跨多层
这些情况不会报“segment fault”,程序可能暂时跑通,但 memcheck 会稳定暴露——前提是别跳过 -g 和仔细看 Invalid read/write 的上下文行号。


















