动态内存分配(malloc/free)简单实现--隐式空闲链表

2023-05-16

本文使用隐式空闲链表实现简单的动态内存分配。

动态内存分配器维护一个大块区域,也就是堆,处理动态的内存分配请求。分配器将堆视为一组不同大小的块的集合来维护,每个块要么是已分配的,要么是空闲的。

实现动态内存分配要考虑以下问题:

(1)空闲块组织:我们如何记录空闲块?

(2)放置:我们如何选择一个合适的空闲块来放置一个新分配的块?

(3)分割:在我们将一个新分配的块放置到某个空闲块之后,我们如何处理这个空闲块中的剩余部分?

(4)合并:我们如何处理一个刚刚被释放的块?

任何分配器都需要一些开销,需要数据结构来记录信息,区分块边界,区分已分配块和空闲块等。大多数实现方式都把信息放在块本身内部。隐式空闲链表就是通过每个块的头部中存放的信息可以方便的定位到下一个块的位置。头部一般就是本块的大小及使用情况(分配或空闲)。

本块的起始地址加上本块的大小就是下一个块的起始地址。

本文使用的控制块结构如下:

struct mem_block
{
	int size;     // 本块的大小,包括控制结构
	int is_free;  // 使用情况,1为空闲,0为已分配
}

为了内存对齐,这里is_free也是用4字节的int存储。其实控制信息根本不需要这么多,此处为了方便理解。

下面是一个块的表示图


返回给用户的区域并不包含控制信息。

当接收到一个内存分配请求时,从头开始遍历堆,找到一个空闲的满足大小要求的块,若有剩余,将剩余部分变成一个新的空闲块,更新相关块的控制信息。调整起始位置,返回给用户。

释放内存时,仅需把使用情况标记为空闲即可。

隐式空闲链表的优点是简单。显著的缺点是任何操作的开销,例如放置分配的块,要求空闲链表的搜索与堆中已分配块和空闲块的总数呈线性关系。

搜索可以满足请求的空闲块时,常见的策略有以下几种:

(1)首次适应法(First Fit):选择第一个满足要求的空闲块

(2)最佳适应法(Best Fit):选择满足要求的,且大小最小的空闲块

(3)最坏适应法(Worst Fit):选择最大的空闲块

(4)循环首次适应法(Next Fit):从上次分配位置开始找到第一个满足要求的空闲块

这里不对各种策略的优劣进行比较了。

当找不到满足请求的空闲块时,并不代表就此失败了,如,我们申请50个字节大小的块,没有找到满足要求的,但可能存在存在两个相邻的块都是空闲,且每个块的大小是30字节,这种情况我们应该能够处理。即合并空闲块问题。有两种策略,一个是立即合并,另一个是推迟合并。本文实现的是推迟合并,立即合并需要同时知道前后两个块的信息,需要额外的一些数据结构,大同小异。

下面是实现代码及测试代码:

#include<stdio.h>
#include<malloc.h>

// 内存对齐,至少应该是mem_block的大小,而且应该是4的整数倍
#define ALIGNMENT 8 

// 初始化堆的大小
#define HEAP_SIZE 10000

// 控制信息结构体
struct mem_block
{
	int size;    // 本块的大小
	int is_free; // 是否已分配
};
typedef struct mem_block mem_block;

// 堆的起始地址和结束地址
void *g_heap_start = 0;
void *g_heap_end = 0;

bool g_heap_inited = false;

// 初始化堆
void init_simple_malloc()
{
	g_heap_inited = true;
	g_heap_start = malloc(HEAP_SIZE);
	if(g_heap_start == 0)
		return;
	mem_block* pos = (mem_block*)g_heap_start;
	pos->size = HEAP_SIZE;
	pos->is_free = 1;
	g_heap_end = (void*)((char*)g_heap_start+HEAP_SIZE-1);

}

