> 技术文档 > 深入解析 MCU 内存架构:Flash、RAM、代码与变量存储详解 .rodata .text heap stack .bss .data code RO-data RW-data ZI-data

深入解析 MCU 内存架构:Flash、RAM、代码与变量存储详解 .rodata .text heap stack .bss .data code RO-data RW-data ZI-data


1. MCU 内存概述

1.1 MCU 内存计算

STM32F103CBT6 为例,在 Keil 中可以看见,该款型号 MCU 的 Flash, RAM 大小为:
深入解析 MCU 内存架构:Flash、RAM、代码与变量存储详解 .rodata .text heap stack .bss .data code RO-data RW-data ZI-data

​可以看出 Flash 的地址范围为 0x08000000 ~ 0x08010000,大小为 0x10000(164 byte = 216 byte,所以为 26 KB = 64 KB),同理 RAM 的地址范围为 0x20000000 ~ 0x20005000,大小为 0x5000(5 * 163 byte = 5 * 212 byte,所以为 5 * 22 KB = 20 KB)

1.2 内存分布结构

  • 64KB Flash(用于存放 .text.rodata
  • 20KB RAM(用于 .data.bss、栈和堆)

程序的典型内存分布:

​stm32 由于是 32 位地址总线,所以地址位用八位十六进制数表示(168 = 232)。

pgsql复制编辑+------------------+ 0x08010000 (Flash 结束)| .rodata | 只读数据(Flash)+------------------+| .text | 代码段(Flash)+------------------+ 0x08000000 (Flash 起始)+------------------+ 0x20005000 (RAM 结束)| Heap | 动态分配(malloc)+------------------+| Stack | 栈(局部变量、返回地址)+------------------+| .bss |初始化数据(RAM)+------------------+| .data | 已初始化全局变量(RAM)+------------------+ 0x20000000 (RAM 起始)

注意

  1. .text.rodata 只能存放在 Flash(代码和只读常量)。
  2. .data 段在 Flash 里存有初值,但运行时会被复制到 RAM
  3. .bss不会占用 Flash,但会在 RAM 里初始化为 0
  4. 栈和堆共享 RAM,栈 Stack 从 高地址 0x20005000 向低地址 方向增长,堆 Heap 从 低地址 0x20000000 向高地址 方向增长
  5. 当 Stack 和 Heap 彼此增长到一个临界点(即二者相遇),就会导致 Stack Overflow(栈溢出)。

2.ARM 编译器中的存储字段

​在 Keil 或 ARM GCC 编译器的 Program Size 统计信息中,列出了四个关键字段:

  • Code(.text)程序代码
  • RO-data(.rodata)只读数据
  • RW-data(.data)已初始化的全局/静态变量
  • ZI-data(.bss)未初始化的全局/静态变量

示例:Keil 编译器的存储统计
深入解析 MCU 内存架构:Flash、RAM、代码与变量存储详解 .rodata .text heap stack .bss .data code RO-data RW-data ZI-data

​上图显示了,该程序的内存统计信息如下:

  • Code=13996 byte
  • RO-data=312 byte
  • RW-data=156 byte
  • ZI-data=12156 byte

3. 存储区域的详细解析

​MCU 代码和变量在存储时会被划分到不同的区域,各个区域用途如下:

存储区域 用途 存储位置 .text(Code) 存放编译后的程序指令(代码) Flash(只读) .rodata(RO-data) 存放只读常量const 变量等) Flash(只读) .data(RW-data) 已初始化的全局/静态变量 Flash + RAM .bss(ZI-data) 未初始化的全局/静态变量,启动时清零 RAM(自动清零) Heap(堆) malloc() 动态分配的内存 RAM(向上增长) Stack(栈) 局部变量、函数调用返回地址 RAM(向下增长)

