valgrind 能检测 c++ 中因指针误用导致的 invalid read/write、use of uninitialised value、invalid free 等内存错误,但不报空指针解引用本身,仅在解引用非法地址时触发;需 -g -o0 编译并启用 --leak-check=full 等参数。

valgrind 能查哪些 C++ 指针错误
valgrind 本身不直接“检查指针”,它通过内存访问监控发现指针误用引发的底层行为:比如读写已释放内存、越界访问、未初始化指针解引用(间接导致非法地址访问)。它对 malloc/free 和 new/delete 都有效,但对栈上变量或纯指针赋值(如 p = nullptr;)无反应——只有当指针被解引用(*p 或 p->x)且指向非法内存时才会报错。
常见对应错误类型包括:Invalid read、Invalid write、Use of uninitialised value、Invalid free。注意:它不会报“空指针解引用”本身,而是报 Address 0x0 is not stack'd, malloc'd or (recently) free'd 这类地址不可访问提示。
编译时必须加 -g 并禁用优化
没有调试信息,valgrind 只能显示汇编行号;开启 -O2 后,内联、寄存器重用、死代码消除会让报告和源码严重脱节,甚至漏报。
- 正确编译命令:
g++ -g -O0 -o myapp main.cpp - 若用 CMake,确保
CMAKE_BUILD_TYPE=Debug,并检查生成的compile_commands.json中确实含-g且无-O2或-O3 - 链接了第三方静态库?确认它们也带调试符号,否则 valgrind 在库内崩溃时只显示
???
运行 valgrind 时要用 --tool=memcheck + 关键参数
memcheck 是默认工具,但默认参数对 C++ 不够友好。必须显式启用两项:
-
--leak-check=full:否则只报明显泄漏,漏掉循环引用或 long-lived 对象 -
--show-leak-kinds=all:把possible leak和still reachable也列出来(后者常是全局对象或 static 成员,需人工判断) - 可选但实用:
--track-origins=yes:定位未初始化值源头(代价是慢 2–3 倍)
完整常用命令:valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all --track-origins=yes ./myapp
识别报告里真正要修的问题
valgrind 报告常混着噪音。重点盯三类输出:
- Definitely lost:new 出来没 delete,且指针已丢失(比如局部指针离开作用域),必须修复
-
Invalid read/write at address:后面跟的地址不是
0x0就是明显偏移(如0x... +4096),结合堆栈回溯看哪行解引用了野指针 -
Conditional jump or move depends on uninitialised value:说明某个 bool 判断或分支用了未初始化内存,根源常在
malloc后没memset,或struct成员未初始化
注意:C++ 容器(如 std::vector)越界访问(at() 会抛异常,operator[] 不检查)会被 valgrind 捕获为 Invalid read;但 std::shared_ptr 的循环引用泄漏,valgrind 无法识别——它只管内存块是否被释放,不管智能指针逻辑。
复杂点在于:有些泄漏是设计使然(比如单例的静态实例),有些 still reachable 是程序退出前未清理的缓存。别见 leak 就删,先确认生命周期是否合理。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











