动态内存分配:malloc、calloc、realloc、free 函数的使用
说实话,C语言的动态内存分配,是很多初学者觉得“玄乎”的地方。你想想看,平时我们写int a[100],那是编译时就定死的。可程序跑起来之后,到底需要多少内存,很多时候是未知的。这时候,就得靠动态内存分配来救场了。
我个人习惯把动态内存分配比作“向操作系统借地皮”。你写个申请,系统批一块内存给你用,用完你得还回去。不还?那就是内存泄漏,程序跑久了,系统资源就被你吃光了。
malloc:最基础的内存申请
malloc 的全称是 memory allocation。它的作用很简单——向系统申请一块连续的内存空间。
#include <stdlib.h>
int *p = (int *)malloc(10 * sizeof(int));
if (p == NULL) {
// 处理内存分配失败
return -1;
}
这里有个细节:malloc 返回的是 void*,所以需要强制类型转换。我早期写代码时经常忘了检查返回值,结果程序在嵌入式设备上跑着跑着就崩了。嗯,从那以后,我每次 malloc 完第一件事就是判空。
calloc:带清零的分配
calloc 和 malloc 很像,但它多做了两件事:一是把分配的内存全部清零,二是参数形式不同。
int *p = (int *)calloc(10, sizeof(int));
// 等价于 malloc + memset(p, 0, 10 * sizeof(int));
我在项目中遇到过一种情况:用 malloc 分配了一个结构体数组,结果忘了初始化,某个标志位是随机值,导致逻辑判断出错。后来改用 calloc,问题就解决了。说白了,如果你需要初始化为零的内存,直接用 calloc 更省事。
| 函数 | 初始化 | 参数形式 | 适用场景 |
|---|---|---|---|
| malloc | 不初始化 | (size) | 需要原始内存,后续自己赋值 |
| calloc | 清零 | (count, size) | 需要零初始化,如数组、结构体 |
realloc:调整已分配的内存大小
有时候,你一开始申请了 100 个元素的空间,跑着跑着发现不够了。难道再 malloc 一块新的,然后把数据拷过去?太麻烦了。realloc 就是干这个的。
int *p = (int *)malloc(5 * sizeof(int));
// ... 使用中 ...
int *new_p = (int *)realloc(p, 10 * sizeof(int));
if (new_p == NULL) {
// realloc 失败,原内存 p 仍然有效
free(p);
return -1;
}
p = new_p;
这里有个坑,我曾经踩过:realloc 可能会移动内存块,返回一个新的地址。如果你直接 p = realloc(p, new_size),万一 realloc 失败返回 NULL,原来的 p 就丢了,造成内存泄漏。正确的做法是用一个临时指针接返回值。
free:用完记得还
动态分配的内存,用完之后必须用 free 释放。否则,这块内存就一直被占用着,直到程序退出。
int *p = (int *)malloc(100 * sizeof(int));
// 使用 p
free(p);
p = NULL; // 好习惯:释放后置空
为什么释放后要置空?因为 free 之后,p 指向的地址已经无效了,但 p 本身的值还在。如果不置空,后面不小心再 *p = 10,那就是操作野指针,后果不可预料。我曾经在调试一个通信协议栈时,就是因为 free 后没置空,导致偶发性的数据错乱,查了整整两天才找到原因。
- 不要重复 free 同一块内存
- 不要 free 栈上的地址(比如局部变量)
- 不要 free 之后继续使用该指针
知识体系总览
下面这张图,把四个函数的关系和流程梳理清楚了。我建议你把它记在脑子里,写代码时随时对照。
实际项目中的避坑指南
我做了这么多年嵌入式开发,动态内存这块踩过的坑真不少。给你总结几条血的教训:
- 嵌入式环境慎用动态分配——在资源受限的 MCU 上,频繁 malloc/free 会导致内存碎片。我建议在系统初始化时一次性分配好,后续不再释放。
- 配对检查——每次 malloc 都要对应一个 free,可以用一个计数器来跟踪。我曾经在一个网络协议栈里,因为某个异常路径漏了 free,导致设备运行 72 小时后内存耗尽重启。
- 不要 free 栈变量——比如
int a[10]; free(a);这是未定义行为,程序可能当场崩溃。 - realloc 的第三个参数陷阱——有些新手以为 realloc 可以缩小内存,确实可以,但缩小的部分不会自动清零。如果你依赖清零,得自己动手。
好了,动态内存分配这块,说白了就是“借、用、还”三个字。malloc/calloc 是借,realloc 是调整,free 是还。把这四个函数用熟了,你的 C 语言功力就能上一个台阶。