// 内部使用的分配内存函数
void *_simple_malloc(size_t size)
{
	if(g_heap_start == 0) 
		return 0;
	// 调整内存大小,满足对齐要求
	size = (size+ALIGNMENT-1) & (~(ALIGNMENT-1));
	mem_block *pos = (mem_block*)g_heap_start;
	while((void*)pos < g_heap_end)
	{
		// 最先适应法
		if(pos->is_free && pos->size >= sizeof(mem_block)+size)
		{
			if(pos->is_free == sizeof(mem_block)+size)
				pos->is_free = 0;
			else
			{
				// 取出需要的大小,剩下的成为堆中的一个新块
				mem_block *next_pos = (mem_block*)((char*)pos+sizeof(mem_block)+size);
				next_pos->is_free = 1;
				next_pos->size = pos->size-sizeof(mem_block)-size;
				pos->is_free = 0;
				pos->size = sizeof(mem_block)+size;
			}
			return (void*)((char*)pos+sizeof(mem_block));
		}
		else
			pos = (mem_block*)((char*)pos+pos->size);
	}
	return 0;
}

// 内部使用的合并空闲块函数
void _merge_free_blocks()
{
	mem_block *pos = (mem_block*)g_heap_start;
	while((void*)((char*)pos+pos->size) < g_heap_end)
	{
		mem_block *next_pos = (mem_block*)((char*)pos+pos->size);
		// 若相邻的两个块都是空闲,合二为一
		if(pos->is_free && next_pos->is_free)
			pos->size = pos->size+next_pos->size;
		else
			pos = next_pos;
	}
	return;
}

// 外部使用的内存分配函数
void *simple_malloc(size_t size)
{
	if(!g_heap_inited)
		init_simple_malloc();
	void * pos = _simple_malloc(size);
	if(pos)
		return pos;
	// 若第一次分配内存失败,则进行合并空闲块,再次尝试分配
	_merge_free_blocks();
	return _simple_malloc(size);
}

// 外部使用的内存释放函数
void simple_free(void *p)
{
	mem_block * pos = (mem_block*)((char*)p-sizeof(mem_block));
	// 释放仅需标记一下
	pos->is_free = 1;
	return;
}

// 测试使用的打印堆信息函数
void print_heap_info()
{
	mem_block *pos = (mem_block*)g_heap_start;
	puts("Heap info:");
	while((void*)pos < g_heap_end)
	{
		// 输出堆中所有控制块的起始地址,大小,使用情况
		printf("mem_block info: start_addr, %d; size, %d; is_free, %d\n", pos, pos->size, pos->is_free);
		pos = (mem_block*)((char*)pos+pos->size);
	}
	putchar('\n');
	return;
}

int main()
{
	void *p1 = simple_malloc(3000);
	// 状态一
	puts("State 1");
	print_heap_info();

	void *p2 = simple_malloc(5000);
	// 状态二
	puts("State 2");
	print_heap_info();

	void *p3 = simple_malloc(1000);
	// 状态三
	puts("State 3");
	print_heap_info();

	simple_free(p1);
	simple_free(p2);
	simple_free(p3);
	// 状态四
	puts("State 4");
	print_heap_info();

	void *p4 = simple_malloc(8000);
	// 状态五
	puts("State 5");
	print_heap_info();

	simple_free(p4);
	// 状态六
	puts("State 6");
	print_heap_info();

	return 0;
}
运行结果:



