运行valgrind必须加--leak-check=full和--show-leak-kinds=all;每次验证需用-g -o0重新编译;重点关注definitely lost和invalid read/write;修复未初始化问题需加--track-origins=yes验证源头。

运行 Valgrind 时必须加 --leak-check=full 和 --show-leak-kinds=all
只跑一次 valgrind ./program 是无效的——默认不检查泄漏,也不会报告 still reachable 以外的细节。必须显式启用完整检测:
- --leak-check=full 才会打印每块泄漏的分配栈(否则只写“definitely lost: 40 bytes”)
- --show-leak-kinds=all 才能区分 definitely lost、indirectly lost、possibly lost 和 still reachable
- 漏掉任一参数,都可能让你误判“修好了”,其实只是没显示出来
每次验证都要用 -g -O0 重新编译
改完代码后如果直接复用旧二进制,或者用了 -O2 编译,Valgrind 报告的行号会错位、栈帧会丢失,甚至把 new 优化成栈变量,导致“未初始化”误报消失——不是问题没了,是 Valgrind 看不见了。
- 必须确认编译命令含 g++ -g -O0(或 CMake 中 set(CMAKE_BUILD_TYPE Debug) 并检查 CMAKE_CXX_FLAGS_DEBUG)
- 如果项目有 CI 流程,建议把 Valgrind 检查步骤绑定在 Debug 构建之后,避免手工疏漏
关注 definitely lost 和 Invalid read/write,忽略 still reachable
Valgrind 报告里真正代表 bug 复发的是这两类:
- definitely lost:内存分配后彻底丢失指针,没 delete 也没存到全局容器里
- Invalid read of size 4 或 Invalid write:越界或 use-after-free,程序随时可能崩溃
- still reachable 通常只是 static std::vector 或单例在 main 结束前没清空,OS 会回收,不用修
- 如果修复后报告里仍有 definitely lost,说明释放逻辑没覆盖所有路径(比如异常分支、提前 return)
用 --track-origins=yes 验证未初始化问题是否根除
修复 Conditional jump or move depends on uninitialised value 后,光看错误数归零不够——得确认源头真被初始化了。加这个参数后 Valgrind 会指出“这变量在 foo.cpp:23 声明但没赋值”,而不是只说“它在 bar.cpp:45 被拿去当条件判断”。
- 这个选项会让 Valgrind 慢 2–3 倍,但验证阶段值得开
- 如果修复后仍报同一位置的未初始化,大概率是补丁没打对(比如只初始化了某个分支里的变量)
- 第三方库(如 Qt)偶尔也会触发这类警告,若确认是库内部行为,可用 --suppressions 屏蔽,别硬改
最易被忽略的一点:Valgrind 不检查静态生命周期对象的析构顺序,也不捕获 std::shared_ptr 循环引用。如果问题只在长时间运行后出现,得配合 Massif 看堆增长趋势,不能只信 Memcheck 的退出快照。











