当前位置:   article > 正文

关于嵌入式系统内存地址空间的一些疑问(.text、.data、.bass、堆\栈空间)_data bss存放在flash 还是ram

data bss存放在flash 还是ram

深入浅出了解(.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存储器里?

答:主要根据变量在运行时是否可修改,下面举例说明:

  1. unsigned char gvCh;可读写,存放在RAM(无初始值,.bss段)
  2. unsigned int gvInt = 0x12345678;//可读写,存放在RAM(有初始值,.data段)
  3. const unsigned long gvLong = 0x87654321; //只读,存放在ROM
  4.  
  5. int main1(void)//main函数在经过编译以后得到的机器代码,不可修改,存放ROM
  6. {
  7.     unsigned char arry[10],*p;//可读写,存放在栈当中,也就是在RAM中
  8.    
  9.     p = malloc(10*sizeof(char));//p指针变量指向的空间,存放在堆当中,也就是在RAM中
  10.     while(1);
  11. }


有初始值、并且可读写的全局变量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函数进入我们的代码区域。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号