关于单片机内存解释的整理

2019-04-15 18:09发布

单片机内存的合理使用对于一名嵌入式软件工程师来说是至关重要的,这深深关系到项目代码的稳定性。对于c语言程序的bug,最为致命、最难发觉的也是内存的使用不当造成的,这种奇葩现象。之前对这一块有过了解,可惜没有真正的领会其中的要害和内涵。大抵是平时写写代码玩的时候单片机资源足够多,也从来不会去理会内存够不够的问题。最近受领导一番话点醒了我,也逐渐明白了它的重要性。 背景大概是这样的:某个项目写到一个数据处理函数,这里需要进行大量数据处理和拼接长字符串,需要大量的中间变量作为转存(超过1k长度的大数组),我左思右想到底是用全局变量?还是局部变量?抑或是利用malloc申请一段内存来使用,年少无知的我想来想去果断使用了定义在函数内的局部变量,理由是局部变量所占用的内存在该函数运行完之后即会自动释放,这样只会在短时间内占用cpu的ram,至于不使用malloc的理由是怕堆生长太大覆盖了栈区的内存。然而....,他告诉我如果在函数内使用大数组的局部变量有可能会造成全局变量区的内存被这货覆盖,我接着问难不成我使用全局?这样不会占用太多的RAM嘛?他说宁可牺牲掉RAM的部分资源作为固定的内存,也不要这种随时可能爆炸的定时炸弹(大数组局部变量),因为它可能在某个时间节点内突然申请一块巨大的内存,到时候内存覆盖刷掉了全局变量的地址就真的GG了。我仔细一想局部变量大数组还真的是一枚定时炸弹,心里默默地记下他的一番话,开始重新去审视和理解关于单片机内存相关的知识。 说了一大段废话.... 以下是我对该内容的一个整理: 单片机内存被分为flash和sram(ram),在查看任何一款单片机的时候都会有它们的介绍,可以看看最近使用的合泰单片机HT32F2253的介绍 我们看到它的flash是128KB、SRAM是16KB。(其实它的内存还算挺大的........) 以下是我按照自己的理解画的的一个内存结构图: 初始化前: 初始化后: 嗯...可以看到,初始化时RW-data从flash拷贝到RAM,所以在程序跑起来了Rw-data是在RAM里面的。 其中:Code为程序代码部分
RO-data 表示 程序定义的常量const temp;
RW-data 表示 已初始化的全局变量
ZI-data 表示 未初始化的全局变量 (以上这一段是copy的,原地址在:http://anlx27.iteye.com/blog/1575848) 而栈区(stack)、堆区(heap)、全局区(静态区)(static)、文字常量区和程序代码区和上面所介绍的code、RO-data等的关系。 1、栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 这些值是可读写的,那么stack应该被包含在RW-data(读写数据存储区),也就是单片机的sram中。 2、堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。可以理解,这些也是被包含在单片机的sram中的。 3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。这些数据也是可读可写的,和stack、heap一样,被包含在sram中。 4、文字常量区:常量字符串就是放在这里的。这些数据是只读的,分配在RO-data(只读数据存储区),则被包含在flash中。 5、程序代码区:存放函数体的二进制代码,可以想象也是被包含在flash,因为对于MCU来说,当其重新上电,代码还会继续运行,并不会消失,所以存储在flash中。 (然而上这一段还是copy的,原地址在:http://www.51hei.com/mcu/4293.html) 下面是网上找的例子: int a = 0; // RW-data 全局初始化区 char *p1; // ZI-data 全局未初始化区 const int b = 0; // RO-data 只读变量区 main(void) { int b; // RW-data 栈区 char s[] = "abc"; // RW-data 栈区 char *p2; // RW-data 栈区 char *p3 = "123456"; // "123456/0" 在Ro-data 常量区,p3在RW-data 栈区 static int c =0; // 全局(静态)初始化区 rw-data -> static区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在Rw-data 堆区 strcpy(p1, "123456"); // "123456/0" 放在常量区,编译器可能会将它 // 与p3所指向的"123456"优化成一个地方 } 所以,重点来了,在实际编程中,应当实时关注.map的内容来判断单片机内存的使用情况。 在keil中,右击工程就会出现opne map flie的选项,打开它,浏览到最后一段   可以看到该工程占用单片机ram12.43kb,flash13.91kb。