核心概念
在 C 语言中,内存管理主要分为两种:

(图片来源网络,侵删)
- 栈内存:由编译器自动管理,你在函数内部声明的局部变量、函数参数等都存储在栈上,当函数执行完毕,这些内存会自动被释放,栈内存的分配和释放速度非常快,但容量相对较小。
- 堆内存:由程序员手动管理,当你需要一块大小在运行时才能确定的内存时,就需要使用堆内存。
malloc函数就是用来在堆上分配内存的,而free函数则是用来释放这块内存的。
malloc 函数
malloc 是 "memory allocation"(内存分配)的缩写。
函数原型
void *malloc(size_t size);
- 参数:
size_t size: 你需要分配的内存字节数。
- 返回值:
- 成功时: 返回一个指向分配内存块起始地址的
void*指针。void*是一个通用指针,你可以将它转换为任何类型的指针。 - 失败时: 如果堆内存不足,无法满足你的请求,
malloc会返回NULL指针。
- 成功时: 返回一个指向分配内存块起始地址的
使用步骤
- 包含头文件: 使用
malloc必须包含<stdlib.h>头文件。 - 声明指针: 声明一个与你所需数据类型匹配的指针。
- 调用
malloc: 调用malloc并传入所需的大小。强烈建议检查返回值是否为NULL,这是一个非常重要的健壮性编程习惯。 - 类型转换: 将
malloc返回的void*指针转换为你所需类型的指针。
示例:分配一个整型数组
#include <stdio.h>
#include <stdlib.h> // 必须包含
int main() {
int n = 10;
int *arr; // 1. 声明一个整型指针
// 2. 调用 malloc 分配 n 个 int 大小的内存
// sizeof(int) 获取一个 int 所占的字节数
// 3. 检查返回值是否为 NULL
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("内存分配失败!\n");
return 1; // 分配失败,退出程序
}
// 4. 你可以像使用普通数组一样使用 arr
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
printf("动态数组内容:\n");
for (int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// ... 后续代码 ...
return 0;
}
free 函数
free 用于释放由 malloc、calloc 或 realloc 分配的堆内存。如果你不再使用一块动态分配的内存,必须立即释放它,否则会导致内存泄漏。
函数原型
void free(void *ptr);
- 参数:
void *ptr: 指向要释放的内存块起始地址的指针,这个指针通常就是malloc返回的指针。
- 返回值:
- 无返回值 (
void)。
- 无返回值 (
使用步骤
- 确保指针有效: 传入一个非
NULL的指针,这个指针必须是之前通过malloc等函数获得的。 - 调用
free: 释放内存。 - 置空指针 (Good Practice): 释放内存后,将指针设置为
NULL,这是一个非常好的编程习惯,可以防止“悬垂指针”(Dangling Pointer)的问题,悬垂指针是指向一块已释放内存的指针,对它进行解引用会导致未定义行为。
示例:释放内存
接续上面的示例:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 10;
int *arr;
arr = (int *)malloc(n * sizeof(int));
if (arr == NULL) {
printf("内存分配失败!\n");
return 1;
}
// ... 使用数组 ...
// 1. 调用 free 释放内存
free(arr);
// 2. 将指针置为 NULL,防止悬垂指针
arr = NULL;
// 如果你再尝试访问 arr,程序会崩溃
// printf("arr[0] = %d\n", arr[0]); // 错误!arr 是 NULL
printf("内存已成功释放,arr 已被置为 NULL,\n");
return 0;
}
malloc 和 free 的核心规则与最佳实践
规则 1:必须配对使用
malloc 和 free 必须成对出现,有多少个 malloc,就应该有多少个对应的 free,忘记 free 会导致内存泄漏,程序运行时间越长,占用的内存越多,最终可能导致系统崩溃。

