首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >源码+设计文档 | C语言实现内存池管理器

源码+设计文档 | C语言实现内存池管理器

作者头像
C语言中文社区
发布2025-07-02 10:56:55
发布2025-07-02 10:56:55
20900
代码可运行
举报
文章被收录于专栏:C语言中文社区C语言中文社区
运行总次数:0
代码可运行

正文

文末附完整源代码

1. 引言

本内存池管理器旨在提供一种高效的内存管理机制,通过预分配固定大小的内存块,避免频繁的内存分配和释放操作,减少内存碎片,提升程序性能。适用于需要高频次分配/释放相同大小内存块的场景,如网络服务器、嵌入式系统等。


2. 设计目标

  • 高效分配与释放:实现 O(1) 时间复杂度的 mallocfree 操作。
  • 批量处理:支持快速分配和释放多个内存块。
  • 内存零碎片:通过固定大小内存块管理,避免内存碎片。
  • 轻量级:最小化内存开销,仅占用必要的管理空间。

3. 数据结构设计

3.1 内存池结构体 MemoryPool

代码语言:javascript
代码运行次数:0
运行
复制
typedef struct MemoryPool {
    size_t block_size;     // 每个内存块的总大小(含管理头)
    size_t total_blocks;   // 内存池总块数
    size_t free_count;     // 当前空闲块数
    void* free_list;       // 空闲链表头指针
    char data[];           // 柔性数组,存储实际内存块
} MemoryPool;

3.2 内存块布局

每个内存块前 sizeof(void*) 字节用于存储链表指针(next),用户可用内存从 block + sizeof(void*) 开始,大小为 block_size - sizeof(void*)


4. 核心函数设计

4.1 内存池创建

代码语言:javascript
代码运行次数:0
运行
复制
MemoryPool* create_memory_pool(size_t user_block_size, size_t num_blocks);
  • 功能:创建一个内存池,预分配 num_blocks 个大小为 user_block_size 的内存块。
  • 参数
    • user_block_size:用户请求的内存块大小(需满足 user_block_size + sizeof(void*) <= block_size)。
    • num_blocks:内存池中内存块的总数。
  • 返回值:成功返回内存池指针,失败返回 NULL

4.2 内存分配

代码语言:javascript
代码运行次数:0
运行
复制
void* mem_pool_alloc(MemoryPool* pool);
  • 功能:从内存池中分配一个内存块。
  • 参数pool 指向内存池的指针。
  • 返回值:成功返回指向内存块的指针,失败返回 NULL

4.3 内存释放

代码语言:javascript
代码运行次数:0
运行
复制
void mem_pool_free(MemoryPool* pool, void* ptr);
  • 功能:将内存块释放回内存池。
  • 参数
    • pool 指向内存池的指针。
    • ptr 要释放的内存块指针(必须来自 mem_pool_alloc)。

4.4 内存池重置

代码语言:javascript
代码运行次数:0
运行
复制
void mem_pool_reset(MemoryPool* pool);
  • 功能:将所有已分配的内存块重新标记为空闲,无需逐个释放。
  • 参数pool 指向内存池的指针。

4.5 内存池销毁

代码语言:javascript
代码运行次数:0
运行
复制
void destroy_memory_pool(MemoryPool* pool);
  • 功能:释放整个内存池占用的内存。
  • 参数pool 指向内存池的指针。

5. 内存管理机制

5.1 空闲链表初始化

create_memory_pool 中,将预分配的内存块逐个连接成链表。每个块的前 sizeof(void*) 字节存储下一个块的地址。

代码语言:javascript
代码运行次数:0
运行
复制
for (size_t i = 0; i < num_blocks; ++i) {
    void* block = pool->data + i * pool->block_size;
    *((void**)block) = pool->free_list;
    pool->free_list = block;
}

5.2 分配流程

  1. 检查空闲链表是否非空。
  2. 取出链表头节点 block
  3. 更新链表头为 block->next
  4. 返回 block + sizeof(void*) 作为用户内存指针。

