valgrind 不直接判定代码是否有问题,仅报告确认的非法行为;definitely lost 表示堆内存泄漏且无任何有效引用,属严重 bug;possibly lost 多因复杂数据结构或指针转换导致,需人工核查;still reachable 通常非 bug,但需确认是否真需长期持有;invalid read 和 use of uninitialised value 比泄漏更危险,常致崩溃或逻辑异常;必须用 -g 编译才能定位行号;memcheck 与 helgrind 需分别运行以覆盖内存与线程问题。

Valgrind 不会直接告诉你“有没有问题”,它只报告它能确认的非法行为——只要输出里没报错,不等于代码安全。
看 definitely lost 和 possibly lost 这两类泄漏标记
Memcheck 把泄漏分等级,最危险的是 definitely lost:指那块内存的指针彻底丢了,既没被保存到全局变量、也没传给其他函数、更没存进数据结构里。这种基本就是 bug。而 possibly lost 常见于指针被存在了容器里但 Valgrind 没法追踪(比如自定义哈希表、或指针被转成整数再转回),不能直接忽略,得人工查。
-
definitely lost出现,说明程序退出前有堆内存没释放,且找不到任何有效引用 -
possibly lost多见于复杂数据结构、多线程共享指针、或用了memcpy拷贝指针值的场景 -
still reachable通常不算 bug,是程序退出时还存着合法指针(比如全局缓存、单例对象),但要确认是否真需要长期持有
别只盯着泄漏,Invalid read 和 Use of uninitialised value 更要命
这两类错误往往比泄漏更早引发崩溃。比如 Invalid read of size 4 表示你读了 malloc 分配区之外的 4 字节,可能刚好踩中相邻变量,也可能触发段错误;Use of uninitialised value 则常导致逻辑随机失败(比如 if 判断结果飘忽不定)。它们在日志里排在泄漏前面,优先级更高。
- 越界读写不一定立刻 crash,但会让内存状态不可预测,后续行为难以复现
- 未初始化值在不同编译器优化等级下表现可能不同,
-O0下容易暴露,-O2下可能被优化掉或掩盖 - 这类错误一旦出现,必须修复,不能靠“暂时没崩”来侥幸
valgrind --leak-check=full 必须配合 -g 编译,否则看不到行号
没有调试符号,Valgrind 只能显示 vg_replace_malloc.c:381 这种内部调用栈,对定位毫无帮助。实际排查时,at 0x1092AB: create_string_set_from_file (spellcheck.c:25) 这样的输出才是有效的。
- 编译时漏掉
-g,就等于让 Valgrind “睁眼瞎” - 如果开了优化(如
-O2),函数内联可能导致调用栈变浅,建议开发阶段统一用-g -O0 - 静态链接的二进制文件(
-static)会让部分系统库调用栈丢失,尽量避免
多线程下 Helgrind 和 Memcheck 要分开跑
Memcheck 本身不检查竞态条件,它只管内存操作是否合法。两个线程同时 free 同一块内存,Memcheck 可能只报一次 double free 或干脆漏掉;而 Helgrind 专盯锁缺失和数据竞争,但它不擅长检测内存泄漏。想覆盖全面,得分别运行:
-
valgrind --tool=memcheck --leak-check=full ./prog查内存使用问题 -
valgrind --tool=helgrind ./prog查线程间同步缺陷 - 两者输出互不替代,也不能合并执行——
--tool参数只能指定一个
真正难缠的问题往往藏在 possibly lost 和 still reachable 的边界上,比如对象生命周期管理模糊、RAII 实现有缺陷、或跨模块传递裸指针。Valgrind 给的是线索,不是判决书,最终得靠你对照源码判断那个指针“该不该还在”。