(图片来源网络,侵删)
规则 2:只能释放堆内存
free 只能用于释放通过 malloc、calloc 或 realloc 分配的内存。绝对不要尝试释放栈上的变量、静态变量或未经过 malloc 分配的指针。
int x = 10; int *ptr = &x; free(ptr); // 错误!会导致未定义行为
规则 3:只能释放一次
对同一块内存只能调用一次 free,释放后,如果再次调用 free 会导致未定义行为。
int *ptr = (int *)malloc(sizeof(int)); free(ptr); // ... free(ptr); // 错误!
规则 4:只能释放起始地址
你必须将 malloc 返回的原始指针(或经过 realloc 后的指针)传给 free,不能进行指针算术运算后再释放。
int *arr = (int *)malloc(10 * sizeof(int)); int *p = arr + 5; // p 指向 arr 的第 5 个元素 free(p); // 错误!必须 free(arr)
最佳实践:置空指针
在 free(ptr) 之后,立即执行 ptr = NULL;。
- 原因:
free只是释放了内存的所有权,但指针变量ptr本身仍然存在,并且它仍然保存着那块已释放内存的地址,这种指针被称为“悬垂指针”,如果你不小心再次使用这个指针(如*ptr = 5;),程序会尝试向一块不属于你的内存写入数据,导致崩溃或数据损坏。 - 好处:将指针置为
NULL后,如果你再次使用它,程序会立即崩溃(在对NULL指针解引用时),这比访问一块“脏”内存更容易被发现和调试。
常见错误
| 错误类型 | 描述 | 示例 | 后果 |
|---|---|---|---|
| 内存泄漏 | malloc 了内存,但没有 free。 |
int *p = malloc(sizeof(int)); // 程序结束前忘记 free |
程序持续占用内存,长期运行可能导致系统资源耗尽。 |
| 重复释放 | 对同一块内存调用了两次 free。 |
free(p); free(p); |
未定义行为,通常导致程序崩溃。 |
| 释放非堆内存 | 尝试释放栈变量或静态变量的地址。 | int x; free(&x); |
未定义行为,通常导致程序崩溃。 |
| 悬垂指针 | 释放内存后,继续使用该指针。 | free(p); *p = 10; |
未定义行为,可能导致数据损坏或程序崩溃。 |
相关函数
malloc 并不是唯一的动态内存分配函数,它的“亲戚”们也经常一起使用:
-
calloc:- 原型:
void *calloc(size_t num, size_t size); - 作用: 分配
num个大小为size的内存块,并将所有字节初始化为 0。 - 与
malloc区别:malloc不会初始化内存(内容是随机的),而calloc会。calloc适用于需要清零的场景,比如结构体数组。
// 分配 10 个 int,并全部初始化为 0 int *arr_calloc = (int *)calloc(10, sizeof(int));
- 原型:
-
realloc:- 原型:
void *realloc(void *ptr, size_t new_size); - 作用: 改变一个已经分配的内存块的大小。
- 参数:
ptr: 原始内存块的指针,如果为NULL,realloc的行为等同于malloc。new_size: 新的内存大小。
- 返回值:
- 成功时,返回新内存块的指针。这个指针可能和原来的不同,因为
realloc可能需要找到一块新的、更大的连续内存,并将旧数据拷贝过去。 - 失败时,返回
NULL,但原始内存块中的数据依然存在且有效。
- 成功时,返回新内存块的指针。这个指针可能和原来的不同,因为
- 重要: 必须使用
realloc的返回值来更新你的指针,并且要检查是否为NULL。
int *arr = malloc(10 * sizeof(int)); // ... 使用 arr ... // 现在需要扩大数组 int *temp = realloc(arr, 20 * sizeof(int)); if (temp == NULL) { printf("内存重新分配失败!\n"); // 原来的 arr 仍然有效,可以继续使用或 free free(arr); return 1; } arr = temp; // 更新指针 // ... 现在可以使用 20 个元素的 arr ... free(arr); - 原型:
| 函数 | 功能 | 是否初始化内存 | 失败时返回 |
|---|---|---|---|
malloc |
分配指定字节数的内存 | 否 | NULL |
calloc |
分配并初始化指定数量的元素 | 是(全部置0) | NULL |
realloc |
调整已分配内存的大小 | 不保证(但会保留原数据) | NULL(原数据不变) |
free |
释放内存 | N/A | N/A |
掌握 malloc 和 free 是 C 语言编程的基石,记住“谁分配,谁释放”的原则,并养成检查返回值和释放后置空的好习惯,就能写出健壮、可靠的 C 程序。
