参考1:c语言中内存分配
参考2:http://blog.csdn.net/shine0181/article/details/7305551(里面有对malloc和free的源码分析)
本文章所谈内容是基于stm32平台。所以也许需要对stm32的存储结构说明一下。如图(图片来自stm32数据手册):
图1
图1是stm32整体存储器结构图,这些数字指的是存储器的地址范围,如下面倒数第3个Flash对应的地址是0x08000000~0x0807FFFF,
计算得出这部分空间为:512KByte,这个地址就是用来存储代码的;
可以看到flash所在的块(block 0)还有其他几个区,根据名称可以大概知道各个区的作用,具体用途这里无需多讨论。
另外block 0共有512M其中Reserved即为保留区,即可以使用外部flash扩展这个块的存储空间,只要地址在范围内就可以。
下面重点来看一下block 1:
往上一个方框,block 1名称就是SRAM,所以这个区域就是用来放置RAM。而图中的64KBSRAM就是stm32内置的RAM,
即是说不用外部扩展RAM(MCU的基本配置就是得有CPU+RAM+外设)也能运行处理器。
所以通常用一片单片机+晶振+电源就能跑了跑马灯程序,原因就是这里,学过单片机的应该都明白这个的。
可以看到这个块也有512MB,所以这个RAM也可以通过外部扩展至512MB,当然外部扩展的肯定不如内置的好用了。
那么这个RAM是干什么的呢?
有计算机基础的同学应该都知道,它就是运行内存,就和电脑、手机上经常说的几G内存是同一个东西。
它的作用就是把flash(这里只对于stm32来说)里正在运行的代码段(函数、变量等等)放进这个内存里,
然后CPU对这里面的数据进行读写操作,得出你想要的结果。
而我们经常说的堆、栈也是属于这片区域的,这里所说的堆栈和数据结构的堆栈概念不能混为一谈,当然也是类似的。
stm32的堆、栈设置可以在底层驱动文件startup_stm32f10x_hd.s(这个文件要看你所用MCU的型号,这个是大容量的)里设置。
就是改下面两句的值:
Stack_Size EQU 0x00000400 ;栈大小
Heap_Size EQU 0x00000200 ;堆大小
可以看到只能设置堆、栈的大小,不能设置起始地址和终止地址;
通过度娘查到堆、栈的起始地址设置是在.sct文件中的,具体设置方法还需进一步翻阅相关资料,这里不讨论。
用默认配置,则编译器会自动分配地址给堆栈。这个地址当然要在RAM规定的内存范围内。
由图1(stm32存储器映像图)可以知道,堆栈地址在0x20000000~0x2000FFFF范围内,通过查找.map文件关键字:__initial_sp
可以找到栈地址。堆地址暂时不知道怎么搜索到,也许是因为动态分布和不连续性的原因,编译器无法给出初始地址。
stm32的存储器就先介绍到这里,根据图1再往上就是外设的地址了,图中标识的很明确,也就不细说了。
到这里stm32的堆栈的位置已经很明确了。所以可以着重来谈谈几个关键概念的使用了。
1、堆的使用是要结合malloc函数,即使用一次malloc所得到的内存空间既是属于堆的空间。
2、堆的增长方向是向上,所以malloc申请的地址也是越来越大的,前提是连续申请且在最后一次申请后再释放内存(free)。
则第一次申请的地址永远小于后面申请的地址。
3、堆是不连续的,由于RAM中还存在局部变量,代码段和栈等等,所以动态分配的内存是取暂时空闲的内存,
而不是预先划出一块区域,这就是动态分配内存的好处。
4、使用堆的坏处,由于使用malloc申请内存时,不单只申请了所需的大小空间,还要额外暂用管理这部分空间的内存,而释放时又只释放申请的内存,
所以使用堆会引入内存碎片。当然如果不是在短时间内频繁的使用malloc申请和free释放内存,那么操作系统就有足够的时间来回收碎片空间。
1、由编译器分配,目的是将RAM划分处一块区域供程序运行时的局部变量参数等使用;
2、栈是一块连续的内存空间,由上往下增长,即使用栈时地址是会越来越小的,如先声明的局部变量比后声明的地址要高;
3、栈是由程序(操作系统)自动分配,不会有内存碎片的问题;
4、栈的坏处:栈是固定且连续的一个大小,如果使用局部变量等超出了栈的大小则会造成内存溢出,
而编译器通常是发现不了的,只有当程序运行到那个函数时才会发生的。这就会引入很难查找的bug。
另外如如果使用malloc申请的内存不规范使用,当释放内存后,没将指针地址清空,仍指向那个地址刚好是栈的地址,则会造成越界访问。
1、这两个函数是配对使用的,如果申请了一个内存,而没有使用free释放,反复的操作后就很快会发生内存溢出,造成难以查找的问题。
所以申请了要释放很重要。
2、malloc使用格式:
char * Onebyte;//声明一个char指针
Onebyte = (char*)malloc(sizeof(Onebyte));//向堆中申请一个字节内存
//下面两句效果一样,若在函数里
char Onearr[10];//向栈中申请10个字节
char *Onearr = (char*)malloc(10);//向堆中申请10个字节
3、free的使用:
//对于上面malloc的使用
free(Onebyte);
free(Onearr);
学会内存管理可以解决一些非常棘手的问题,如:内存溢出,内存越界访问等等。
内存溢出编译器不能发现,引入的问题通常只能在运行代码时才能出现,且是影响到其他代码的运行。
在一个多任务嵌入式系统中如果出现该问题,则会变现的很奇怪,会影响到各个任务的运行。
所以才会说懂内存管理是很重要的。