单片机的动态内存管理-一以链表为例

2019-04-15 18:52发布


程序在运行时有时候会需要一个很大的空间去临时存储一些变量,如果一开始就分配一个很大的数组区的话,有可能会造成内存的浪费,有可能很长一段时间根本不需要这么大的内存,这就造成了一种情况,如果不分配这么大的空间,需要的时候内存不够,如果分配了,可能有一些功能就要被摒弃_(┐「ε:)_动态内存管理就是在这样的背景下产生的,先说说编译器在编译时的内存分配,编译器在编译时会将内存分为 栈区、堆区、BSS区、全局初始化变量区、代码区。而这些区域只有堆区是可以动态管理的,也就是程序可以去申请调用的内存区域,详细的内存分布以及它们之间的特性请看我的另一篇博客,这里就不展开讲了。 今天我要说的是关于单片机的动态内存管理,其实对于单片机开发者而言,也许一辈子都接触不到动态内存动情况,为什么,内存太小了,本来就几k的空间,做内存管理有意义吗,对于这个话题,我且按下不表,这里只做技术展示,不喷口水战(~ ̄▽ ̄~) 一步一步跟着我走,包教包会,先说说环境,我使用的是5.21版本的keil,在stm32f103v8t6 和 mb9af314n上实验过这份代码,所以这份代码是适用于Cotex-M3系列芯片的。 例子使用的芯片是stm32vet6 第一步,设置 堆 的大小 Heap_Size       EQU     0x00000200  //这里设置了堆的大小为512个字节,在启动文件那里,设置堆的大小不能为0,而且必须为4的倍数,如果设置的不是4的倍数会有警告,而且程序跑不动,警告如下 warning: A1581W: Added 1 bytes of padding at address 0x1  原因是指需要字节对齐,stm32为32位机,剩下的自己琢磨去,不展开讲了。 然后在工程配置中打开微苦库,Target 标签栏下勾选上 USE MicroLIB。 然后包含这两个头文件 #include "stdlib.h"
#include "string.h"
然后还有一些需要用掉的宏定义 #ifndef uint16_t
#define uint16_t unsigned int
#endif


#ifndef uint8_t
#define uint8_t unsigned char
#endif


#ifndef TURE
#define TURE 1
#endif


#ifndef FAULSE 
#define FAULSE 0
#endif


#ifndef TAIL 
#define TAIL 0xffff
#endif
//定义一个结构体节点 struct node
{
int data; //有效数据
struct node *pNext; //指向下一个节点的指针
};

下面是函数的具体实现,我直接贴代码,然后在一一说明 //创建链表节点
struct node* CreateNode(void)
{
struct node* p = (struct node*)malloc(sizeof(struct node));
if(NULL == p)
{
return NULL;
}

memset(p,0,sizeof(struct node));
p->pNext = NULL;

return p;
}


//获取链表节点地址
struct node* GetNode(struct node *pHeader,uint16_t num)
{
uint8_t i;
struct node *p=pHeader;

if(NULL == p->pNext)
{
return FAULSE;
}

for(i=0;i{
if(p->pNext != NULL)//节点的位置不得大于链表长度
{
p=p->pNext;
}
else
{
return FAULSE;
}
}

return p;
}


//插入节点
//形参 pHeader:链表头  num = 0:头 TAIL:尾部 !0&&!TAIL : 链表中间
//返回值 TURE:成功 FAULSE:失败
uint8_t InsertNode(struct node *pHeader,uint16_t num)
{
uint8_t i;
struct node *p=pHeader;
struct node *p1 =(struct node*)malloc(sizeof(struct node));

if(NULL == p1)
{
return FAULSE;
}

if(0 == num)//在头部增加节点
{
if(NULL == pHeader)
{
pHeader = p1;
}
else
{
pHeader->pNext = p1;
}
}
else if (TAIL == num)//末尾增加节点
{
while(p->pNext != NULL)
{
p=p->pNext;
}
p->pNext = p1;
}
else if(num > 0 && num != TAIL)//在链表中间增加节点
{
for(i=0;i{
if(NULL != p->pNext)//增加节点的位置不得大于链表长度
{
p=p->pNext;
}
else
{
return FAULSE;
}
}

p1->pNext = p->pNext;
p->pNext = p1;
}



return TURE;
}


//删除链表节点
//形参 pHeader:链表头  num = 0:头 TAIL:尾部 !0&&!TAIL : 链表中间,注意num不能为0
//返回值 TURE:成功 FAULSE:失败
uint8_t DeleNode(struct node *pHeader,uint16_t num)
{
uint8_t i;
struct node *p=pHeader,*p1,*p2;

if(NULL == p)//空链表,无意义
{
return FAULSE;
}

// if(0 == num)//在头部删除节点
// {
// p1 = pHeader;
// pHeader = pHeader->pNext;
// free(p1);
// }
if (TAIL == num)//末尾删除节点
{
while(p->pNext != NULL)
{
p1=p;//获取新的尾巴
p=p->pNext;//指向下一个节点
}
p1->pNext = NULL;
free(p);
}
else if(num > 0 && num != TAIL)//在链表中间删除节点
{
for(i=0;i{
if(NULL != p->pNext)//增加节点的位置不得大于链表长度
{
p=p->pNext;
}
else
{
return FAULSE;
}
}

p1 = p->pNext;
p2 = p1->pNext;
p->pNext = p2;
free(p1);
}

return TURE;
}


//获取链表长度
uint16_t  GetNodeNum(struct node *pHeader)
{
uint16_t ret,i;
struct node *p=pHeader;

if(NULL == p)//空链表
{
return FAULSE;
}

for(i=1;p->pNext != NULL;i++)
{
p = p->pNext;
}
ret = i;

return ret;
}

源代码贴完了,现在我在keil上仿真一下,读者可以直接在下方下载我的源码去试试。
图中是程序未执行的时候,下面一步一步调试。
程序执行到断点为止,分配了内存地址为0x20000118。 到此,先打开*.map文件看看是不是在堆中分配的内存。     HEAP                                     0x20000110   Section      256  startup_stm32f10x_hd.o(HEAP)
    STACK                                    0x20000210   Section     1024  startup_stm32f10x_hd.o(STACK)
编译器中关于堆的内存分配的基地址为0x20000110,堆的上界即为栈顶0x20000210,而pHeader的地址为0x20000118,在堆区内。 这里有必要说明一件事是,堆和栈其实是相邻的两个区域,堆的基地址为0x20000110,增长到0x20000209停止;而栈的基地址为0x20000210+0x00000200,向下增长,增长到0x20000210停止。 继续单步调试
查看a0=5,而图中分配了5个节点有地址变量。
为第二个节点赋值2,图中正常赋值。有必要说明的是获取节点地址的函数是从0开始的,0代表链表头。
对比上图,第二个节点的地址已经消失,原第三个节点地址上移,原最后一个节点变为NULL,且内容变脏了。
综上,在函数中调用了maclloc()和free()函数实现了单片表的操作,实现了单片机的动态内存管理。 本函数还有优化的空间,后面会再发布一个优化的版本。 源码的下载链接 http://pan.baidu.com/s/1hsNo3mG 完!         ∧_∧ 
   ∧_∧  (´<_`︵) 
  (´_ゝ`)  /   i 
  /  \ /   || 
  /  / ̄ ̄ ̄ ̄ ̄/| 
 _(ニっ/     / |______ 
   \/_____/ (u  ⊃
写于2016年10月14日 深圳