C语言学习笔记20260711-动态内存管理四大核心函数详解及用例
·
C语言学习笔记20260711-C语言动态内存管理四大核心函数详解及用例
核心头文件:
<stdlib.h>
适用场景: 运行时才知道大小、需要跨函数共享数据、处理大型数据结构
一、四大核心函数详解
1. malloc - 分配未初始化内存
函数原型
void *malloc(size_t size);
功能说明
在堆区分配一块大小为 size 字节的连续内存,内存内容未初始化(垃圾值)。
代码示例
#include <stdio.h>
#include <stdlib.h>
int main(void) {
// 分配10个int的空间
int *arr = (int *)malloc(10 * sizeof(int));
// ⚠️ 必须检查返回值
if (arr == NULL) {
perror("malloc failed");
return 1;
}
// 未初始化的内存是垃圾值,必须手动赋值
for (int i = 0; i < 10; i++) {
arr[i] = i * 2;
}
printf("arr = %d\n", arr); // 输出: arr = 10
free(arr);
arr = NULL; // 防止悬空指针
return 0;
}
关键要点
- 返回
void*,C语言中可隐式转换,但建议显式强转提高可读性 - 分配失败返回
NULL,必须检查 - 内存内容是未定义的垃圾值
2. calloc - 分配并清零内存
函数原型
void *calloc(size_t num, size_t size);
功能说明
分配 num 个大小为 size 字节的内存块,并自动将所有字节初始化为0。
代码示例
#include <stdio.h>
#include <stdlib.h>
int main(void) {
// 分配10个int,且全部初始化为0
int *arr = (int *)calloc(10, sizeof(int));
if (arr == NULL) {
perror("calloc failed");
return 1;
}
// 验证是否全为0
for (int i = 0; i < 10; i++) {
printf("%d ", arr[i]); // 输出: 0 0 0 0 0 0 0 0 0 0
}
printf("\n");
free(arr);
arr = NULL;
return 0;
}
关键要点
- 等价于
malloc(num * size)+memset(ptr, 0, num * size) - 适合数组、结构体、哈希表等需要清零的场景
- 性能略低于
malloc(多了清零操作)
3. realloc - 调整内存大小
函数原型
void *realloc(void *ptr, size_t size);
功能说明
调整已分配内存块的大小,行为逻辑如下:
| 参数情况 | 等价操作 | 说明 |
|---|---|---|
ptr == NULL | malloc(size) | 等同于分配新内存 |
size == 0 且 ptr != NULL | free(ptr) | 等同于释放内存 |
| 原空间足够 | 原地扩展 | 返回原指针 |
| 原空间不足 | 重新分配+拷贝+释放旧块 | 返回新指针 |
⚠️ 致命陷阱
如果 realloc 失败返回 NULL,原内存块依然有效且未释放。直接用原指针接收返回值会导致内存泄漏。
代码示例
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *arr = (int *)malloc(5 * sizeof(int));
if (arr == NULL) return 1;
for (int i = 0; i < 5; i++) arr[i] = i;
// ✅ 正确用法:用临时指针接收返回值
int *tmp = (int *)realloc(arr, 10 * sizeof(int));
if (tmp == NULL) {
perror("realloc failed");
free(arr); // realloc失败时,原内存仍有效,必须手动释放
return 1;
}
arr = tmp; // 确认成功后再赋值给原指针
// 新增的空间内容是未初始化的
for (int i = 5; i < 10; i++) arr[i] = i * 10;
for (int i = 0; i < 10; i++) printf("%d ", arr[i]);
printf("\n");
free(arr);
arr = NULL;
return 0;
}
关键要点
- 永远用临时指针接收返回值
- 扩容后新增的内存未初始化
- 缩容时数据会被截断,但不会丢失已保留部分
4. free - 释放内存
函数原型
void free(void *ptr);
功能说明
释放之前由 malloc/calloc/realloc 分配的堆内存。
代码示例
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *p = (int *)malloc(sizeof(int));
if (p == NULL) return 1;
*p = 42;
printf("*p = %d\n", *p);
free(p);
p = NULL; // ✅ 释放后立即置NULL,防止悬空指针
// free(p); // ❌ 如果p没置NULL,这里会崩溃(重复释放)
free(p); // ✅ free(NULL) 是安全的,什么都不做
return 0;
}
关键要点
- 只能释放堆内存,不能释放栈变量、全局变量、字符串常量
free(NULL)是安全操作,什么都不做- 重复释放是未定义行为,会导致堆损坏
- 释放后指针变成悬空指针,必须置
NULL
二、常见错误与最佳实践
错误速查表
| 错误类型 | 说明 | 后果 | 预防措施 |
|---|---|---|---|
| 未检查返回值 | malloc 失败返回 NULL,直接使用 | 段错误 | 每次分配后必须检查 == NULL |
| 内存泄漏 | 分配后未 free,或指针丢失 | 内存耗尽 | 遵循"谁分配谁释放"原则 |
| 悬空指针 | free 后未置 NULL,再次使用 | 未定义行为 | free(p); p = NULL; 形成肌肉记忆 |
| 重复释放 | 同一指针 free 两次 | 堆损坏/崩溃 | 置 NULL 后 free 是安全的 |
| 越界访问 | 读写超出分配大小的内存 | 未定义行为 | 始终记录分配大小,使用工具检测 |
| realloc 错误用法 | ptr = realloc(ptr, size) | 失败时内存泄漏 | 用临时指针接收,成功后再赋值 |
| 释放栈内存 | free(&local_var) | 崩溃 | 只释放堆内存 |
| 释放后使用 | 访问已 free 的内存 | 未定义行为 | 释放后立即置 NULL |
最佳实践清单
- 分配必检查:每次
malloc/calloc/realloc后检查返回值 - 释放必置空:
free(p); p = NULL;写成固定套路 - realloc用临时指针:避免失败时内存泄漏
- 记录分配大小:用变量或结构体成员保存大小,避免越界
- 谁分配谁释放:明确内存所有权,避免重复释放或泄漏
- 函数返回前检查:确保所有路径都正确释放内存
- 使用工具检测:不要依赖肉眼排查内存问题
三、实战综合示例
动态数组封装
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int *data;
size_t size;
size_t capacity;
} DynArray;
// 初始化
int dynarray_init(DynArray *arr, size_t init_cap) {
arr->data = (int *)calloc(init_cap, sizeof(int));
if (arr->data == NULL) return -1;
arr->size = 0;
arr->capacity = init_cap;
return 0;
}
// 追加元素
int dynarray_push(DynArray *arr, int value) {
if (arr->size >= arr->capacity) {
size_t new_cap = arr->capacity * 2;
int *tmp = (int *)realloc(arr->data, new_cap * sizeof(int));
if (tmp == NULL) return -1;
arr->data = tmp;
arr->capacity = new_cap;
}
arr->data[arr->size++] = value;
return 0;
}
// 释放
void dynarray_free(DynArray *arr) {
free(arr->data);
arr->data = NULL;
arr->size = 0;
arr->capacity = 0;
}
int main(void) {
DynArray arr;
if (dynarray_init(&arr, 4) != 0) {
perror("init failed");
return 1;
}
for (int i = 0; i < 10; i++) {
if (dynarray_push(&arr, i * i) != 0) {
perror("push failed");
dynarray_free(&arr);
return 1;
}
}
for (size_t i = 0; i < arr.size; i++) {
printf("%d ", arr.data[i]);
}
printf("\n");
dynarray_free(&arr);
return 0;
}
四、总结口诀
分配必检查,释放必置空。
Realloc 用临时,越界要命凶。
谁分配谁释放,工具勤检测。
堆上无小事,谨慎保平安。
最后更新:2026-07-11
更多推荐



所有评论(0)