参考资料:《深入理解计算机系统》,机械工业出版社。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态内存分配(malloc/free)简单实现--隐式空闲链表 的相关文章

  • solvepnp三维位姿估算

    一 前言 关于PNP问题就是指通过世界中的N个特征点与图像成像中的N个像点 xff0c 计算出其投影关系 xff0c 从而获得相机或物体位姿的问题 opencv提供的solvepnp函数就是用来解决pnp问题 利用该函数可以实现测算相机 物
  • emwin自定义颜色

    颜色管理中已经帮助我们定义了这些颜色 xff0c 但是我们通常会使用自定义的颜色 xff0c 怎么怎么设置值呢 xff1f 通常情况下使用的是BGR颜色 就是蓝色和红色是相反的 GUI SetBkColor 0x00FFaa80 自定义调色
  • STemwin 实现滑动切换主页 滑动翻页 滑动解锁功能

    STM32上实现类似iPhone的解锁和滑屏功能 xff0c emwin这个库官方的文档中控件没有一样的 xff0c 但是有一个上下滑动的 xff0c 基本上能够完成大致上的功能 xff0c 但是如果想使用emwin实现类似的效果的话 xf
  • freeRTOS中断简介

    目录 参考材料 中断简介 中断管理简介 优先级分组定义 正点原子freertos手册 优先级设置 用于中断屏蔽的特殊寄存器 primask暂时屏蔽中断寄存器 xff08 RT THREAD使用 xff09 faultmask寄存器 base
  • 【01】初识ThreadX

    目录 简介 微内核 资料链接 入门索引 简介 ThreadX是一个成熟的商用硬实时嵌入式操作系统 xff0c 被广泛应用于消费电子 航空航天 通信 工业控制与医疗等应用领域中 xff0c 至今已服务超过62亿设备 它以轻量级的规模 xff0
  • [解决方案] VNC Viewer 连接灰屏问题 (能够连接上,但全是灰点,没有任何菜单、按钮,鼠标变为x)

    解决方案 VNC Viewer 连接灰屏问题 xff08 能够连接上 xff0c 但全是灰点 xff0c 没有任何菜单 按钮 xff0c 鼠标变为x xff09 情况1情况2情况3 情况1 登陆VNCviewer可能会发现服务器的mate桌
  • VNC Viewer 10061, connection refused

    在Windows系统下用VNC Viewer去连接Linux系统的VNC Server xff0c 双方都可ping通 xff0c 但是VNC Viewer连接不上 xff0c 显示connection refused 10061 xff0
  • 现代C++语言(C++11/14/17)特性总结和使用建议(一)

    C 43 43 语言在历史上经过了很多次的演进 最早的时候 xff0c C 43 43 语言没有模板 STL 异常等特性 xff0c 之后加入这些特性形成大多数人所熟悉的C 43 43 98 03标准 在此之后 xff0c C 43 43
  • 现代C++语言(C++11/14/17)特性总结和使用建议(二)

    override和final成员函数 以前C 43 43 中虚函数没有一个强制的机制来标识虚函数会在派生类里被改写 vitual关键字是可选的 xff0c 这使得阅读代码变得很费劲 因为可能需要追溯到继承体系的源头才能确定某个方法是否是虚函
  • 高通芯片方案的Wi-Fi6路由器汇总和推荐

    2017年 xff0c 高通宣布推出端到端的802 11ax产品组合 xff0c 其中包括用于网络基础设施的IPQ 8074 SoC 用于客户端设备的QCA 6290解决方案 xff0c 这让高通公司成为第一家宣布支持802 11ax的端到
  • (十)嵌入式:使用TCP协议实现图传

    这段时间做了通信相关的项目 xff0c 需要用到无线图传 xff0c 因此想到了用TCP协议实现 废话不多说 xff0c 直接上代码 xff1a 服务器端 xff1a include lt stdlib h gt include lt st
  • PnP 单目相机位姿估计(一):初识PnP问题

    简介理解更多 IDE xff1a visual studio 2013 使用库 xff1a Eigen opencv2 4 9 文档版本 xff1a 1 1 简介 PnP问题是求解3D 2D点对运动的方法 他描述了当知道n个三维空间点坐标及
  • 多传感器融合中的时间同步2-论文阅读

    文章目录 前言主要内容pps对于INS时间戳校准作用原理 测试结果参考文献 前言 阅读硕士论文 GPS INS组合导航系统研究及实现 xff0c 该论文第5章为时间同步系统设计 xff0c 为GPS INS系统设计的时间同步系统部分内容非常
  • PSINS源码阅读—STIM300/GNSS组合导航

    文章目录 前言代码解读主要框架代码阅读主要脚本sinsgps函数 结果测试 前言 严老师最近在PSINS网站上上传了一组STIM300 GNSS跑车数据 xff0c 并且有光纤惯导数据作为真值参考 xff0c 网站是一组STIM300 GN
  • mpu6500-gnss组合导航代码分析

    文章目录 前言代码分析调参P矩阵陀螺仪偏置P矩阵加速度计偏置P矩阵 前言 导航数据为如下链接 xff0c 数据集使用了低成本Mems器件MPU6500和GNSS做组合导航 代码运行需要严老师psins210406组合导航函数库的支持 xff
  • Java中数组元素的删除

    这是一个LeetCode的简单题 xff0c 在二刷做过的题时突然感觉这个题真的是非常的不错 xff0c 虽然是个简单题 xff0c 没有什么技巧 xff0c 但是写代码的过程中有很多要注意的点 xff0c 感觉还是很考验基本功 xff0c
  • 【视觉里程计】对极几何,三角测量,PnP,ICP原理

    老早就想写些东西 xff0c 但是介于个人懒惰 xff0c 一直没开这个头 xff0c 前几天才发现自己以前学的东西很容易忘记 xff0c 于是决定还是将学习做个总结 xff0c 以便后续回头查看 xff0c 温故而知新嘛 此文章为对相关知
  • Java泛型--泛型应用--泛型接口、泛型方法、泛型数组、泛型嵌套

    1 泛型接口 1 1泛型接口的基本概念 1 2泛型接口实现的两种方式 定义子类 xff1a 在子类的定义上也声明泛型类型 interface Info lt T gt 在接口上定义泛型 public T getVar 定义抽象方法 xff0
  • Linux下调试段错误的方法[Segmentation Fault]--GDB

    原文 1 段错误是什么 xff1f 段错误是指访问的内存超出了系统给这个程序所设定的内存空间 xff0c 例如访问了不存在的内存地址 访问了系统保护的内存地址 访问了只读的内存地址等等情况 A segmentation fault ofte
  • linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问

    设备读操作 如果该操作为空 xff0c 将使得read系统调用返回负EINVAL失败 xff0c 正常返回实际读取的字节数 ssize t read struct file filp char user buf size t count l

