C Academy · 课时

释放内存并避免泄漏

正确地清理资源。

第 4 / 4 课13 个步骤

释放内存并避免泄漏 是 CoddyKit 上的免费 C Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C Academy 课程共包含 4 节课。

每次 malloc 都需要一次 free

堆内存在使用 free 释放之前会一直被保留。如果您没有释放某个堆块就丢失了指向它的唯一指针,这块内存就会泄漏。

泄漏会随着时间不断累积,并可能耗尽长时间运行的程序的内存。

int *a = malloc(100 * sizeof(*a));
/* ... use a ... */
free(a);

free 实际做了什么

free(p) 会将堆块归还给分配器,以便重新使用。它不会改变 p 本身的值。

执行 free 后,p 仍然指向原来的地址,但该地址现在已经无效。访问它会导致未定义行为。

free(p);
/* p is now a dangling pointer */

悬空指针陷阱

释放指针后仍然使用它,是一种释放后使用错误。程序可能一开始看似正常,随后却破坏数据或发生不可预测的崩溃。

一个简单的防护措施是在释放后立即将指针设为 NULL。

free(a);
a = NULL;   /* now misuse is a clean NULL deref, easier to catch */

重复释放会导致未定义行为

对同一个堆块调用两次 free 会破坏分配器的内部状态,并且通常会导致崩溃。

第一次释放后将指针设为 NULL 会有所帮助,因为 free(NULL) 明确保证是安全的,并且不会执行任何操作。

free(a);
a = NULL;
free(a);   /* free(NULL) is a harmless no-op */

只释放通过 malloc 分配的内存

传给 free 的指针必须来自 malloc、calloc 或 realloc。

释放栈变量的地址、字符串字面量,或指向堆块中间位置的指针,都会导致未定义行为。

int x = 5;
/* free(&x);   WRONG: x is on the stack */
int *p = malloc(sizeof(int));
free(p);        /* correct */

提前返回导致的泄漏

一种常见的泄漏情况是:您分配了内存,随后进入错误处理路径,在释放之前就返回了。这样一来,堆块就丢失了。

请确保每条负责这块内存的退出路径都会先释放它。

int *a = malloc(n * sizeof(*a));
if (something_failed) {
    free(a);   /* don't forget this before returning */
    return -1;
}

干净地分配和释放内存的程序

这个程序会在每条路径上分配、使用并释放内存,不会产生任何泄漏。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int n = 4;
    int *a = malloc(n * sizeof(*a));
    if (!a) return 1;
    for (int i = 0; i < n; i++) a[i] = i + 1;
    int sum = 0;
    for (int i = 0; i < n; i++) sum += a[i];
    printf("%d\n", sum);
    free(a);
    a = NULL;
    return 0;
}

释放嵌套分配

如果一个堆块中包含指向其他堆块的指针,请先释放内部堆块,再释放外部堆块。

如果先释放外部堆块,就会丢失内部堆块的地址,从而导致它们泄漏。

for (size_t i = 0; i < rows; i++)
    free(grid[i]);   /* free each row first */
free(grid);          /* then the array of pointers */

每个堆块只有一个所有者

请确定代码中的哪一部分拥有每次分配得到的内存,并负责释放它。

如果两个指针指向同一个堆块并且都释放它,就会发生重复释放。明确所有权可以避免这种问题。

使用工具检测泄漏

您不必靠肉眼查找泄漏。valgrind 或 AddressSanitizer 等工具可以报告泄漏和无效释放,并指出对应的行号。

请使用 -g 进行编译,然后在该工具下运行程序,以准确查看哪些内存没有被释放。

/* gcc -g -fsanitize=address prog.c && ./a.out */
/* or: valgrind --leak-check=full ./a.out */

按设置顺序的相反顺序释放

一个可靠的习惯是:在获取多个资源的函数中,按照获取顺序的相反顺序释放它们。

这与构建依赖关系的方式相对应,也能让清理过程更加可预测。

char *buf = malloc(64);
int  *idx = malloc(64 * sizeof(int));
/* ... use them ... */
free(idx);   /* free last-acquired first */
free(buf);

快速检查

请测试您对释放内存的理解。

回顾

请在每条代码路径上为每次内存分配恰好配对一次 free。

释放后将指针设为 NULL,可以避免悬空指针和重复释放。请先释放嵌套分配的内部对象,为每个堆块指定一个所有者,并使用 valgrind 或地址消毒器捕获泄漏。现在,您已经掌握了完整的动态数组工具集。

免费开始

用 AI 导师学习 C — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
39
课程
144

常见问题解答

「释放内存并避免泄漏」课时是免费的吗?

是的 — 「释放内存并避免泄漏」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C Academy 课程的其余内容,请升级到 CoddyKit PRO。 C Academy 课程共包含 4 节课。

「释放内存并避免泄漏」这节课中我会学到什么?

正确地清理资源。 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「释放内存并避免泄漏」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C Academy 课中编写并运行代码吗?

能。每节 C Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 分配数组
  2. 使用 realloc 扩展
  3. 可复用的向量类型
  4. 释放内存并避免泄漏
← 返回 C Academy