内存动态分配
在单片机中由于内存资源紧张,不可能给每个任务分配专有的内存区,尤其是涉及到通讯模块的程序,对内存的使用更是敏感。为此开发一个简单的内存管理库,对以后的开发还是有着不小的帮助的。
功能实现:内存动态分配、内存动态释放、内存碎片回收
heap.c
// 内存划分:申请的一块内存分为两部分,一部分用于存储链表结构体,另一部分给用户使用
// 内存管理:链表不存在头节点和尾节点,理论上在内存在可以存在无数个节点,通过空闲标志位来识别该内存块是否可用
// 内存管理:链表不存在头节点和尾节点,理论上在内存在可以存在无数个节点,通过空闲标志位来识别该内存块是否可用
#include "heap.h"
#define HEAP_TRUE 1
#define HEAP_FALSE 0
#define HEAP_FALSE 0
#pragma pack(push, 1)
typedef struct _sHeapList_t
{
unsigned int size; // 内存块大小
unsigned char isfree; // 空闲标志
unsigned char *penter; // 内存入口地址
struct _sHeapList_t *plast; // 指向上一个节点,头节点为NULL
struct _sHeapList_t *pnext; // 指向下一个节点,尾节点尾NULL
}sHeapList_t;
#pragma pack(pop)
typedef struct _sHeapList_t
{
unsigned int size; // 内存块大小
unsigned char isfree; // 空闲标志
unsigned char *penter; // 内存入口地址
struct _sHeapList_t *plast; // 指向上一个节点,头节点为NULL
struct _sHeapList_t *pnext; // 指向下一个节点,尾节点尾NULL
}sHeapList_t;
#pragma pack(pop)
// 申请一个内存块,作为基础内存
static unsigned char Heap[HEAP_TOTAL_SIZE];
// 内存申请(注:函数不可重入)
void *pmalloc(unsigned int size)
{
sHeapList_t *pnode = (sHeapList_t *)Heap;
// 内存管理模块未初始化时需要执行初始化(以第一个节点是否存在作为判断依据)
if(pnode->penter != (Heap + sizeof(sHeapList_t)))
{
pnode->plast = NULL;
pnode->pnext = NULL;
pnode->isfree = HEAP_TRUE;
pnode->penter = Heap + sizeof(sHeapList_t); // 入口地址向后偏移一个结构体的位置
pnode->size = HEAP_TOTAL_SIZE - sizeof(sHeapList_t); // 剩余内存需要减去一个结构体大小的空间
}
if(pnode->penter != (Heap + sizeof(sHeapList_t)))
{
pnode->plast = NULL;
pnode->pnext = NULL;
pnode->isfree = HEAP_TRUE;
pnode->penter = Heap + sizeof(sHeapList_t); // 入口地址向后偏移一个结构体的位置
pnode->size = HEAP_TOTAL_SIZE - sizeof(sHeapList_t); // 剩余内存需要减去一个结构体大小的空间
}
if(size > 0)
{
// 搜索一个不小于指定大小的内存块
while(pnode != NULL)
{
if(pnode->isfree == HEAP_TRUE)
{
{
// 搜索一个不小于指定大小的内存块
while(pnode != NULL)
{
if(pnode->isfree == HEAP_TRUE)
{
if(pnode->size >= size)
{
break;
}
}
pnode = pnode->pnext;
}
{
break;
}
}
pnode = pnode->pnext;
}
// 找到了符合条件的内存块就进行分配,否则就直接返回NULL
if(pnode != NULL)
{
// 找到的内存块大于所需内存,就需要进行拆分,因为每个内存块都需要一个内存管理结构体,所以
// 如果拆分后另一个内存块大小大于一个结构体大小,则拆分出一个新的空闲内存块,并插入链表,
// 如果拆分后另一个内存块大小小于一个结构体大小,无法作为一个新内存块使用,那就不拆分。
if(pnode != NULL)
{
// 找到的内存块大于所需内存,就需要进行拆分,因为每个内存块都需要一个内存管理结构体,所以
// 如果拆分后另一个内存块大小大于一个结构体大小,则拆分出一个新的空闲内存块,并插入链表,
// 如果拆分后另一个内存块大小小于一个结构体大小,无法作为一个新内存块使用,那就不拆分。
if(pnode->size > (size + sizeof(sHeapList_t)))
{
unsigned char *start = pnode->penter + size;
sHeapList_t *newnode = (sHeapList_t *)start;
{
unsigned char *start = pnode->penter + size;
sHeapList_t *newnode = (sHeapList_t *)start;
// 初始化新内存块
newnode->plast = pnode;
newnode->pnext = pnode->pnext;
newnode->isfree = HEAP_TRUE;
newnode->penter = start + sizeof(sHeapList_t);
newnode->size = pnode->size - (size + sizeof(sHeapList_t));
newnode->plast = pnode;
newnode->pnext = pnode->pnext;
newnode->isfree = HEAP_TRUE;
newnode->penter = start + sizeof(sHeapList_t);
newnode->size = pnode->size - (size + sizeof(sHeapList_t));
// 初始化用户申请的内存块
pnode->size = size;
pnode->pnext = newnode;
pnode->isfree = HEAP_FALSE;
}
else
{
// 找到无法拆分的内存块就直接给用户使用
pnode->isfree = HEAP_FALSE;
}
pnode->size = size;
pnode->pnext = newnode;
pnode->isfree = HEAP_FALSE;
}
else
{
// 找到无法拆分的内存块就直接给用户使用
pnode->isfree = HEAP_FALSE;
}
return pnode->penter; // 返回用户内存入口地址
}
}
return NULL;
}
}
}
return NULL;
}
// 内存释放
void pfree(void *p)
{
sHeapList_t *pnode = (sHeapList_t *)((unsigned char *)p - sizeof(sHeapList_t));
void pfree(void *p)
{
sHeapList_t *pnode = (sHeapList_t *)((unsigned char *)p - sizeof(sHeapList_t));
if(pnode == NULL)
{
return;
}
{
return;
}
// 因为内存块都是按地址顺序排列在链表中的(空闲内存块和用户内存块在同一个链表),
// 因此释放时只需合并内存块就可以了
// 因此释放时只需合并内存块就可以了
if(pnode->pnext != NULL)
{
// 当下一个节点存在时,且是空闲内存块
if(pnode->pnext->isfree == HEAP_TRUE)
{
// 当前节点和下一个节点相邻,可以合并
pnode->size += pnode->pnext->size + sizeof(sHeapList_t);
pnode->pnext = pnode->pnext->pnext;
}
}
{
// 当下一个节点存在时,且是空闲内存块
if(pnode->pnext->isfree == HEAP_TRUE)
{
// 当前节点和下一个节点相邻,可以合并
pnode->size += pnode->pnext->size + sizeof(sHeapList_t);
pnode->pnext = pnode->pnext->pnext;
}
}
if(pnode->plast != NULL)
{
// 当上一个节点存在时,且是空闲内存块
if(pnode->plast->isfree == HEAP_TRUE)
{
// 当前节点和上一个节点相邻,可以合并
pnode->plast->size += pnode->size + sizeof(sHeapList_t);
pnode->plast->pnext = pnode->pnext;
}
}
{
// 当上一个节点存在时,且是空闲内存块
if(pnode->plast->isfree == HEAP_TRUE)
{
// 当前节点和上一个节点相邻,可以合并
pnode->plast->size += pnode->size + sizeof(sHeapList_t);
pnode->plast->pnext = pnode->pnext;
}
}
pnode->isfree = HEAP_TRUE; // 不管内存块是否合并成功,最后都要将内存块设置为空闲状态
}
}
heap.h
#ifndef __HEAP_H
#define __HEAP_H
#define __HEAP_H
// Define NULL pointer value
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
// 模块功能:单片机内存动态管理,支持内存碎片回收
// 动态管理的内存大小,单位:字节
#define HEAP_TOTAL_SIZE (10 * 1024) // 10KB
#define HEAP_TOTAL_SIZE (10 * 1024) // 10KB
// 内存申请(注:函数不可重入)
void *pmalloc(unsigned int size);
void *pmalloc(unsigned int size);
// 内存释放(带有内存碎片回收机制)
void pfree(void *p);
void pfree(void *p);
#endif
不但要知其然,还要知其所以然