3.1 .text段(存储 code)

  • 存储内容:存放程序指令,即 CPU 执行的代码。
  • 存储位置:通常存放在 Flash(ROM),因为代码是固定的,不需要在运行时修改。
  • 特点
    • MCU 复位后,程序从 Flash 读取指令并执行。
    • 代码段通常是只读的,防止意外修改。
    • C 语言的函数和中断服务函数都存放在这里。
  • 示例:
void func(void) { // func 的指令代码存放在 .text 段 // do something}

3.2 .rodata 段 (存储 RO-data,Read-Only Data)

  • 存储内容:存放只读数据,如 const 修饰的全局变量、字符串常量等。
  • 存储位置:通常存放在 Flash,因为这些数据不会被修改。
  • 特点
    • 只能被读取,不能修改。
    • 对于资源有限的 MCU,尽量让数据存放在 Flash,而不是占用 RAM(不需要改变的常量可以用 const 修饰)。
  • 示例
const char message[] = \"Hello, World!\";

3.3 .data 段(存储 RW-data,Read-Write Data )

存储内容:存放已初始化的全局变量/静态变量

存储位置

  • 编译时:数据存放在 Flash。
  • 程序运行时:MCU 启动代码(Startup 文件)会在程序启动时,将 .data 段的数据从 Flash 复制到 RAM,确保变量可读写。

所以已初始化的全局变量会同时占用 Flash 和 RAM 的内存空间

  • 特点

    • 变量必须初始化,否则存放在 .bss 段。
    • 既占用 Flash 又占用 RAM(启动时复制)。
  • 示例:

int a = 10; // 存放在 .data 段(Flash + RAM)static int b = 20; // 也存放在 .data 段(Flash + RAM)

3.4 .bss 段(存储 ZI-data,Zero Initialized Data ,)

  • 存储内容:存放未初始化的全局变量和静态变量
  • 存储位置:直接存放在 RAM。
  • 特点
    • 上电后,系统会自动清零(由 Startup 代码完成)。
    • 变量未初始化时占用 RAM 但不会占用 Flash,因为 Flash 里没有初始值。
  • 示例
int global_var; // 存放在 .bss 段,自动初始化为 0static int static_var; // 也是 .bss 段,自动初始化为 0

3.4 堆(Heap)和栈(Stack)

​除了上述四个标准段,MCU 运行时还包含 堆(Heap)栈(Stack),它们都位于 RAM,但用法不同。

存储区域 作用 增长方向 堆(Heap) 存放 malloc() 动态分配的数据 向上增长 栈(Stack) 存放局部变量、函数调用返回地址 向下增长

示例:

void foo(void) { int local_var = 10; // local_var 在栈(stack) int *ptr = malloc(10); // malloc 分配的内存在堆(heap)}

4. 代码优化建议

4.1 尽可能使用 const 修饰全局变量:

  • 让编译器将变量放入 Flash(节省 RAM)。
const int lookup_table[] = {1, 2, 3, 4}; // 存在 Flash,而不是 RAM

4.2 避免全局变量初始化为 0

  • 让变量默认存放到 .bss,减少 Flash 和 RAM 的同时占用。
int a = 0; // 额外占用 Flashint b; // 更节省空间,存放在 .bss,自动初始化为 0

4.3 避免 malloc(),使用静态分配

  • 不要频繁使用 malloc(),嵌入式系统中 RAM 资源有限,推荐使用静态数组。
// 避免动态分配,改为静态数组int buffer[256];

5. 总结

Code(.text):存放程序代码(存储在 Flash)。

RO-data(.rodata):存放只读数据(存储在 Flash)。

RW-data(.data):存放已初始化的全局变量(Flash 复制到 RAM)。

ZI-data(.bss):存放未初始化的全局变量(RAM,上电后自动清零)。

Heap(堆)malloc() 动态分配内存(RAM)。

Stack(栈):存放局部变量、函数调用(RAM)。

ROM (Flash) size = Code + Ro-data + rw-data;

RAM size = Rw-data + zi-data