五十行代码教你写一个简单的内存池(二级指针的应用)

news2025/2/23 19:36:10

固定大小内存的内存池实现

该内存池是一个很简单的内存池,只能申请固定大小的内存,仅供学习

要点:

  • 构造隐式链表
  • 二级指针

存储结构

typedef struct mempool_s{
    int block_size;  // 每一块的大虚哎
    int free_count;  // 剩余有多少块是可以用的
    char *free_ptr;  // 可以分配的开始位置
    char *mem;       // 指向4k的起始位置
}mempool_t;

开放的API

  • int memp_init(mempool_t * m, int block_size)

    对内存池每一项都赋值,用malloc申请大块内存,让mem指向malloc

    这里面有特殊操作:构建隐式链表

  • void* memp_alloc(mempool_t* m)

    free_ptr链开始分配

  • void* memp_free(mempool_t* m, void *ptr)
    释放给定的内存块,并将它头插在隐式链表中

构建隐式链表

我们会用malloc分配一个4k大小的内存,mem指向这块起始地址,free_ptr指向可以分配的内存的起始地址

每次从内存池中分配一个32字节大小的内存块,于是4k内存就分分割为下图所示

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aE7UcXe-1685176160152)(image\image-20230527153251884.png)]

现在就有一些问题

  • 如何管理些块块内存?

  • 分配时分配哪一个块?

  • 释放了的块怎么回收?

上述这些问题全部都可以用隐式链表来解决

构造隐式链表的思路:

每次我们申请的是32个字节,我们可以将前面8个字节拿来存储next指针,也就是下一个块的地址,这样我们的内存就变成了这样

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjX7wq0L-1685176160153)(image\image-20230527153926626.png)]

这样的话我们就构建了一个隐式链表,每次分配就从头部取,每次挥手就将32B的块头插进这个链表

如何实现

利用二级指针可以很方便的实现隐式链表的构造。

二级指针是一个指向指针的指针

操作

假设有下述4K内存

char * free_ptr = malloc(1024);

每个块的大小为32用变量block_size表示

int block_size = 32;

一共有1024/32个块用free_count表示

int free_count = 1024 / block_size;

使用二级指针

int i = 0;
char *ptr = m->free_ptr;
for(i = 0; i < m->free_count; i++){
    *(char**)ptr = ptr + block_size; 
    ptr += block_size;
}
*(char**)ptr = NULL;

ptr一级指针被强转为了二级指针,ptr本来是指向一个字节(char)的指针,现在变为了指向8个字节的指针,

对于这一*(char**)ptr解引用操作,就能够操作以ptr原本指向的一字节开始的八个字节,就将这8个字节赋予为下一个块的地址ptr + block_size,这里不懂的话最后还会有一个二级指针操作队列尾部的例子。

这里我有必要解释一下一级指针与二级指针以char为例

  • char*一级指针,指向char类型的指针,char类型为一字节,也可以说是指向一个字节的指针
  • char**二级指针,指向char*类型的指针,由于64位下指针类型大小为8个字节所以,也可以说是指向8个字节的指针

经过上述操作,就隐式的构建了链表这样一来内存池内存块的管理就实现了。

  • 分配时从free_ptr中取出
  • 回收时从将内存块插入链表的头部

完整代码实现

#include <stdio.h>
#include <stdlib.h>
// 实现一个分配固定大小块的内存池
// 将4k的内存分割为大小相等的块  比如 32bytes/block  就有128个块
#define MEM_PAGE_SIZE 0X1000
typedef struct mempool_s{
    int block_size;  // 每一块
    int free_count;  // 剩余有多少块 可以用
    char *free_ptr;  // 可以分配的开始位置
    char *mem;       // 指向4k的起始位置
}mempool_t;

初始化内存池块

int memp_init(mempool_t * m, int block_size){  // 初始化4k内存
    if(!m) return -2;
    // 对内存池的每一项赋值
    m->block_size = block_size;
    m->free_count = MEM_PAGE_SIZE / block_size;  // 4k/32
    m->free_ptr = (char*)malloc(MEM_PAGE_SIZE);
    if(!m->free_ptr) return -1;
    m->mem = m->free_ptr;

    // 构建了隐式链表
    int i = 0;
    char *ptr = m->free_ptr;
    for(i = 0; i < m->free_count; i++){
        *(char**)ptr = ptr + block_size; 
        ptr += block_size;
    }
    *(char**)ptr = NULL;
    return 0;
}

分配内存块

// 以free_ptr链开始分配
void* memp_alloc(mempool_t* m){  // 已经确定了固定大小  不需要传入大小
    if(!m || m->free_count == 0) return NULL;
    void *ptr = m->free_ptr;
    m->free_ptr = *(char**)ptr;  // 后移指向下一个空闲处
    m->free_count--;

    return ptr;
}

