核心就是RAM和ROM的作用和区别
1 .bss .data .text
1.1 bss段:
bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。
bss是英文Block Started by Symbol的简称。
bss段属于静态内存分配。
1.2 data段:
数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。
数据段属于静态内存分配。
1.3 text段:
代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。
这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
程序本质
一个程序本质上都是由 bss段、data段、text段三个组成的。
这样的概念,不知道最初来源于哪里的规定,但在当前的计算机程序设计中是很重要的一个基本概念。
而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配,存储单元占用空间大小的问题。
在采用段式内存管理的架构中(比如intel的80x86系统),bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时bss 段部分将会清零。bss段属于静态内存分配,即程序一开始就将其清零了。
比如,在C语言之类的程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
text和data段都在可执行文件中(在嵌入式系统里一般是固化在镜像文件中),由系统从可执行文件中加载;
而==bss段不在可执行文件中,由系统初始化,==全局的未初始化变量存在于.bss段中,具体体现为一个占位符;
全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间;.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);.data却需要占用,其内容由程序初始化。
bss段(未手动初始化的数据)并不给该段的数据分配空间,只是记录数据所需空间的大小;
bss段的大小从可执行文件中得到 ,然后链接器得到这个大小的内存块,紧跟在数据段后面。
data段(已手动初始化的数据)则为数据分配空间,数据保存在目标文件中;data段包含经过初始化的全局变量以及它们的值。
包含data段和bss段的整个区段此时通常称为数据区。
2 Keil MDK中的Code, RO-data , RW-data, ZI-data
2.1 Code(inc.Data) :
包含两部分,即代码和数据
code,即程序代码部分
-== inline data==. For example, literal pools(文字常量池), and short strings(短字符串)等. 这个一般被忽略,请大家注意!!!
2.2 RO Data:
read-only data,只读的数据
Shows how many bytes are occupied by read-only data. This is in addition to the inline data included in the Code (inc. data) column. 除inline data 之外的所有只读数据。
2.3 RW Data:
read write data,可读写的数据
Shows how many bytes are occupied by read-write data.
2.4 ZI Data:
zero initialized data,零初始化的可读写变量
Shows how many bytes are occupied by zero-initialized data.
keil编译器默认是把你没有初始化的变量都赋值一个0。初始化为零,或者未初始化的变量,都存储于这个区域。
存储Size:
1 RO size: Code + RO_data
2 RW size: RW_data + ZI_data
3 ROM (minimum)size = Code + RO_data + RW_data (即烧/下载程序到FLASH/ROM时,所占用的最小空间)
4 Total ROM Size (Code + RO Data + RW Data)这样所写的程序占用的ROM的字节总数,也就是说程序所下载到ROM flash 中的大小。
5 RAM size: RW Data + ZI Data (即程序运行的时,RAM使用的空间)
为什么ROM(flash)中还要存RW,因为掉电后RAM中所有数据都丢失了,每次上电RAM中的数据是被重新赋值的,每次这些固定的值就是存储在ROM中的,
为什么不包含ZI段呢,是因为ZI数据都是0,没必要包含,只要程序运行之前将ZI数据所在的区域一律清零即可。包含进去反而浪费存储空间。
3 例子
3.1 keil编译输出
1 linking...
2 Program Size: Code=30550 RO-data=762 RW-data=140 ZI-data=48436
1 Code, RO-data,RW-data ............flash
2 RW-data, ZIdata...................RAM
3.2 MAP
初始化时RW-data从flash拷贝到RAM
生成的map文件位于list文件夹下 (KEIL)
1 Total RO Size (Code + RO Data) 31312 ( 30.58kB)
2 Total RW Size (RW Data + ZI Data) 48576 ( 47.44kB)
3 Total ROM Size (Code + RO Data + RW Data) 31452 ( 30.71kB)
C语言变量的存储类别
内存中供用户使用的存储空间分为代码区与数据区两个部分。
变量存储在数据区,数据区又可分为静态存储区与动态存储区。
静态存储是指在程序运行期间给变量分配固定存储空间的方式。如全局变量存放在静态存储区中,程序运行时分配空间,程序运行完释放。
动态存储是指在程序运行时根据实际需要动态分配存储空间的方式。如形式参数存放在动态存储区中,在函数调用时分配空间,调用完成释放。
对于静态存储方式的变量可在编译时初始化,默认初值为O或空字符。
对动态存储方式的变量如不赋初值,则它的值是一个不确定的值。
auto 存储类指明符
用于说明具有局部作用域的变量,它表示变量具有局部(自动)生成期,但由于它是所有局部作用域变量说明的缺省存储类指明符,所以使用得很 少。要注意的是,所有在函数内部定义的变量都是局部变量,函数内部定义的变量其作用域只在函数内部。它的生存期为该函数运行期间,一旦离开这个函数或这个 函数终止,局部变量也随之消失。
register 存储类指明符
当声明了这个指明符后,编译程序将尽可能地为该变量分配CPU内部的寄存器作为变量的存储单元,以加快运行速度。注意,寄存器与存储器是 不同的。寄存器一般在CPU内部,而存储器一般指外部的(比如内存条),CPU内部的寄存器其运算速度是很高的。当寄存器已分配完毕,就自动地分配一个外 部的内存。它的作用等价于auto,也只能用于局部变量和函数的参量说明。
static 存储类指明符
表示变量具有静态生成期。static变量的的特点是它离开了其作用域后,其值不会消失。(即内存一直被它占有,不会被释放)
extern 存储类指明符
一般用在工程文件中。在一个工程文件中因为有多个程序文件,当某一个变量在一个程序文件中定义了之后,如果在另一个程序文件中予以定义, 就会出现重复定义变量的错误。使用extern存储类型指明符就可以指出在该文件外部已经定义了这个变量。extern变量的作用域是整个程序。
动态数组和静态数组
动态数组和静态数组虽然在使用时看起来没有什么差别,但他们实现是不一样的。
反汇编看一下他们的代码。
数组类型 C/C++代码 汇编实现 简略说明
局部变量 a[7] = 0; MOV DWORD PTR SS:[EBP-C], 0 采用EBP在堆栈定位变量[EBP - 28] a[0] … [EBP - 4] a[9]
静态局部变量 s_a[7] = 0; MOV DWORD PTR DS:[4C5E5C], 0 静态变量会被放到数据.data段中
全局变量 g_a[7] = 0; MOV DWORD PTR DS:[4C5E84], 0 全局变量和静态变量一样,会被放到数据.data段中
数组指针(malloc) p1_a[7] = 0; MOV EAX, DWORD PTR SS:[EBP-2C] MOV DWORD PTR DS:[EAX+1C], 0 对于数组指针,要进行两次寻址 0x1C / 4 = 7
数组指针(new) p2_a[7] = 0; MOV EAX, DWORD PTR SS:[EBP-30] MOV DWORD PTR DS:[EAX+1C], 0 同上
堆栈
堆(heap):
堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);
当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。
栈(stack):
栈又称堆栈,是用户存放程序临时创建的局部变量,
也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。
除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。
由于栈的先进先出(FIFO)特点,所以栈特别方便用来保存/恢复调用现场。
从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。