C语言内存池实现-对齐、重用、双向链表管理

概述

        在项目上,经常遇到芯片内存不够导致编码困扰问题,在此写个笔录,方便后续查阅。
此示例,考虑了一些额外的功能和边界条件,例如内存分配的对齐、内存池的重用等。这个示例使用了双向链表来管理内存块。

源码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>// 内存块结构
typedef struct MemoryBlock {size_t size;                // 内存块大小struct MemoryBlock* prev;   // 前一个内存块的指针struct MemoryBlock* next;   // 下一个内存块的指针
} MemoryBlock;// 内存池结构
typedef struct MemoryPool {size_t totalSize;           // 内存池总大小MemoryBlock* firstBlock;    // 内存池中第一个内存块的指针
} MemoryPool;// 初始化内存池
void memoryPoolInit(MemoryPool* pool, size_t totalSize) 
{pool->totalSize = totalSize;pool->firstBlock = (MemoryBlock*)malloc(totalSize);if (pool->firstBlock == NULL) {fprintf(stderr, "Failed to allocate memory for memory pool\n");exit(EXIT_FAILURE);}pool->firstBlock->size = totalSize - sizeof(MemoryBlock);pool->firstBlock->prev = NULL;pool->firstBlock->next = NULL;
}// 从内存池中分配内存
void* memoryPoolAllocate(MemoryPool* pool, size_t size) 
{MemoryBlock* currentBlock = pool->firstBlock;while (currentBlock != NULL) {// 计算对齐后的内存块大小size_t alignedSize = (size + sizeof(MemoryBlock) - 1) / sizeof(MemoryBlock) * sizeof(MemoryBlock);// 查找第一个满足大小的内存块if (currentBlock->size >= alignedSize) {// 如果内存块过大,将剩余部分分割成新的内存块if (currentBlock->size > alignedSize + sizeof(MemoryBlock)) {MemoryBlock* newBlock = (MemoryBlock*)((uint8_t*)currentBlock + alignedSize + sizeof(MemoryBlock));newBlock->size = currentBlock->size - alignedSize - sizeof(MemoryBlock);newBlock->prev = currentBlock;newBlock->next = currentBlock->next;if (currentBlock->next != NULL) {currentBlock->next->prev = newBlock;}currentBlock->next = newBlock;}// 更新当前内存块的大小currentBlock->size = alignedSize;// 返回分配的内存块地址return (void*)((uint8_t*)currentBlock + sizeof(MemoryBlock));}currentBlock = currentBlock->next;}// 没有合适大小的内存块可供分配return NULL;
}// 释放内存池中的内存
void memoryPoolFree(MemoryPool* pool, void* ptr) 
{if (ptr == NULL) {return;}MemoryBlock* block = (MemoryBlock*)((uint8_t*)ptr - sizeof(MemoryBlock));// 合并相邻的空闲内存块if (block->prev != NULL && block->prev->size > 0) {block->prev->next = block->next;block->prev->size += block->size + sizeof(MemoryBlock);block = block->prev;}if (block->next != NULL && block->next->size > 0) {block->size += block->next->size + sizeof(MemoryBlock);block->next = block->next->next;if (block->next != NULL) {block->next->prev = block;}}
}// 释放内存池
void memoryPoolDestroy(MemoryPool* pool)
{free(pool->firstBlock);pool->firstBlock = NULL;pool->totalSize = 0;
}int main(void) 
{MemoryPool pool;size_t totalSize = 1024;memoryPoolInit(&pool, totalSize);// 使用内存池分配和释放内存void* ptr1 = memoryPoolAllocate(&pool, 128);void* ptr2 = memoryPoolAllocate(&pool, 256);memoryPoolFree(&pool, ptr1);void* ptr3 = memoryPoolAllocate(&pool, 64);// 在程序结束时释放内存池memoryPoolDestroy(&pool);return 0;
}

科普知识

在计算机编程中还有其他类型的池,它们用于管理不同类型的资源。以下是一些常见的池类型:

  1. 对象池(Object Pool):用于管理对象的池。对象池预先分配一组对象,并在需要时分配和回收对象,以提高性能。对象池通常用于管理具有复杂构造函数或频繁创建销毁的对象,例如线程池中的工作线程对象。

  2. 连接池(Connection Pool):用于管理数据库连接、网络连接或其他类型的资源连接的池。连接池预先创建一组连接,以减少连接的创建和销毁开销,并提高系统的响应速度和性能。

  3. 线程池(Thread Pool):用于管理线程的池。线程池预先创建一组线程,并在需要时分配任务给空闲线程执行,以减少线程创建和销毁的开销,并提高系统的并发性能。

  4. 缓冲池(Buffer Pool):用于管理缓冲区的池。缓冲池预先分配一组缓冲区,并在需要时分配给请求者,以减少内存分配和释放的开销,并提高系统的内存使用效率。

  5. 资源池(Resource Pool):用于管理各种类型的资源的池。资源池可以是一个通用的池,用于管理不同类型的资源,例如文件句柄、内存块、套接字等。资源池可以帮助减少资源的创建和销毁开销,提高系统的性能和可伸缩性。

这些池类型都遵循相似的原理,即预先分配一定数量的资源,并在需要时分配给请求者使用,以减少资源的创建和销毁开销,并提高系统的性能和可伸缩性。具体选择何种类型的池取决于应用程序的需求和场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/722260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL】视图、索引

目录 视图视图的用途优点视图的缺点创建视图查看视图修改视图删除视图注意事项 索引索引的原理索引的数据结构二分查找法Hash结构Hash冲突&#xff01;&#xff01;&#xff01; B树二叉查找树 存在问题改造二叉树——B树降低树的高度 B树特点案例继续优化的方向 改造B树——B树…

Java反射,动态代理。笔记

1.pathClass Loader 和 Dex ClassLoader 在Android 5.0以下的版本中,两者之间的区别为: DexClassLoader:可加载jar、apk和dex」可以从SD卡中加载PathClassLoader:只能加载已安裝到系統中(即/data/app目录下)的apk文件但是随着Android版本的升级,到Android …

ip https证书360元买一年送一月

随着互联网的发展&#xff0c;不论是用户还是开发者&#xff0c;都越来越重视互联网环境的安全性。IP https证书是一种网络安全协议&#xff0c;用于保护网络通信的安全性和机密性。IP https数字证书是CA认证机构为只有公网IP地址&#xff0c;没有域名的站点颁发的数字证书&…

构建信息蓝图:概念模型与E-R图的技术解析

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua&#xff0c;在这里我会分享我的知识和经验。&#x…

【JavaEE进阶】CSS选择器的常见用法

CSS选择器的主要功能就是选中页面指定的标签元素&#xff0c;选中了元素&#xff0c;才可以设置元素的属性。 CSS选择器主要有以下几种: 标签选择器类选择器id选择器复合选择器通配符选择器 接下来用代码来学习这几个选择器的使用。 <!DOCTYPE html> <html lang&q…

【Algorithms 4】算法(第4版)学习笔记 15 - 4.1 无向图

文章目录 前言参考目录学习笔记1&#xff1a;图表介绍1.1&#xff1a;定义1.2&#xff1a;常见应用1.3&#xff1a;术语1.4&#xff1a;一些图表处理问题2&#xff1a;图表 API2.1&#xff1a;图的表示2.2&#xff1a;无向图 API2.3&#xff1a;典型图处理代码2.4&#xff1a;图…

基于Apifox实现javaweb的数据响应与请求

前言 之前文章已经写过了怎么基于springboat以及maven创建javaweb项目&#xff0c;这里就不在讲述了 可以看看我之前的文章&#xff0c;前一篇发布的javaweb的数据请求与响应&#xff0c;下面具体介绍怎么基于 Apifox实现javaweb的数据响应与请求&#xff0c;顺便给大家介绍…

全网最全 Linux 命令总结,建议收藏!

1、基本命令 uname -m 显示机器的处理器架构 uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作系统信息 arch 显示机器的处理器架构 uname -m 显示机…

类与对象(二)--类的六个默认成员函数超详细讲解

目录 1.类的默认六个成员函数✒️ 2.构造函数 2.1构造函数的概念✒️ 2.2构造函数的特性✒️ 3.析构函数 3.1析构函数的概念✒️ 3.2析构函数的特征✒️ 4.拷贝构造函数 4.1拷贝构造函数的概念✒️ 4.2拷贝构造函数的特征✒️ 4.3思考❓ 4.4深拷贝和浅拷贝⭐️…

UE5 C++ TPS开发 学习记录(九

p20 首先我们现在有一个多人游戏的系统类MultiplayerSessionsSubsystem 在这个系统内提供了很多会话系统的接口SessionInterface 当现在我们有一些SessionInterfaceDelegates的委托,这个委托的来源是SessionInterface,所以我们使用的委托可以接收到来自SessionInterface的消息(…

网络学习:MPLS标签与标签分配协议—LDP

目录 前言&#xff1a; 一、MPLS标签 1、定义&#xff1a; 2、标签结构&#xff1a; 3、标签识别&#xff1a; 二、标签分配协议---LDP&#xff08;Lable Distribution Protocol&#xff09; 1、定义&#xff1a; 2、标签分配协议的种类&#xff1a; 3、LDP消息类型 …

回溯五题【Leetcode17数独/37组合问题/51N皇后/212字典树/980状态压缩】

文章目录 关于回溯37. 解数独&#xff08;37.sudoku-solver&#xff09;17. 电话号码的数字组合&#xff08;17.letter-combinations-of-a-phone-number&#xff09;51. N皇后&#xff08;51.n-queens&#xff09;212. 单词搜索 II&#xff08;212.word-search-ii&#xff09;简…

K次取反后最大化的数组和 加油站 分发糖果 柠檬水找零

1005.K次取反后最大化的数组和 力扣题目链接(opens new window) 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#xff1a;我们选择某个索引 i 并将 A[i] 替换为 -A[i]&#xff0c;然后总共重复这个过程 K 次。&#xff08;我们可以多次选择同一个索引 i。&a…

安装算法依赖时版本报错,依赖之间对应版本

困惑了很久&#xff0c;毕竟不是计算机专业专业出身&#xff0c;才知道安装深度学习算法各个依赖之间是有版本对应关系的。 &#xff08;本文使我随笔记录&#xff0c;无价值&#xff09; 比如&#xff1a; 再比如&#xff1a; 由于我第一步安装cuda时就和其他博主不一致&…

Vue基础入门(2)- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程

Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程 文章目录 Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程5 生命周期5.1 Vue生命周期钩子5.2 在creat…

docker pull 拉取失败,设置docker国内镜像

遇到的问题 最近在拉取nginx时&#xff0c;显示如下错误&#xff1a;Error response from daemon: Get “https://registry-1.docker.io/v2/”: net/http: request canceled (Client.Timeout exceeded while awaiting headers)。 这个的问题是拉取镜像超时&#xff0c;通过检索…

c语言经典测试题11

1.题1 #include <stdio.h> int main() { int a[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, *p a 5, *q NULL; *q *(p5); printf("%d %d\n", *p, *q); return 0; }上述代码的运行结果是什么呢&#xff1f; 我们来分析一下&#xff1a;我们创建了一个数…

第1题:两数之和

题目内容&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。…

数据持久层框架:MyBatis-Plus

数据持久层框架&#xff1a;MyBatis-Plus 前言注解代码生成器CURD接口Service CRUD 接口Mapper CRUD 接口 条件构造器QueryWrapper和UpdateWrapperallEqeq、negt、ge、lt、lebetween、notBetweenlike、notLike、likeLeft、likeRight、notLikeLeft、notLikeRightisNull、isNotNu…

推荐收藏!这是我见过计算机视觉图像算法最全面经了

节前&#xff0c;我们组织了一场算法岗技术&面试讨论会&#xff0c;邀请了一些互联网大厂朋友、参加社招和校招面试的同学&#xff0c;针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 今天我整…