C语言动态内存管理:malloc、calloc、realloc与free

C语言动态内存管理:malloc、calloc、realloc与free
LichermioneC语言动态内存管理
1. 为什么需要动态内存
固定数组的大小通常在编译阶段确定。当数据规模运行时才知道,或对象需要跨越函数作用域长期存在时,可以从堆上动态申请空间。
2. malloc与free
1 |
|
malloc 返回未初始化内存。C语言中不需要对 void * 返回值强制转换,保留原写法还能避免漏包含 <stdlib.h> 时隐藏问题。
乘法计算大小时应防止溢出:
1 | if (count > SIZE_MAX / sizeof(*arr)) |
3. calloc
1 | int *arr = calloc(count, sizeof(*arr)); |
calloc 接收元素个数和单个元素大小,并将所有字节初始化为0。全零字节通常对应整数0,但不能泛化为所有类型的所有语义值。
4. realloc
1 | int *new_arr = realloc(arr, new_count * sizeof(*arr)); |
realloc 可能原地扩展,也可能申请新块、复制数据并释放旧块。失败时旧指针仍然有效,所以不能直接写:
1 | arr = realloc(arr, new_size); // 失败会丢失旧地址 |
5. 常见错误
- 忘记
free,造成内存泄漏; - 释放后继续访问,形成悬空指针;
- 同一地址重复释放;
- 释放非动态申请的地址;
- 写入超过申请空间;
- 使用未初始化的
malloc内容; - 函数返回局部变量地址;
- 浅拷贝含动态指针的结构体,导致重复释放。
6. 柔性数组
C99允许结构体最后一个成员为不完整数组:
1 | struct Packet |
结构头和变长数据一次分配,减少内存碎片并改善局部性。
7. 程序内存区域
典型进程包括代码区、只读数据区、已初始化数据区、BSS、堆、内存映射区和栈。具体布局由操作系统、编译器和链接器决定。
8. 总结
动态内存的核心是所有权:谁申请、谁释放、何时失效、容量多大。每个动态对象都应有明确生命周期,并检查所有分配和扩容结果。