C 语言 calloc 与 realloc
更新时间:2026-08-26。本文是
languages/c/主题入门层第 52 篇。malloc只管给空间,内容靠你自己清;那"要一块清零的内存"该用什么?数组跑着跑着不够长了,又不能把栈变量撑大——答案是calloc和realloc。三个分配函数凑齐,动态内存的基本功就齐了。
本文要回答的问题
calloc和malloc到底差在哪?realloc怎么扩容?失败怎么办?- 三个分配函数该怎么选?
一、calloc:分配 + 清零
c
#include <stdlib.h>
int *p = calloc(100, sizeof(int)); // 100 个 int,全部初始化为 0与 malloc 的对比:
| 对比项 | malloc(n) | calloc(count, size) |
|---|---|---|
| 参数 | 字节数 | 元素个数 × 元素大小 |
| 初始内容 | 未定义(垃圾值) | 全 0 |
| 典型场景 | 马上要写入 | 需要"全空"初值(数组、计数) |
两个参数的好处:自动防溢出——calloc 内部用乘法并检查,malloc(count * size) 手写乘法可能整数溢出(count、size 很大时)。
c
// 清零数组,省得手动 memset
int *table = calloc(n, sizeof(int));
for (int i = 0; i < n; i++) {
table[i] += 1; // 初值确定是 0,逻辑安全
}二、realloc:原地扩容(或搬走)
c
#include <stdlib.h>
int *arr = malloc(10 * sizeof(int));
// ... 用了 10 个,不够了 ...
arr = realloc(arr, 20 * sizeof(int)); // 扩到 20 个realloc 的行为分成两种:
- 原地扩展:后面有空闲,直接往后延长,返回原指针;
- 搬家:后面没有连续空间,新找一块更大的,把旧内容全部拷过去,释放旧块,返回新地址。
无论哪种,新申请的部分(第 11~20 个)内容未初始化,要用得自己赋值。
三、realloc 最大的坑:失败丢指针
c
// ❌ 经典错误:失败时把原指针弄丢了
arr = realloc(arr, new_size); // 失败返回 NULL,arr 被覆盖!
// ✅ 正确:先用临时变量接住
int *tmp = realloc(arr, new_size);
if (tmp == NULL) {
// 失败:arr 仍然有效,内容不变,还能继续用
// 处理错误后 return
}
arr = tmp;为什么:realloc 失败返回 NULL,但原指针仍然有效(没被释放)。直接 arr = realloc(...) 会把 NULL 赋给 arr——原内存既没释放又找不回来,泄漏了。用临时变量,失败时原指针还在,可以从容处理。
四、realloc 后指针全部失效
realloc 可能搬家,搬家后所有指向旧内存的指针都悬垂:
c
int *arr = malloc(10 * sizeof(int));
int *first = &arr[0]; // 记录指向元素
arr = realloc(arr, 100 * sizeof(int)); // 可能搬家
// ❌ first 可能已悬垂
*first = 42;规则:realloc 之后,只用 realloc 返回的新指针;之前存的任何旧地址都不要再用。
五、三函数选型
| 需求 | 用哪个 |
|---|---|
| 要一块马上写满的内存 | malloc |
| 要全零的数组/表 | calloc |
| 数组要长大/缩小 | realloc |
| 频繁小对象 | 考虑内存池(进阶) |
配套铁律:无论哪个分配的,最后统一 free;realloc 后的内存只释放一次(realloc 已经处理旧块,别对旧指针再 free)。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
arr = realloc(arr, ...) | 失败丢指针、泄漏 | 临时变量接住 |
| realloc 后还用旧指针 | 悬垂、数据错乱 | 只用返回的新指针 |
| 忘了 calloc 已清零 | 多写 memset | 直接用 |
| realloc 缩小后继续访问尾部 | 越界 | 记住新大小 |
手写 count * size 溢出 | 分配过小 | 用 calloc(count, size) |
七、与本站主线衔接
一句话总结
calloc(count, size) = 分配 + 清零(还防乘法溢出),适合要"全空初值"的数组;realloc 可扩可缩,可能原地也可能搬家,失败返回 NULL 但原指针仍有效——必须用临时变量接住,成功后旧指针全部作废——三个函数一条铁律:谁分配谁 free,realloc 之后只用新指针。