1 各段含义
一般 MCU 包含的存储空间有:片内 Flash (ROM)与片内 RAM。
    Total RO  Size (Code + RO Data)    
    Total RW  Size (RW Data + ZI Data)  >>> RAM     
    Total ROM Size (Code + RO Data + RW Data) 	>>> Flash(ROM)
 
| Section | 含义 | 内存地址分配阶段 | 属性 | 
|---|---|---|---|
| Code | 存放函数体的二进制代码 | 编译时分配 | 只读 | 
| RO-data(Read Only data) | 常量(局部常量在栈区) | 编译时分配 | 只读 | 
| RW-data(Read Write data) | 初值非0的全局变量和静态变量 | 编译时分配 | 读写 | 
| ZI-data(Zero Initialie data) | 未初始化或初始化为0的全局变量和静态变量 | 编译时分配 | 读写 | 
| ZI-data栈空间(Stack) | 局部变量(局部静态变量除外) | 运行时分配 | 读写 | 
| ZI-data堆空间(Heap) | 使用malloc动态分配的空间 | 运行时分配 | 读写 | 
MDK的map文件可以查看RO段、RW段和部分ZI段的变量与函数的起始地址、大小等,ZI段的堆栈区中的局部变量是无法查看的,因为它们的内存地址是运行时分配的。
2 测试代码
struct test{
    int a;
    char b;
    short c;
    const char* p;
};
volatile const char* s1 = "hello world";     // 常量 RO
volatile int g_buf1[100] = {1};   // 初值非0的全局数组	RW
volatile char g_buf2[9];          // 初值为0的全局数组	ZI (bss)
volatile char g_buf3[8];          // 初值为0的全局数组	ZI (data)
volatile int g_a1 = 1;	          // 初值非0的全局变量	RW
volatile char g_a2;	              // 未初始化的全局变量	ZI
volatile static int s_a1 = 2;	  // 初始化的全局静态变量	RW
volatile static int s_a2;	      // 未初始化的全局静态变量	ZI
volatile int* g_ptr1;             // 未初始化的全局指针变量	ZI      
struct test ss_t;                 // 未初始化的全局结构体变量	ZI (bss)     
// int *ptr1 = rt_malloc(4 * sizeof(int));	// 直接报错
int main(void)  // Code
{	
    volatile static int s_a3[3] = {0};  // 初始化为0的局部静态数组 ZI   (bss)
    volatile static int s_a4[4] = {1};  // 初始化的局部静态数组 RW  (data)
    volatile static int s_a5;           // 未初始化的局部静态变量 ZI
    volatile static int s_a6 = 2;       // 初始化的局部静态变量	RW
    const char c1 = 't';                // 局部常量	ZI-Stack
	volatile int a1;		            // 局部变量	ZI-Stack
	volatile int *ptr2 = rt_malloc(4 * sizeof(int));	// 局部变量	ZI-heap, ptr2指针指向的内存空间位于动态内存堆空间中
    
    // bss段变量必须至少要有1个被调用,才能显示
    g_buf2[0] = 3;
    /* 局部静态变量只有被调用才能在map文件查看到 */
    rt_kprintf("%d, %d, %d, %d", s_a3[0], s_a4, s_a5, s_a6);
    return 0;
}
 
加volatile关键字防止未使用的变量被编译器优化。
map文件相关内容如下:
data段:
	全局变量和常量:
    s1                                       0x20000000   Data           4  main.o(.data)
    g_buf1                                   0x20000004   Data         400  main.o(.data)
    g_buf3                                   0x20000194   Data           8  main.o(.data)
    g_a1                                     0x2000019c   Data           4  main.o(.data)
    g_a2                                     0x200001a0   Data           1  main.o(.data)
    g_ptr1                                   0x200001ac   Data           4  main.o(.data)
    静态变量:
    s_a1                                     0x200001a4   Data           4  main.o(.data)
    s_a2                                     0x200001a8   Data           4  main.o(.data)
    s_a4                                     0x200001b0   Data          16  main.o(.data)
    s_a5                                     0x200001c0   Data           4  main.o(.data)
    s_a6                                     0x200001c4   Data           4  main.o(.data)
bss段(mdk优化,大于8字节才进bss):
	未初始化的全局变量:
    g_buf2                                   0x200004c4   Data           9  main.o(.bss)
    ss_t                                     0x200004d0   Data          12  main.o(.bss)
	未初始化的静态变量:
	s_a3                                     0x200004dc   Data          12  main.o(.bss)
 
 
关于text、data、bss说明:
- text:存放程序执行代码,只读。
 - data:存放已初始化的全局变量和静态变量、全局常量
 - bss:即ZI-data(不含堆栈),存放未初始化或初始化为0的全局变量和静态变量

 
注意:
- 测试函数要调用外部变量或函数,不然函数会被编译器优化,这样即使用
volatile关键字定义的变量也无法在map文件中查看。 - Keil对程序进行了优化,未初始化为初始化为0的全局变量和静态变量(结构体、数组)只有内存大于8字节时才会存放在bss段,否则存放在data段!
 - bss段变量必须至少要有1个被调用,才可以在
map文件中查看。 - 局部静态变量只有被调用才能在
map文件查看到。 
3 可执行映像文件启动搬运流程
可执行映像文件(烧录文件)的内存分布如下图(图左)所示,主要包含RO段和RW段部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
 
STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 段的起始地址和大小分配出 ZI 段,并将这块 RAM 区域清零(由用户程序或编译器提供的一些库函数初始化成零,这样做节省ROM空间)。
RTT的动态内存堆即为未使用的 RAM 空间,应用程序申请和释放的内存块都来自该空间。
END










