0Pricing
C Academy · 课时

检测内存泄漏

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为什么使用 Valgrind
  2. 检测内存泄漏
  3. 无效访问
  4. 阅读报告
← 返回 C Academy