本示例使用vscode中cubemx插件,芯片STM32G411CUE6,Flash、RAM分配示意图如下:

一、编译链接阶段静态划分
1. text 段(代码段 / 文本段)
存储内容:程序的机器码(函数体、指令)、const修饰的只读常量(如const int g_val = 10;);
物理映射:唯一映射到Flash(非易失、只读,防止程序代码被意外修改);
核心特性:编译链接后固化,运行时不可修改,占用 Flash 空间,不占用 RAM 空间;
2. rodata 段(只读数据段)
存储内容:字符串常量(如"hello world")、未用const修饰但实际只读的常量;
物理映射:映射到Flash(只读,非易失);
与 text 段的关系:text 段偏 "执行指令",rodata 段偏 "只读数据",本质都是 Flash 中的只读内容。
3. data 段(已初始化数据段)
存储内容:显式赋初始值的全局变量、显式赋初始值的静态变量(static修饰),如int g_init = 10;、static char s_buf = 'a';
物理映射:双占用特性------ 初始值存 Flash(烧录时固化),运行时变量本体存 RAM(可读写);
核心特性:程序上电启动后,启动文件会自动将 Flash 中的初始值拷贝到 RAM的 data 段区域,完成变量初始化;
与 BSS 段的核心区别:都是存全局 / 静态数据、都占 RAM,但 data 段需要 Flash 存初始值,BSS 段不需要。
4. bss 段(未初始化数据段)
存储内容:未显式赋初始值的全局变量、未显式赋初始值的静态变量,如int g_uninit;、static long s_uninit;;
物理映射:仅占用 RAM,不占用任何 Flash 空间(核心特性,无初始值可存储,无需烧录);
核心特性:程序上电启动后,启动文件会自动将 bss 段对应的 RAM 区域全部清零(赋 0),因此未初始化的全局 / 静态变量默认值一定是 0;
二、程序运行时的RAM 动态内存区域
1. 栈区(Stack)------ 自动管理
存储内容:局部变量(函数内定义的非 static 变量,如void func(){ int a; })、函数调用的返回地址、函数参数、寄存器临时值;
管理方式:编译器自动分配和释放,函数调用时,局部变量入栈;函数执行结束,局部变量出栈,内存自动回收;
核心特性:① 地址向下生长(从高地址向低地址分配内存);② 有固定大小限制(小心溢出);③ 局部变量默认值随机(不会被清零,与 BSS 段的 "自动清零" 形成鲜明对比);
与 BSS 段的核心区别:
| 对比维度 | 栈区 | BSS 段 |
|---|---|---|
| 存储数据 | 局部变量、函数参数 | 未初始化全局 / 静态变量 |
| 管理方式 | 编译器自动管理 | 链接器静态分配 |
| 生命周期 | 随函数调用结束消失 | 整个程序运行周期 |
| 初始化方式 | 无,默认随机值 | 启动文件自动清零 |
2. 堆区(Heap)------ 手动管理
存储内容:程序员通过动态内存函数申请的内存,如 C 标准库的malloc()、calloc()、realloc(),单片机裸机开发中也可自定义堆区申请函数;
管理方式:程序员手动分配和释放------ 申请后必须用free()释放,否则会造成内存泄漏;
核心特性:① 地址向上生长(从低地址向高地址分配内存);② 无固定大小限制(最大为 RAM 中未被其他区域占用的剩余空间);③ 是单片机动态内存分配的唯一方式(如运行时根据需求创建数组、缓冲区);
与 BSS 段的核心区别:BSS 段是静态分配(编译链接时确定大小),堆区是动态分配(运行时确定大小);BSS 段由系统管理,堆区由程序员手动管理。
三、对比表
| 内存层级 | 区域名称 | 存储核心内容 | 映射 | 分配阶段 | 管理方式 | 初始化方式 | 生命周期 |
|---|---|---|---|---|---|---|---|
| 逻辑段(静态) | text | 机器码、const 只读常量 | Flash | 编译链接 | 链接器 | 编译固化,不可修改 | 整个程序运行期 |
| 逻辑段(静态) | rodata | 字符串常量、只读数据 | Flash | 编译链接 | 链接器 | 编译固化,不可修改 | 整个程序运行期 |
| 逻辑段(静态) | data | 已初始化全局 / 静态变量 | RAM | 编译链接 | 链接器 | 启动时从 Flash 拷贝到 RAM | 整个程序运行期 |
| 逻辑段(静态) | bss | 未初始化全局 / 静态变量 | RAM | 编译链接 | 链接器 | 启动文件自动清零(赋 0) | 整个程序运行期 |
| 运行时区域(动态) | 栈区 | 局部变量、函数参数、返回地址 | RAM | 程序运行 | 编译器自动 | 无,默认随机值 | 随函数调用结束 |
| 运行时区域(动态) | 堆区 | 动态申请的内存(malloc 等) | RAM | 程序运行 | 程序员手动 | 无,默认随机值(calloc 除外) | 手动释放前 |
四、实践
使用CMake中的辅助工具,可以清晰看到代码编译后的内存分配

data段数据示例(static uint8_t 编译后被归类到RAM data段)

bss数据段示例(未进行初始化或初始化为0的数据被编译归类为RAM bss段,注:STATE_WAIT_START = 0)

text数据段示例(函数SQUARE_Init被编译后归类到Flash text)

rodata数据段示例(const字体数组被编译归类到Flash rodata段)
