第十三章:存储
级别 2 — 认知
malloc、存储期、初始化——C 如何管理对象生命周期以及 Auto 如何使其自动化。
内存管理是 C 程序最容易出错的地方。内存泄漏、释放后使用、双重释放和悬空指针占据了 C 安全漏洞的很大一部分。本章研究 C 的存储机制,并展示 Auto 如何消除整类内存错误。
13.1 malloc 及相关函数
C 提供四个函数用于动态内存分配:
c
// C 深入:动态分配
void *malloc(size_t size); // 分配 size 字节(未初始化)
void *calloc(size_t n, size_t s); // 分配 n*s 字节(零初始化)
void *realloc(void *p, size_t s); // 调整分配大小(可能移动)
void free(void *p); // 释放C 中的典型用法:
c
// C 深入:malloc 生命周期
int *arr = malloc(10 * sizeof(int)); // 分配
if (!arr) { /* 处理错误 */ } // 检查 NULL
for (int i = 0; i < 10; i++) {
arr[i] = i * i; // 使用
}
// ... 稍后 ...
free(arr); // 释放
arr = NULL; // 避免悬空指针问题层出不穷:
- 忘记释放:随时间增长的内存泄漏。
- 释放后使用:未定义行为,通常可被利用。
- 双重释放:破坏堆分配器的内部状态。
- 不检查 NULL:解引用失败的分配导致崩溃。
- 错误大小:写
malloc(sizeof(int*))而本意是malloc(sizeof(int))。
Auto 的替代:自动存储。 Auto 自动管理对象生命周期。Auto 中没有 malloc、free、realloc 或 calloc:
auto
// Auto:没有 malloc/free,自动生命周期管理
var arr [10]int
for i in 0..10 {
arr[i] = i * i
}
print("First:", arr[0])
// 不需要 free——存储自动回收当 Auto 需要动态大小的集合时,标准库提供带有自动清理的可增长容器:
auto
// Auto:可增长缓冲区
type Buffer {
data [256]int
size int
}
fn Buffer.new() Buffer {
Buffer([256]int{}, 0)
}
fn Buffer.push(b Buffer, val int) {
if b.size < 256 {
b.data[b.size] = val
b.size = b.size + 1
}
}