赞
踩
深入浅出了解(.text、.data、.bss、堆空间、栈空间)的含义
接下来所说的是嵌入式系统的内存地址空间的布局,简单的说就是我们写好的代码,在编译过程种中,把代码里不同的变量、函数相应的保存在每个段中(.text、.data、.bss),至于堆空间、栈空间是代码在芯片上运行时才存在的。
.text:代码段。包含了操作系统和应用程序的所有代码。
.data:数据段。存放了操作系统和应用程序当中所有带有初始值的全局变量。
.bss:bss段。存放了操作系统和应用程序当中所有未带初始化的全局变量。
堆空间:动态分配的内存空间。在系统运行时,可以通过malloc/free之类的函数来申请或者释放一段连续的内存空间
栈空间:保存运行上下文以及函数调用时的局部变量和形参。
那么问题1,什么样的数据会保存在.text段里?.data段?.bss段?栈空间?堆空间?
答:下面举例说明:
unsigned char gvCh;//全局变量,没有初值,放在.bss段中
unsigned short gvShort;// 全局变量,没有初值,放在.bss段中
unsigned int gvInt = 0x12345678;//全局变量,有初值,放在.data段中
unsigned long gvLong = 0x87654321; //全局变量,有初值,放在.data段中
int main(void)//main函数在经过编译以后得到的机器代码,存放在.text代码段当中
{
unsigned char arry[10],*p;//局部变量,存放在栈当中
p = malloc(10*sizeof(char));//p指针变量指向的空间,存放在堆当中
while(1);
}
补充:上面定义的全局变量没有在代码上引用,这里只是举例,如果在实际应用中,它们会被编译器优化掉,那么它们根本就不会占用内存空间。
问题2,上面的例子可能只是给你解开了一部分疑惑,如果用上变量修饰词static、const,那么它们又是怎么存储的呢?
答:下面举例说明:
static unsigned char gvCh;//全局变量,只能在本文件内引用,没有初值,放在.bss段中
const unsigned short gvShort;//全局变量,不可修改(只读),放在.data段中。实际不会定义无初值的const变量
static unsigned int gvInt = 0x12345678;//全局变量,只能在本文件内引用,有初值,放在.data段中
const unsigned long gvLong = 0x87654321;//全局变量,不可修改(只读),放在.data段中。
int main(void)
{
static unsigned char lvCh;//加上static的局部变量,只能在本函数内引用,函数结束回时不会消失,没有初值,放在.bss段中
const unsigned short lvShort;//加上const的局部变量,不可修改(只读),放在.data段中。实际不会定义无初值的const变量
static unsigned int lvInt = 0x12345678;//加上static的局部变量,只能在本函数内引用,函数结束回时不会消失,有初值,放在.data段中
const unsigned long lvLong = 0x87654321//加上const局部变量,不可修改(只读),放在.data段中
while(1);
}
总结,如果变量加上const,不管是全局变量还是局部变量,不管有没有初值,都保存在.data段。如果变量加上static,不会影响变量所保存的段,static的作用是改变变量的作用域。
问题3,我们了解完怎么样的变量该保存到什么段,或许还会有疑惑,.data段的数据和.bss段的数据有什么区别?把数据这么区分出来的作用是什么?
答:原因其实很简单,就是为了节省编译出来的bin文件占用的内存大小。.data段变量的值会记录在bin二进制文件中,而.bss记录的是变量的起始地址和大小,在程序运行时初始化为零。下举例说明:unsigned char gvCh_init[3] = {1,2,3}; //.data段,如果数组增大100字节,那么bin文件大小会也会随之增大unsigned char gvCh_no_init[3];//.bss段,如果数组增大为100字节,那么bin文件大小不会发生变化,在bin文件里只是记录这个数组的起始地址和大小,当程序运行时自动把它的值清零。
问题4,unsigned chat gvCh[100] ={0};算不算给数组gvCh设置了初值而被保存在.data段,将占用bin文件大小?
答:在IAR编译器里,unsigned chat gvCh[100] ={0}等同于unsigned chat gvCh[100],保存于.bss段。
问题5,我们知道在芯片上有ROM存储器和RAM存储器,在程序bin文件烧录到芯片上时,不同的段会如何存在于在ROM和RAM里呢?
答:没有一一对应的关系。因为我可以把bin文件烧录到flash(ROM)里面运行,也可以把它烧录到RAM里面运行(调试时会这么做)。
通常地,我们会把编译好的程序烧录到flash(ROM)里面去,芯片掉电时不会消失;对于RAM存储器来说,只有芯片上电代码运行起来,才会被分配使用(栈/堆就是在RAM开辟的)。
问题6,代码中的变量会以什么样的规则存在于ROM存储器和RAM存储器里?
答:主要根据变量在运行时是否可修改,下面举例说明:
- unsigned char gvCh;可读写,存放在RAM(无初始值,.bss段)
- unsigned int gvInt = 0x12345678;//可读写,存放在RAM(有初始值,.data段)
- const unsigned long gvLong = 0x87654321; //只读,存放在ROM
-
- int main1(void)//main函数在经过编译以后得到的机器代码,不可修改,存放ROM
- {
- unsigned char arry[10],*p;//可读写,存放在栈当中,也就是在RAM中
-
- p = malloc(10*sizeof(char));//p指针变量指向的空间,存放在堆当中,也就是在RAM中
- while(1);
- }
有初始值、并且可读写的全局变量gvInt存放在RAM,但我们知道在RAM的数据掉电会丢失,其实它的初始值0x12345678一开始保存在flash(ROM)里,在芯片上电时,会将初始值复制到RAM里的gvInt变量(它所在RAM的地址在编译后已经确定)。可能我们会疑惑,自己的代码明明没有写给gvInt变量赋值的过程,这一部分代码并不需要我们做。芯片上电就会进入Reset_Handler中断,打开.s启动文件,我们可以发现在Reset_Handler中断里,调用了SystemInit函数,然后再调用了_main(IAR编译器的话是__iar_program_start)函数,在_main函数会进行RW data的复制,和.bss段的初始化,以及C库函数的初始化(比如malloc函数需要初始化才能使用),进而调用main函数进入我们的代码区域。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。