内存泄漏
查找并避免泄漏
内存泄漏 是 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 反馈 — 无需本地设置。