回收内存块

void* memp_free(mempool_t* m, void *ptr){  // 
    // 将空闲块头插 进空闲列表
    *(char**)ptr = m->free_ptr;
    void * old_free = (void*)m->free_ptr;
    m->free_ptr = (char*)ptr;

    m->free_count++;

    return old_free;
}

测试

int main(){
    mempool_t m;
    int i = memp_init(&m, 32);
    printf("%d\n", i);
    void* p1 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p1);
    void* p2 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p2);
    void* p3 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p3);
    void* p4 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p4);
    memp_free(&m, p1);
    memp_free(&m, p3);
    void* p5 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p5);
    void* p6 = memp_alloc(&m);
    printf("memp_alloc : %p\n", p6);
    
}

打印

memp_alloc : 0x5624aa9cc260 # p1
memp_alloc : 0x5624aa9cc280 # p2
memp_alloc : 0x5624aa9cc2a0 # p3
memp_alloc : 0x5624aa9cc2c0 # p4
memp_alloc : 0x5624aa9cc2a0 # p5
memp_alloc : 0x5624aa9cc260 # p6

这个内存池存在的问题

可以明显的看见,我们分配的32个字节的内存其中将前面8个字节用来构造了隐式链表,所以实际能用的内存只有32-8,并且如果你分配的块的大小小于8,则上述程序会出现段错误

二级指针操作自定义类型

自定义类型二级指针的应用

假设有下列结构

typedef struct task_s {
    task_t *next;  // 必须是第一个字段
    handler_pt func;
    void *arg;
} task_t;

初始化

task_t *a = malloc(sizeof(task_t));

二级指针操作

*(task_t**)a = NULL;  // 等价于 a->next = NULL;

解释:a本来是一个task_t类型的指针,由于task_t24个字节,也可以说a是指向24个字节的指针,由于a被转为了二级指针,意思就是现在a是一个指向8个字节的指针,该8个字节就对应task_t中前8个字节也就是next指针,与是解引用就是相当于解了next指针的引用

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

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

相关文章

C#医院LIS系统源码 LIS实验室管理信息系统源码 LIS检验系统源码

1、LIS系统技术框架 &#xff08;1&#xff09;总体框架&#xff1a; SaaS架构的Client/Server应用 服务可伸缩&#xff0c;多服务协同 服务可拆分&#xff0c;功能易扩展 &#xff08;2&#xff09;技术细节&#xff1a; 体系结构&#xff1a;Client/Server架构 客户端…

资深SRE带你看阿里云香港故障

一、故障背景 12月18日阿里云香港Region发生重大故障&#xff0c;多个重要互联网服务受到影响&#xff0c;包括澳门日报、金融管理局、澳门银河、莲花卫视、澳门水泥厂等基础服务&#xff0c;澳觅和MFood等外卖平台&#xff0c;多个区块链交易所也受到影响。详情见官方故障报告…

第12课【DMA 直接数据访问】直接数据访问 效率 数据总线冲突 通道 仲裁器

目录 简介功能框图请求通道仲裁器 使用配置传输方向数据量传输模式 实例分析存储器间传输存储器到外设 简介 DMA&#xff08;Direct Memory Access 直接内存访问&#xff09;指的是STM32中的一个外设。它可以在无需CPU介入的情况下&#xff0c;实现外设和存储器之间或存储器与…

毕业季,给大家用python画一个飞机吧~预祝大家一帆风顺

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 今天我们主要用到turtle这个模块&#xff0c;它是python自带的&#xff0c; 所以无需安装&#xff0c;我们直接导入使用就好了~命令如下&#xff1a; import turtle那这个模块有什么作用呢&#xff1f; 它是python中…

LeetCode刷题(ACM模式)-02链表

参考引用&#xff1a;代码随想录 注&#xff1a;每道 LeetCode 题目都使用 ACM 代码模式&#xff0c;可直接在本地运行&#xff0c;蓝色字体为题目超链接 0. 链表理论基础 0.1 链表定义 链表是一种通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff1a…

01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集

学算法好痛苦&#xff0c;完全是对我智力的一次次折磨&#xff0c;看了好多博客&#xff0c;对二维dp数组的理解都是直接搬了代码随想录&#xff0c;搬了随想录又没详细解释&#xff0c;大家都是一眼看懂的吗&#xff0c;好吧&#xff08;&#xff09; LeetCode 416. 分割等和子…

华侨大学主题模版

华侨大学主题模版 适用于 课程作业、实验报告、项目策划、毕业论文、毕设答辩... 这是一个非官方项目&#xff0c;仅供个人学习研究。 &#x1f680; 指南 &#x1f4d6; 仓库简介 该仓库提供了一系列适用于课程作业、实验报告、项目策划、毕业论文、毕设答辩等场景的华侨大…