5.3 释放流程

  1. 计算原始块地址:block = ptr - sizeof(void*)
  2. 将块插入空闲链表头部。
  3. 更新链表头为当前块。

5.4 批量释放(重置)

mem_pool_reset 重新初始化空闲链表,将所有内存块重新链接到链表中,实现快速批量释放。


6. 使用限制

  • 块大小限制user_block_size 必须满足 user_block_size >= sizeof(void*),否则无法存储链表指针。
  • 释放限制:只能释放通过 mem_pool_alloc 分配的内存,跨池释放会导致未定义行为。
  • 线程安全:当前实现非线程安全,多线程环境下需自行加锁。

7. 性能分析

操作

时间复杂度

说明

mem_pool_alloc

O(1)

直接取出链表头节点

mem_pool_free

O(1)

插入链表头部

mem_pool_reset

O(n)

重建链表,n 为总块数

destroy_pool

O(1)

释放整个内存池

内存池通过固定大小分配和链表管理,确保无内存碎片,适合高频次分配/释放场景。


8. 测试结果

代码语言:javascript
代码运行次数:0
运行
复制
D:\CODING\cyyzwsq\MemoryPool\cmake-build-debug\MemoryPool.exe
=== 内存池测试 ===
Allocated block 0 at 000001fd2ede1940
Allocated block 1 at 000001fd2ede18b8
Allocated block 2 at 000001fd2ede1830
Allocated block 3 at 000001fd2ede17a8
Allocated block 4 at 000001fd2ede1720
Freeing block 0
Resetting memory pool
Allocated block 0 at 000001fd2ede1940
Allocated block 1 at 000001fd2ede18b8
Allocated block 2 at 000001fd2ede1830
Allocated block 3 at 000001fd2ede17a8
Allocated block 4 at 000001fd2ede1720
Destroying memory pool
=== 测试结束 ===

进程已结束,退出代码为 0

9. 完整代码

公众号分享的项目源码已上传至git仓库: https://gitee.com/cyyzwsq/C-Coding.git

代码语言:javascript
代码运行次数:0
运行
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 内存池结构体
typedefstruct MemoryPool {
    size_t block_size;     // 每个内存块的总大小(含管理头)
    size_t total_blocks;   // 内存池总块数
    size_t free_count;     // 当前空闲块数
    void* free_list;       // 空闲链表头指针
    char data[];           // 柔性数组,存储实际内存块
} MemoryPool;

/**
 * 创建内存池
 * @param user_block_size 用户请求的内存块大小
 * @param num_blocks      内存池中内存块的总数
 * @return 成功返回内存池指针,失败返回 NULL
 */
MemoryPool* create_memory_pool(size_t user_block_size, size_t num_blocks) {
    // 检查参数合法性
    if (user_block_size == 0 || num_blocks == 0) {
        returnNULL;
    }

    // 每个块至少要能容纳一个指针(用于空闲链表)
    size_t actual_block_size = user_block_size + sizeof(void*);
    size_t pool_size = sizeof(MemoryPool) + actual_block_size * num_blocks;

    // 分配内存池结构体及内存块空间
    MemoryPool* pool = (MemoryPool*)malloc(pool_size);
    if (!pool) {
        returnNULL;
    }

    // 初始化内存池字段
    pool->block_size = actual_block_size;
    pool->total_blocks = num_blocks;
    pool->free_count = num_blocks;
    pool->free_list = NULL;

    // 初始化空闲链表
    for (size_t i = 0; i < num_blocks; ++i) {
        void* block = pool->data + i * actual_block_size;
        *((void**)block) = pool->free_list;
        pool->free_list = block;
    }

    return pool;
}

/**
 * 从内存池中分配一个内存块
 * @param pool 内存池指针
 * @return 成功返回内存块指针,失败返回 NULL
 */
