检测内存泄漏
memcheck 基础
检测内存泄漏 是 CoddyKit 上的免费 C Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。
什么是内存泄漏
当您使用 malloc、calloc 或 realloc 分配堆内存,却在调用 free 前丢失了指向它的所有指针时,就会发生内存泄漏。
这块内存会一直处于保留状态,但在进程退出前都无法访问。在长时间运行的程序中,泄漏会不断累积,直到内存耗尽。
一个最小的泄漏示例
这个程序泄漏了一个 40 字节的内存块。
指针 p 是一个局部变量。当 main 返回时,p 消失了,但它所指向的堆内存块从未被释放。
#include <stdlib.h>
int main(void) {
int *p = malloc(10 * sizeof(int));
p[0] = 1;
return 0; /* never free(p) */
}运行 memcheck
memcheck 是默认工具,因此可以省略工具名称。
添加 --leak-check=full,即可获得每次泄漏分配的堆栈跟踪。
gcc -g -O0 -o leak leak.c
valgrind --leak-check=full ./leak泄漏摘要
退出时,Valgrind 会打印 LEAK SUMMARY。对于上面的程序,内容大致如下:
definitely lost: 40 bytes in 1 blocks
跟踪信息会指向确切的 malloc 调用位置,因此您可以准确知道哪个分配从未被释放。
四种泄漏类别
memcheck 会将丢失的内存分为四类:
- definitely lost — 不再存在任何指针;是真正的泄漏
- indirectly lost — 只能通过 definitely-lost 内存块访问
- possibly lost — 只剩下指向内存块内部的指针
- still reachable — 退出时仍存在指针,但您从未释放这块内存
Definitely lost 与 still reachable
definitely lost 是需要紧急处理的类别:程序运行期间永远无法回收这些字节。
still reachable 的严重程度较低。程序退出时该内存块仍属于泄漏状态,但全局指针或静态指针仍引用着它,因此通常只是一次性分配后一直没有释放。
间接泄漏
当您丢失链式结构的头部时,每个节点都会变得无法访问。
头部会显示为 definitely lost,其余部分显示为 indirectly lost。修复根本原因,也就是找回丢失的头部,就可以恢复整个结构。
struct Node { int v; struct Node *next; };
struct Node *make(void) {
struct Node *h = malloc(sizeof *h);
h->next = malloc(sizeof *h);
h->next->next = NULL;
return h;
}
/* if the caller drops the returned head, both nodes leak */修复后的版本
分配了什么,就释放什么。这个版本不会泄漏任何内存。
在 Valgrind 下运行时,它会报告 All heap blocks were freed -- no leaks are possible。
#include <stdlib.h>
int main(void) {
int *p = malloc(10 * sizeof(int));
p[0] = 1;
free(p);
return 0;
}统计分配次数
即使没有泄漏,HEAP SUMMARY 也能告诉您程序执行了多少内存操作:
total heap usage: 1 allocs, 1 frees, 40 bytes allocated
当分配次数和释放次数相等时,说明本次运行没有问题。两者不匹配,是发现某些内存未被释放的第一个线索。
显示仍可访问的内存块
默认情况下,memcheck 不会详细列出仍可访问的内存块。若也要检查这些内存块,请添加:
--show-leak-kinds=all
这会强制为每个类别输出跟踪信息,适合检查那些希望在退出前彻底释放所有内存的程序。
valgrind --leak-check=full --show-leak-kinds=all ./leak错误路径上的泄漏
最隐蔽的泄漏往往藏在提前返回的路径中。在这里,如果第二次分配失败,第一块内存就会泄漏。
普通测试很少会走到这些分支,但只要执行到相应路径,memcheck 就能捕获它们。在发生错误并返回前,请始终释放手中持有的内存。
char *a = malloc(100);
char *b = malloc(100);
if (!b) {
/* bug: a is leaked here */
return -1;
}
/* fix: free(a); before returning */快速检查
请选择最需要优先处理的泄漏类别。
回顾
现在您已经可以使用 memcheck 检测泄漏:
- 对使用
-g编译的二进制文件运行valgrind --leak-check=full ./prog - 阅读 LEAK SUMMARY 和 HEAP SUMMARY
- 优先处理 definitely lost,然后追查间接泄漏的根源
- 使用
--show-leak-kinds=all检查仍可访问的内存块
接下来:无效的内存访问。
常见问题解答
「检测内存泄漏」课时是免费的吗?
是的 — 「检测内存泄漏」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。
「检测内存泄漏」这节课中我会学到什么?
memcheck 基础 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「检测内存泄漏」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C Academy 课中编写并运行代码吗?
能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 为什么使用 Valgrind
- 检测内存泄漏
- 无效访问
- 阅读报告