API接口对电商平台有怎样的帮助?如何获取API接口?

API&#xff08;Application Programming Interface&#xff09;接口对电商平台有很多帮助&#xff0c;下面列举几点&#xff1a; 1.提高效率 API接口可以让电商平台的不同模块之间进行快速的数据交互&#xff0c;帮助平台实现更高效的业务处理。比如&#xff0c;在订单管理和…

牛客网学习之倒置字符串(详解fgets函数,如何读取含有空格的字符串)

题目要求&#xff1a; 过程分析&#xff1a;&#xff08;大致思路&#xff09; 具体代码&#xff1a; #include <stdio.h> #include <string.h> /*逆序字符串*/ void reverse(char* begin,char* end) {while (begin < end){char temp *begin;*begin *end;*en…

程序员的出路是什么?

做为一个程序员&#xff0c;工作了一定时间后&#xff0c;一定会有一个想法&#xff0c;那就是程序员能干多久&#xff0c;未来的发展方向是什么&#xff0c;更专业一点的说法是&#xff0c;职业规划到底是什么。 说实话&#xff0c; 我也仔细考虑过这个问题&#xff0c;偶尔也…

基于 HTTP Range 实现文件分片并发下载!

目录 前言 基础下载功能 进阶下载功能 单片下载 多片下载 浏览器发送预检&#xff08;preflight&#xff09;请求 express 不支持多段 range multipart/** 搭配 boundary** 分片下载功能 “只读的” ArrayBuffer 对象 DataView 子类 Uint8Array 操作二进制数据 Bl…

不愧是腾讯出来的,太厉害了...

前段时间公司缺人&#xff0c;也面了许多测试&#xff0c;一开始瞄准的就是中级水准&#xff0c;当然也没指望能来大牛&#xff0c;提供的薪资在15-20k这个范围&#xff0c;来面试的人有很多&#xff0c;但是平均水平真的让人很失望。看了简历很多上面都是写有4年工作经验&…

Go对Json的支持[encoding/json]

数据类型中转json的函数 基本数据转json Marshal使用以下与类型相关的默认编码: 布尔值编码为JSON布尔值。 浮点数、整数和数字值编码为JSON数字。 字符串值编码为JSON字符串&#xff0c;强制为有效的UTF-8; 用Unicode替换符文替换无效字节。 这样JSON就可以安全地嵌入到HTML …

2023年5月软考考试真题及答案汇总

2023上半年软考考试在5月27日-5月28日举行&#xff0c;为大家整理了各科目的考试真题及答案&#xff0c;可用于估分。&#xff08;会持续更新哦&#xff09; ​ 2023年上半年系统集成项目管理工程师上午题 1、在&#xff08; &#xff09;领域&#xff0c;我国还远未达到世界…

【数据结构】---几分钟简单几步学会手撕链式二叉树(中)

文章目录 前言&#x1f31f;一、二叉树链式结构的实现&#x1f30f;1.1 二叉树叶子节点个数&#x1f4ab;代码&#xff1a;&#x1f4ab;流程图&#xff1a; &#x1f30f;1.2 二叉树的高度&#x1f4ab;第一种写法(不支持)&#xff1a;&#x1f4d2;代码&#xff1a;&#x1f…

TCP协议的三次握手和四次挥手

完整的TCP内容&#xff0c;请参考RFC 9293 TCP协议为应用提供可靠的、有序的的字节流服务。TCP是面向连接的&#xff0c;提供了全双工的通信。TCP使用端口号来识别应用程序服务并在主机之间复用不同的流。 TCP header格式 TCP header也像IP header一样&#xff0c;在header中…

【Kafka】超详细介绍

文章目录 概念部署方案磁盘网络CPUpartition的数量 命令查看版本找kafka和zookeeper的ip/porttopic创建 topic查看get topic 列表get topic 详情 修改topic修改分区级别参数(如增加partition) 删除topic设置消息大小上限 生产查看生产生产消息 查看消费server 查看 offset查看积…

车载电子之汽车HUD技术

一、什么是HUD技术 HUD&#xff08;head up display抬头显示器&#xff09;是将重要信息显示在挡风玻璃上的一种显示系统&#xff0c;基本原理是&#xff1a;投影仪发出的光信息&#xff0c;经过一系列的折射、反射等投影到挡风玻璃上&#xff0c;人眼就能看到投射在上面的信息…

springboot+vue广场舞团系统(java项目源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的广场团舞系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

Jmeter接口测试-websocket测试

壹 Jmeter接口测试-websocket测试 测试之前的准备工作,需要websocket插件 方式一: 去github下载: https://github.com/maciejzaleski/JMeter-WebSocketSampler/wiki/Dependencies jetty-http-9.1.2.v20140210.jarjetty-io-9.1.2.v20140210.jarjetty-util-9.1.2.v20140210…