void* mem_pool_alloc(MemoryPool* pool) {
    if (!pool || !pool->free_list || pool->free_count == 0) {
        returnNULL;
    }

    void* block = pool->free_list;
    pool->free_list = *((void**)block);
    pool->free_count--;

    // 返回用户可用内存起始地址(跳过链表指针)
    return (char*)block + sizeof(void*);
}

/**
 * 将内存块释放回内存池
 * @param pool 内存池指针
 * @param ptr  要释放的内存块指针(必须来自 mem_pool_alloc)
 */
void mem_pool_free(MemoryPool* pool, void* ptr) {
    if (!pool || !ptr) {
        return;
    }

    // 计算原始块地址(回退 sizeof(void*) 字节)
    void* block = (char*)ptr - sizeof(void*);

    // 插入空闲链表头部
    *((void**)block) = pool->free_list;
    pool->free_list = block;
    pool->free_count++;
}

/**
 * 重置内存池,将所有内存块重新标记为空闲
 * @param pool 内存池指针
 */
void mem_pool_reset(MemoryPool* pool) {
    if (!pool) {
        return;
    }

    // 重新构建空闲链表
    pool->free_count = pool->total_blocks;
    pool->free_list = NULL;

    for (size_t i = 0; i < pool->total_blocks; ++i) {
        void* block = pool->data + i * pool->block_size;
        *((void**)block) = pool->free_list;
        pool->free_list = block;
    }
}

/**
 * 销毁内存池
 * @param pool 内存池指针
 */
void destroy_memory_pool(MemoryPool* pool) {
    if (pool) {
        free(pool);
    }
}

// =============================
// 测试用例
// =============================

int main() {
    printf("=== 内存池测试 ===\n");

    // 创建内存池:每个块大小为 128 字节,共 10 个块
    MemoryPool* pool = create_memory_pool(128, 10);
    if (!pool) {
        printf("Failed to create memory pool\n");
        return-1;
    }

    // 分配 5 个内存块
    void* blocks[5];
    for (int i = 0; i < 5; ++i) {
        blocks[i] = mem_pool_alloc(pool);
        if (blocks[i]) {
            printf("Allocated block %d at %p\n", i, blocks[i]);
        } else {
            printf("Allocation failed for block %d\n", i);
        }
    }

    // 释放第一个块
    printf("Freeing block 0\n");
    mem_pool_free(pool, blocks[0]);

    // 重置内存池(释放所有块)
    printf("Resetting memory pool\n");
    mem_pool_reset(pool);

    // 再次分配 5 个块
    for (int i = 0; i < 5; ++i) {
        blocks[i] = mem_pool_alloc(pool);
        if (blocks[i]) {
            printf("Allocated block %d at %p\n", i, blocks[i]);
        } else {
            printf("Allocation failed for block %d\n", i);
        }
    }

    // 销毁内存池
    printf("Destroying memory pool\n");
    destroy_memory_pool(pool);

    printf("=== 测试结束 ===\n");
    return0;
}

9. 总结

本内存池管理器通过固定大小内存块和空闲链表管理,实现了高效的内存分配与释放。适用于需要高性能、低延迟的场景,且能有效避免内存碎片问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 C语言中文社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正文
    • 1. 引言
    • 2. 设计目标
    • 3. 数据结构设计
      • 3.1 内存池结构体 MemoryPool
      • 3.2 内存块布局
    • 4. 核心函数设计
      • 4.1 内存池创建
      • 4.2 内存分配
      • 4.3 内存释放
      • 4.4 内存池重置
      • 4.5 内存池销毁
    • 5. 内存管理机制
      • 5.1 空闲链表初始化
      • 5.2 分配流程
      • 5.3 释放流程
      • 5.4 批量释放(重置)
    • 6. 使用限制
    • 7. 性能分析
    • 8. 测试结果
    • 9. 完整代码
    • 公众号分享的项目源码已上传至git仓库: https://gitee.com/cyyzwsq/C-Coding.git
    • 9. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档