为什么需要 malloc 和 free?
在 C 语言中,变量存储在内存的不同区域,主要有:

(图片来源网络,侵删)
- 栈:存放局部变量、函数参数等,它的特点是自动分配和释放,当函数被调用时,栈空间为变量分配;函数返回时,这些空间自动被回收。大小在编译时确定,运行时无法改变。
- 全局/静态区:存放全局变量和静态变量,它的生命周期是整个程序运行期间。
- 堆:这是一块自由内存区域,程序可以在运行时根据需要,从堆中申请任意大小的内存,这块内存的生命周期由程序员自己控制,不会自动释放。
malloc 和 free 就是用来管理堆内存的。
malloc(Memory Allocation):从堆中分配一块指定大小的内存。free:释放一块由malloc(或其他类似函数)分配的堆内存,将其归还给系统,以便后续使用。
malloc 函数
函数原型
#include <stdlib.h> // 必须包含的头文件 void *malloc(size_t size);
参数
size_t size:你需要分配的内存大小,以字节为单位。
返回值
- 成功时:返回一个指向分配内存块起始地址的
void*指针。void*是一个通用指针,可以转换为任何类型的指针。 - 失败时:如果堆内存不足,无法满足请求,
malloc会返回NULL指针。
工作原理
malloc 会向操作系统请求一块连续的内存,如果请求成功,它返回这块内存的地址;如果失败,返回 NULL。
使用示例
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr; // 声明一个整型指针
// 1. 分配内存
// 请求 sizeof(int) 字节的内存,通常是一个整型的大小(4 字节)
ptr = (int *)malloc(sizeof(int));
// 2. 检查分配是否成功
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1; // 返回错误码
}
// 3. 使用分配的内存
*ptr = 100;
printf("ptr 指向的值是: %d\n", *ptr);
// ... 在这里使用 ptr ...
// 4. 释放内存
free(ptr);
// 5. 将指针置为 NULL (好习惯)
ptr = NULL;
return 0;
}
free 函数
函数原型
#include <stdlib.h> void free(void *ptr);
参数
void *ptr:一个指向由malloc、calloc或realloc分配的内存块的指针。
作用
- 释放
ptr指向的堆内存。 - 非常重要:
free只会释放内存本身,不会改变指针ptr的值。ptr仍然指向那块已经被释放的内存地址,这种指针被称为“悬垂指针”。
为什么必须 free?
如果不释放不再使用的堆内存,会导致内存泄漏,随着程序的运行,可用的堆内存会越来越少,最终可能导致程序崩溃,长期运行的程序(如服务器、嵌入式系统)中的内存泄漏是致命的。
关键注意事项和最佳实践
总是检查 malloc 的返回值
忘记检查 malloc 是否返回 NULL 是一个非常常见的错误,在尝试使用指针之前,必须确保它不是 NULL。

(图片来源网络,侵删)
int *arr = (int *)malloc(100 * sizeof(int));
if (arr == NULL) { // 必须检查!
// 处理错误
}
配对使用 malloc 和 free
malloc 和 free 必须成对出现,有多少次 malloc,就应该有多少次对应的 free,最好在 malloc 后立即规划好在哪里 free。
只能 free 由动态分配函数返回的指针
不要尝试 free 栈上的变量、全局变量或不是由 malloc 系列函数分配的内存,这会导致未定义行为,通常是程序崩溃。
int x = 10; int *ptr_stack = &x; free(ptr_stack); // 错误!不要这样做!
只能 free 一次
对同一块内存 free 两次会导致双重释放,这也是未定义行为,通常会引起程序崩溃。
int *ptr = (int *)malloc(sizeof(int)); free(ptr); // ... 其他代码 ... free(ptr); // 错误!双重释放!
释放后将指针置为 NULL (防御性编程)
free(ptr) 后,ptr 变成了悬垂指针,如果你不小心再次使用它(*ptr = 5;),就会导致内存访问错误,一个很好的习惯是 free 后立即将指针设为 NULL。
int *ptr = (int *)malloc(sizeof(int)); // ... 使用 ptr ... free(ptr); ptr = NULL; // ptr 是 NULL,再次使用前检查 if (ptr) 就可以避免错误
不要越界访问
动态分配的内存有明确的大小,你必须确保你的代码不会写入超出这个范围的内存,否则会破坏其他数据,导致程序行为异常或崩溃。
int *arr = (int *)malloc(10 * sizeof(int)); // 分配了 10 个 int 的空间 arr[10] = 123; // 错误!索引范围是 0 到 9,越界访问了!
malloc 的亲戚们
malloc 是一个家族,还有其他几个类似的函数:
| 函数 | 功能 | 与 malloc 的区别 |
|---|---|---|
malloc |
分配指定字节数的内存 | 不初始化,内存中是垃圾值。 |
calloc |
分配指定数量和大小的内存,并初始化为 0 | 参数是 num_elements 和 element_size。 2. 会将分配的内存全部清零。 |
realloc |
重新调整一块已分配内存的大小 | 可以扩大或缩小内存,如果扩大,新增加的部分不会被初始化,如果内存位置改变,realloc 会自动 free 旧内存并返回新内存的地址。 |
calloc 示例
// 分配一个能容纳 5 个 double 的数组,并全部初始化为 0.0
double *scores = (double *)calloc(5, sizeof(double));
if (scores != NULL) {
printf("scores[0] = %f\n", scores[0]); // 输出 0.000000
}
// ...
free(scores);
scores = NULL;
realloc 示例
int *arr = (int *)malloc(5 * sizeof(int));
// ... 填充 arr ...
// 假设现在需要空间变大为 10
int *temp = (int *)realloc(arr, 10 * sizeof(int));
if (temp != NULL) {
arr = temp; // realloc 成功,更新指针
// ... 现在可以使用 arr[0] 到 arr[9] ...
} else {
// realloc 失败,原内存 arr 仍然有效
printf("内存重新分配失败!\n");
}
// ...
free(arr);
arr = NULL;
| 函数 | 作用 | 关键点 |
|---|---|---|
malloc |
从堆分配内存 | 必须检查返回值是否为 NULL |
free |
释放堆内存 | 必须与 malloc 配对使用 |
calloc |
分配并清零内存 | 适用于数组,自动初始化为 0 |
realloc |
调整已分配内存大小 | 注意处理返回值,可能内存地址会变 |
掌握 malloc 和 free 是 C 语言编程中一项核心技能,它们赋予了程序极大的灵活性,但也带来了责任。谁申请,谁释放;申请多少,释放多少;释放之后,指针置空,遵循这些原则,可以避免绝大多数与动态内存相关的错误。
