C 语言内存管理:malloc() 与 free() 的正确使用
C 语言不提供自动内存回收机制,程序员必须手动申请和释放动态内存。malloc() 和 free() 是最基础、最关键的两个函数。用错它们会导致程序崩溃、内存泄漏或安全漏洞。
理解动态内存的基本规则
动态内存是指程序运行时从堆(heap)中临时借用的一块空间。它不会像局部变量那样在函数结束时自动消失,也不会像全局变量那样一直存在。你必须自己决定何时申请、何时归还。
- 申请内存用
malloc():告诉系统“我需要 N 字节”,系统返回这块内存的起始地址。 - 释放内存用
free():告诉系统“我不再需要这块内存了,请收回”。
这两个操作必须严格配对:每调用一次 malloc(),就必须且只能调用一次 free()。
正确使用 malloc() 的步骤
-
包含头文件
在代码开头写上#include <stdlib.h>。这是malloc()和free()的声明所在。 -
计算所需字节数
使用sizeof运算符获取单个元素的大小,再乘以元素个数。例如:int *arr = malloc(10 * sizeof(int));这表示申请能存放 10 个
int类型数据的空间。 -
检查返回值是否为 NULL
如果系统内存不足,malloc()会返回NULL。必须检查这个值,否则后续操作可能导致程序崩溃。int *ptr = malloc(100 * sizeof(int)); if (ptr == NULL) { // 处理错误,比如退出程序或提示用户 return -1; } -
不要对未初始化的内存做假设
malloc()返回的内存内容是未定义的(可能是任意垃圾值)。如果需要清零,应改用calloc(),或手动用memset()初始化。
正确使用 free() 的步骤
-
只释放通过 malloc/calloc/realloc 申请的内存
不能对栈变量、全局变量或字符串字面量调用free()。例如以下代码是严重错误:int x = 5; free(&x); // 错误!x 是栈变量 char *s = "hello"; free(s); // 错误!"hello" 是常量区字符串 -
释放后立即将指针设为 NULL
释放内存后,原指针仍然保存着旧地址(称为“悬空指针”)。再次使用它(读或写)会导致未定义行为。释放后应立即将指针赋值为NULL:free(ptr); ptr = NULL; // 防止后续误用 -
不要重复释放同一块内存
对同一个非 NULL 指针多次调用free()会导致程序崩溃。例如:free(ptr); free(ptr); // 危险!第二次释放已释放的内存将指针设为
NULL可避免此问题,因为free(NULL)是安全的(什么也不做)。
常见错误模式与规避方法
| 错误类型 | 错误代码示例 | 正确做法 |
|---|---|---|
| 内存泄漏 | c<br>void func() {<br> int *p = malloc(100);<br> // 忘记 free(p)<br>} | 函数结束前确保 free(p) 被执行 |
|
| 重复释放 | c<br>free(p);<br>free(p); | 释放后立即 p = NULL; |
|
| 使用已释放内存 | c<br>free(p);<br>*p = 10; // 危险 |
释放后不再访问该指针 |
| 申请 0 字节 | c<br>malloc(0); // 行为未定义 |
避免传入 0,或显式处理 |
实战:安全封装动态数组
为了减少出错,可以将内存管理逻辑封装成简单函数:
#include <stdio.h>
#include <stdlib.h>
// 安全申请整数数组
int* create_int_array(size_t size) {
if (size == 0) return NULL;
int *arr = malloc(size * sizeof(int));
if (arr == NULL) {
fprintf(stderr, "内存申请失败\n");
return NULL;
}
return arr;
}
// 安全释放并置空
void destroy_int_array(int **arr) {
if (arr != NULL && *arr != NULL) {
free(*arr);
*arr = NULL;
}
}
// 使用示例
int main() {
int *data = create_int_array(50);
if (data == NULL) return -1;
data[0] = 42; // 正常使用
destroy_int_array(&data); // 自动置 NULL
// 此时 data == NULL,无法误用
return 0;
}
关键点:
- 申请函数负责检查和初始化。
- 释放函数接受指针的地址(
int **),从而能修改原指针的值为NULL。 - 调用者无需记住
free后手动置空。
特殊注意事项
-
realloc() 的陷阱:
realloc(ptr, new_size)可能移动内存块。如果成功,原ptr失效;如果失败,原内存仍有效。不要直接赋值给原指针:// 错误 ptr = realloc(ptr, new_size); // 若失败,ptr 变 NULL,原内存丢失! // 正确 void *temp = realloc(ptr, new_size); if (temp != NULL) { ptr = temp; } else { // 处理错误,原 ptr 仍可用 } -
跨函数传递指针:当指针作为参数传入函数时,函数内部释放内存不会影响调用者的指针值。如需同步置空,必须传入指针的地址(如前述
destroy_int_array所示)。 -
多线程环境:
malloc()和free()本身是线程安全的,但多个线程同时操作同一块内存仍需加锁保护,防止竞争条件。
最终检查清单
每次使用动态内存后,问自己四个问题:
- 是否检查了
malloc()返回值? - 是否只对堆内存调用了
free()? - 是否确保每块内存只被
free()一次? - 释放后是否将指针设为
NULL?
只要全部回答“是”,就能避免绝大多数内存管理错误。

暂无评论,快来抢沙发吧!