C Academy · 课时

内存泄漏

查找并避免泄漏

第 3 / 4 课13 个步骤

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

什么是内存泄漏

当您分配了堆内存,却从未释放它,并且丢失了指向它的唯一指针时,就会发生内存泄漏。

这部分内存会一直保留到程序退出,造成资源浪费。

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

int main(void) {
    int *p = malloc(sizeof(int));
    *p = 5;
    printf("%d\n", *p);
    free(p);
    return 0;
}

丢失指针

典型的泄漏情况是:释放旧分配之前,就用新的分配结果覆盖了指针。

第一个内存块会变得无法访问。

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

int main(void) {
    int *p = malloc(sizeof(int));
    free(p);
    p = malloc(sizeof(int));
    *p = 10;
    printf("%d\n", *p);
    free(p);
    return 0;
}

循环中的泄漏

在循环中分配内存,却不在每次迭代中释放,会使泄漏迅速累积。

解决方法是在下一次迭代覆盖指针之前先释放内存。

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

int main(void) {
    for (int i = 0; i < 3; i++) {
        int *p = malloc(sizeof(int));
        *p = i;
        printf("%d ", *p);
        free(p);
    }
    printf("\n");
    return 0;
}

提前返回会导致泄漏

分配内存后提前执行return,可能会跳过清理。请确保每条退出路径都会释放内存。

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

int process(int bad) {
    int *p = malloc(sizeof(int));
    if (bad) {
        free(p);
        return -1;
    }
    *p = 42;
    int r = *p;
    free(p);
    return r;
}

int main(void) {
    printf("%d\n", process(0));
    return 0;
}

使用 goto 的清理模式

让goto跳转到一个统一的清理标签,可以确保在同一处释放所有资源,避免重复调用 free。

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

int main(void) {
    int rc = 1;
    int *p = malloc(sizeof(int));
    if (!p) goto done;
    *p = 7;
    printf("%d\n", *p);
    rc = 0;
done:
    free(p);
    return rc;
}

为每次分配匹配释放

最简单的规则是:每次调用malloc、calloc或realloc,都必须恰好对应一次free。

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

int main(void) {
    char *a = malloc(8);
    char *b = calloc(4, sizeof(char));
    a[0] = 'x';
    printf("%c %d\n", a[0], b[0]);
    free(a);
    free(b);
    return 0;
}

释放嵌套分配

如果一个结构拥有堆内存,请先释放内部指针指向的内存,再释放外部内存块。

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

typedef struct {
    int *data;
} Box;

int main(void) {
    Box *b = malloc(sizeof(Box));
    b->data = malloc(3 * sizeof(int));
    b->data[0] = 1;
    printf("%d\n", b->data[0]);
    free(b->data);
    free(b);
    return 0;
}

释放指针数组

当您分配了一个指针数组,且每个元素都是独立分配的内存时,请先释放每个元素,最后再释放数组。

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

int main(void) {
    int n = 3;
    int **rows = malloc(n * sizeof(int *));
    for (int i = 0; i < n; i++) {
        rows[i] = malloc(2 * sizeof(int));
        rows[i][0] = i;
    }
    for (int i = 0; i < n; i++) free(rows[i]);
    free(rows);
    printf("freed all rows\n");
    return 0;
}

所有权规范

请确定每次分配由谁拥有并负责释放。将这一点记录下来,让调用者知道是否必须释放返回的指针。

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

char *make_greeting(void) {
    char *s = malloc(6);
    s[0]='h'; s[1]='i'; s[2]='\0';
    return s;
}

int main(void) {
    char *g = make_greeting();
    printf("%s\n", g);
    free(g);
    return 0;
}

查找泄漏的工具

Valgrind 和 AddressSanitizer 等工具会跟踪每次内存分配,并显示哪些内存从未被释放,从而报告泄漏。

它们对于验证实际程序必不可少。

完整的内存分配生命周期

总结起来就是:分配、检查、使用、在每条路径上释放,并将指针置为 NULL。这样可以使程序避免内存泄漏。

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

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

快速检查

请测试您对内存泄漏的理解。

回顾

您学习了如何防止内存泄漏:

  • 每次分配都必须对应恰好一次free。
  • 每条退出路径都要释放内存,包括提前返回和错误路径。
  • 释放嵌套分配和指针数组分配时,应先释放内部对象。
  • 明确所有权,并使用 Valgrind 等工具进行验证。
免费开始

用 AI 导师学习 C — 免费

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

课程
39
课程
144

常见问题解答

「内存泄漏」课时是免费的吗?

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

「内存泄漏」这节课中我会学到什么?

查找并避免泄漏 你通过在浏览器中直接运行的动手代码来练习 C Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C Academy 需要有经验吗?

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

「内存泄漏」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. malloc 与 free
  2. calloc 与 realloc
  3. 内存泄漏
  4. 悬空指针
← 返回 C Academy