数组实现内存池的核心是预分配连续内存并用空闲链表管理槽位:分配时O(1)取链表头,释放时O(1)插回头部,避免malloc/free开销与碎片。
用数组实现内存池,核心是把一大块连续内存(比如一个大数组)预先划分为多个等长的“槽位”,每个槽位能存一个固定大小的对象。这样分配时不再调用 malloc/free,而是快速复用空闲槽位,避免碎片和系统调用开销。
一、设计内存池结构:数组 + 空闲链表
内存池本质是一个“预分配数组 + 管理元数据”的组合:
- 用 char buffer[N * sizeof(T)] 模拟堆内存,N 是最大对象数,T 是目标类型(如 int、struct node)
- 用 int free_list 维护空闲槽位索引链表:每个槽位头部(或额外数组)存下一个空闲位置的下标,类似单链表
- 初始时所有槽位空闲,free_list = 0,buffer[0] 存 1,buffer[1] 存 2……buffer[N-2] 存 N-1,buffer[N-1] 存 -1 表示结尾
二、分配逻辑:O(1) 取头节点,更新指针
分配一个对象时,不找空位,只取链表头:
- 若 free_list == -1 → 池满,返回 NULL
- 否则,记下当前索引 pos = free_list
- 更新 free_list = *(int*)&buffer[pos * sizeof(T)](即读取该槽位开头存的下一个空闲索引)
- 返回 &buffer[pos * sizeof(T)] + sizeof(int),跳过头部的“next”字段(或单独用索引数组管理,不侵占对象空间)
✅ 小技巧:若 T 是自定义结构体,可在结构体第一个成员预留 int next;分配后用户拿到的是结构体起始地址,内部 next 字段由池管理使用。
三、释放逻辑:插回链表头部,O(1)
释放时把槽位“还给”空闲链表,只需两步:
- 将待释放指针 ptr 对应的槽位索引 pos = (ptr - buffer) / sizeof(T)
- 写入 *(int*)&buffer[pos * sizeof(T)] = free_list(把原链表头设为它的 next)
- 更新 free_list = pos
⚠️ 注意:必须确保 ptr 确实来自该池,且未重复释放,否则链表会断裂或循环——这是手动内存池需承担的校验责任。
四、实战示例:分配 100 个 struct node(含 next 指针)
假设:
struct node {int data;
struct node* next; // 不用于链表管理,纯业务字段
};
我们让内存池内部用独立的 int free_index[100] 管理空闲链,不污染 node 结构:
- 声明:char pool_buf[100 * sizeof(struct node)]; int free_index[100]; int free_head = 0;
- 初始化:for (int i = 0; i
- alloc():if (free_head == -1) return NULL; struct node* p = (void*)&pool_buf[free_head * sizeof(struct node)]; free_head = free_index[free_head]; return p;
- free(p):int idx = ((char*)p - pool_buf) / sizeof(struct node); free_index[idx] = free_head; free_head = idx;
这样业务代码完全无感,malloc/free 替换为 pool_alloc/pool_free,性能稳定,无外部依赖。

















