程序运行的时候ram空间是如何分配
一、现象:1、 我在通过减少全局变量、函数内变量的使用,减少函数间参数传递等手段来优化,发现某些时候我减少一个变量的使用,keil编译的结果就显示data减少了一字节,有时候这样一直减少几个变量的使用,data值一直都不会变,接着再减少变量的使用,又会一个一个的减少,到后来又不减少了。
2、 我屏蔽程序中一些代码之后编译,显示data反而是增加了。
二、疑问:
1、单片机程序运行的时候,内部ram是如果分配管理的,data值由哪些部分组成?
2、之前描述的两个现象如何解释?
3、data值是不包括堆栈空间的,那么程序运行的堆栈空间大概需要多少,和哪些因素有关,能不能预估?
4、单片机片内ram的使用限度是多少?(指keil编译出来的data值最好不要超过多少)
答:单片机内程序运行的时候ram空间是如何分配的
1、RAM的分配是与你选择的编译模式有关,你可以看下编译器的手册,再打开最后产生的分配对照表仔细对照源程序,应该可以找到规律。
2、仍然与编译模式有关,通常全局变量数量的变化可以立即反映在data段的长度上,但如果局部变量是指定用堆栈,就不一定会反映在data段的长度上了。
3、堆栈空间与你的RAM空间的分配有关,这是在连接时确定的,在链接描述文件中指定的。
4、RAM的使用限度当然跟你的单片机RAM的大小有关。
对不起,我对Keil的环境不熟,我不能帮你解释具体到Keil上如何;上面讲的是基本原理,每个C语言的环境都是这样。
谢谢平常人!若有机会到广佛一带,我请你喝酒!
keil编译模式我选择的small:variables in data模式,大家一般也都应该是这个模式吧。变量都是定义到data/idata区的。
keil编译结果和编译器本身有关,就算是不同的编译器,在内存分配上是不是有共同遵从的方法呢?
或者您能不能介绍其他某个编译器的内存分配方式呢?
通常全局变量数量的变化可以立即反映在data段的长度上,但如果局部变量是指定用堆栈,就不一定会反映在data段的长度上了。
re:你的意思是局部变量占用的空间的使用还不一定包含到编译结果里了?我编译的结果是200多字节的data,由哪些组成?
ram使用限度跟单片机有关,那好比我的单片机片内ram是256的,那我使用的空间(也就是keil编译出来的结果)的限度是多少,或者和哪些有关?也就是通常做法,我要留给堆栈多少空间?
另外再问一下:
同一个程序生成的bin文件和hex文件在大小上有什么关系?
听说bin会是hex的一半?
这两个文件在使用中有什么区别?
变量在内存的分配方式
通常单片机的RAM区可以分成3类,短地址区、长地址区和外部地址区。
短地址区一般指00-FF之间可以用8位地址访问的区域,长地址区一般指0100-FFFF之间必须用多于8位的地址访问的区域,外部地址区指CPU外部总线访问的区域,不同的区域有不同的指令寻址方式,例如:
MOV A, 40H ;访问短地址区
MOV A, @DPTR ;访问长地址区
一般的51中没有外部地址区。
根据用途还划分了一个堆栈区。
不同的存储分配模式决定了全局变量是放在那个区域,访问短地址区的指令可能比访问长地址区的指令短且快,但长地址区可容纳较多变量,尤其是较大的数组。
局部变量有两种分配策略,一种是放在堆栈中,因为局部变量只在他所在的函数中有效,出了这个函数退栈就可以清理掉局部变量所占空间,空间可重复利用。这种策略下,减少局部变量的使用并反映不出内存占用量的减少,因为内存的占用是动态的。
另一种策略是分析函数调用关系,把局部变量放在某段特定的内存区,如下例:
func1()
{
CHAR c1, c2;
....
}
func2()
{
CHAR x1, x2, x3;
...
}
main()
{
func1();
func2();
....
}
编译器发现func1与func2没有调用关系,就把c1与x1分配到同一个地址,c2与x2分配到同一个地址,x3分配到另一个地址;这样处理可以比堆栈的方法得到较高的效率。当你减掉c1时,并没有减少内存的总用量。
所以,堆栈的长度要看你程序的调用关系,局部变量的使用策略等因素,根据实际情况决定。
将程序的可执行文件(如ELF、EXE)加载到RAM,解析代码段(.text)、数据段(.data、.bss)等,并建立虚拟地址到物理地址的映射。 避免在函数中定义过大的局部变量,或进行深度递归调用。 程序启动时,这些变量会被初始化为指定的值或默认值(如0)。 用于存储全局变量和静态变量。全局变量在程序的整个生命周期内都存在,其内存空间在程序启动时就被分配,直到程序结束才被释放。 data区:用于存储全局变量和静态变量,地址范围通常为0x00-0xFF。
idata区:用于存储较大的全局变量和静态变量,地址范围通常为0x00-0xFF,但使用间接寻址方式访问。
xdata区:用于存储更大的数据,地址范围通常为0x0000-0xFFFF,适用于外部RAM。 在程序开始运行时,这些变量会被加载到RAM中的特定区域,该区域的起始地址和大小通常是固定的,由编译器在编译时确定。 程序在RAM中的典型布局如下(从低地址到高地址):
代码段(Text Segment)
存放只读的程序指令,防止意外修改。
通常位于虚拟地址空间的低地址部分。
初始化数据段(Initialized Data Segment, .data)
存储全局变量和静态变量的初始值。
未初始化数据段(BSS Segment, .bss)
存储未显式初始化的全局变量和静态变量(初始化为零)。
堆(Heap)
动态内存分配区域,通过malloc、new等操作扩展,由程序员管理。
增长方向:通常向上增长(向高地址扩展)。
栈(Stack)
存储局部变量、函数参数和返回地址,由系统自动分配和释放。
增长方向:向下增长(向低地址扩展)。
栈帧(Stack Frame):每个函数调用创建一个栈帧,保存局部变量和上下文信息。 分配方式因系统架构、编译器和运行环境而异。 频繁分配和释放小块内存可能导致堆碎片,影响性能。 BSS段属于静态内存分配区域,但与数据段不同,它不需要在程序初始化时进行数据加载,因为其初始值默认为零。在程序运行过程中,当首次访问这些未初始化的变量时,系统会自动将其初始化为零。 每个函数调用都有自己的栈帧,用于存储该函数的局部变量和状态信息。 变量在程序开始运行前已经被赋予了初始值。它们存储在RAM的已初始化数据段(通常称为.data段)。 堆用于动态分配的内存,如使用malloc、calloc或realloc等函数分配的内存。 程序运行时RAM的分配 编译器会为程序中的全局变量和静态变量分配固定的RAM空间。这些变量的地址在编译时就已经确定。 编译器可能会对变量进行优化,以减少RAM的使用。 谨慎使用,避免过度占用静态存储区和全局变量区。 通过malloc、calloc、realloc等函数动态分配的内存存储在堆中。堆用于存储在程序运行时动态创建的对象和数据结构。 RAM空间的分配是一个复杂的过程,涉及到编译器、链接器和微控制器的硬件特性。
页:
[1]
2