随机推荐

  • 函数中的形式参数和实际参数

    1 举例 xff1a 使用函数交换两个整形变量的值 运行结果 xff1a 分析 xff1a c语言中实际参数和形式参数之间采用值传递的方式来传递数据 在被调函数中 xff0c 使用的是实际参数的一个拷贝数据 我们在swap函数中交换了a和b
  • Linux 线程挂起与唤醒功能 实例

    pthread cond wait 多线程的条件变量 条件变量是利用线程间共享的 全局变量进行同步的一种机制 xff0c 主要包括两个动作 xff1a 一个线程等待 34 条件变量的条件成立 34 而挂起 xff1b 另一个线程使 34 条
  • PnP 单目相机位姿估计(二):solvePnP利用二维码求解相机世界坐标

    前言原理简介输入参数准备 1 objectPoints特征点世界坐标2 imagePoints特征点在摄像头下的像素点坐标3cameraMatrixdistCoeffs内参矩阵和畸变矩阵 相机世界坐标的求解 1求世界坐标中的点在相机坐标系下
  • Linux下socket编程,附带tcp例子

    1 网络中进程之间如何通信 xff1f 本地的进程间通信 xff08 IPC xff09 有很多种方式 xff0c 但可以总结为下面4类 xff1a 消息传递 xff08 管道 FIFO 消息队列 xff09 同步 xff08 互斥量 条件
  • 程序员加班到深夜,你经历过没?

    我看到了自己的影子啊 虽然自己非科班出身 xff0c 学历也不高吧 xff0c 但是自认为还是很努力的 xff0c 但是为什么现在的工资水平却跟应届生差不多呢 xff1f xff08 xff09 仔细想想 xff0c 自己毕业3年了 xff
  • 【C/C++学院】(16)QT版:幸运大抽奖

    程序效果 xff1a ifndef DIALOG H define DIALOG H include lt QDialog gt include lt QLabel gt include lt QPushButton gt include
  • 【Python基础】--Pickle/函数默认参数/函数的参数*args/Bytes<=>str/32-64bit/bytes对象

    Pickle gt gt gt import pickle gt gt gt my list 61 1 2 3 39 haha 39 39 and 39 39 or 39 gt gt gt pickle file 61 open 39 my
  • Windows平台python操作串口示例,可以加工下,改写成方便的测试软件

    在 windows中 xff0c 使用 Python 进行串口编程需要安装一个 Serial 模块 pyserial xff1a 下载地址 https pypi python org pypi pyserial下载完成后得到一个 pyser
  • 告别csdn一年了

    原本坚持了4年的学习 xff0c 整理笔记 xff0c 在csdn平台上进行发表 xff0c 记录 同朋友们互动 xff0c 探讨进行学习 xff0c 自己也在不断地成长 今天再次进入博客页面 xff0c 发现界面来了个大改版 xff0c
  • php视频课程

    php视频课程 xff1a 下载地址 xff1a http php itcast cn php video shtml 注 xff1a 此系列视频 xff0c 韩顺平主讲 1 php入门到精通教程 2 第二版mysql视频教程 进行中 3
  • pixhawk ulg转csv

    ulg是目前最新版px4固件生成的log格式 xff0c 下载最新版的flightplot即可对内部数据进行预览分析 xff0c flightplot中支持部分函数和运算符操作 xff0c 但对带 数据的操作不支持 xff0c 如需要对某些
  • 将Kinetic中的Gazebo7升级为Gazebo9

    将Kinetic中的Gazebo7升级为Gazebo9 一 查看所有gazebo7的相关包二 卸载当前已安装的gazebo相关包三 添加源四 安装新版本gazebo五 安装gazebo ros pkgs六 后记 官方教程 http gaze
  • 你真的了解串口 (Serial)吗?

    一 串口的定义 串口 xff0c 全称串行通信接口或串行通讯接口 xff0c 是一种常用于电子设备间通讯的全双工扩展接口 xff1b 串行通信 xff0c 串口通讯的技术基础 xff0c 指一位一位地按顺序传送数据 其特点是线路简单 xff
  • PnP 单目相机位姿估计(三):二维码角点检测

    解PnP问题时用二维码的好处二维码识别的流程代码最后 IDE xff1a visual studio 2013 使用库 xff1a Eigen opencv2 4 9 文档版本 xff1a 1 0 解PnP问题时 xff0c 用二维码的好处
  • 2014年计算机求职总结--面试篇

    又一年实习招聘陆续开始了 xff0c 这里分享一下我在2013年实习招聘和秋季招聘中的一些面试经历 xff0c 希望能对找工作的同学有所帮助 2013年面试过的公司有蘑菇街 网易游戏 阿里巴巴 腾讯 百度 大众点评 人人网 雅虎 xff08
  • 用位运算实现两个整数的加减乘除运算

    位运算的思想可以应用到很多地方 xff0c 这里简单的总结一下用位运算来实现整数的四则运算 1 整数加法 int Add int a int b for int i 61 1 i i lt lt 61 1 if b amp i for in
  • 深入理解C/C++数组和指针

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7044380 C语言中数组和指针是一种很特别的关系 xff0c 首
  • 轻松搞定面试中的链表题目

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7393134 链表是最基本的数据结构 xff0c 面试官也常常用链
  • 轻松搞定面试中的二叉树题目

    版权所有 xff0c 转载请注明出处 xff0c 谢谢 xff01 http blog csdn net walkinginthewind article details 7518888 树是一种比较重要的数据结构 xff0c 尤其是二叉树
  • 动态内存分配(malloc/free)简单实现--隐式空闲链表

    本文使用隐式空闲链表实现简单的动态内存分配 动态内存分配器维护一个大块区域 xff0c 也就是堆 xff0c 处理动态的内存分配请求 分配器将堆视为一组不同大小的块的集合来维护 xff0c 每个块要么是已分配的 xff0c 要么是空闲的 实