嵌入式开发基础知识补充—内存的种类和C语言变量的存储位置(非易失性存储和易失性存储)
我们就用一个''开公司''的比喻,来帮你彻底搞懂它们的关系。
第一部分:硬件基础 - 公司的“仓库”与“办公室”
在MCU这个微型“公司”里,有两种最核心的物理资产:Flash 和 SRAM。
Flash (闪存) - 公司的“档案仓库”
-
是什么? Flash是一种非易失性存储器 (Non-Volatile Memory)。
-
核心特性:“非易失”意味着断电后数据不会丢失。就像仓库里的档案柜,即使公司下班断电了,第二天来文件还在里面。
-
作用:
-
存放程序代码 (Code): 你编译后生成的
.bin或.hex文件,也就是MCU要执行的所有指令,都烧录在这里。这是公司的**“操作手册和规章制度”**。 -
存放常量数据 (Constant Data): 代码中定义的常量,比如
const int a = 100;或者字符串"Hello World",也存放在这里。这些是**“不可更改的档案资料”**。
-
-
特点:
-
容量大: 通常MCU的Flash容量会比SRAM大很多(如512KB Flash vs 128KB SRAM)。
-
速度慢: CPU读取Flash的速度远慢于读取SRAM。
-
擦写次数有限: Flash有写入寿命限制(通常是1万到10万次),不适合频繁改写。
-
SRAM (静态随机存取存储器) - 公司的“办公区工位”
-
是什么? SRAM是一种易失性存储器 (Volatile Memory)。
-
核心特性:“易失”意味着断电后数据会瞬间全部丢失。就像办公桌,下班人走清空,第二天来是干净的。
-
作用:
-
存放变量 (Variables): 程序运行时,所有需要被修改、计算、暂存的数据,都放在SRAM里。这是员工(CPU)进行日常工作的**“办公桌面”**。
-
作为函数调用栈 (Stack): 函数调用、参数传递等,都在这里进行。
-
-
特点:
-
容量小: 因为其物理结构复杂且昂贵,容量通常比Flash小。
-
速度极快: CPU可以直接高速读写SRAM,几乎没有延迟,保证了程序的运行效率。
-
擦写次数近乎无限: 可以随意、频繁地读写。
-
总结一下硬件的区别:
| 特性 | Flash (仓库) | SRAM (办公室) |
| 断电后数据 | 保留 (非易失) | 丢失 (易失) |
| 主要用途 | 存储程序和常量 | 存储变量和运行状态 |
| 读写速度 | 慢 | 极快 |
| 容量大小 | 大 | 小 |
| 擦写寿命 | 有限 | 近乎无限 |
导出到 Google 表格
第二部分:C语言映射 - “员工”如何使用“仓库”和“办公室”
现在,我们来看看你写的C代码,是如何被分配到Flash和SRAM这两块地方的。MCU程序运行时的内存(主要是SRAM),通常被划分为以下几个区域:
存放在SRAM (办公室) 的内容:
-
.data段 (已初始化数据段)
-
存放什么: 全局变量和静态变量(
static),且被初始化为非零值。 -
C语言示例:
int global_var = 100;static int static_var = 50; -
工作流程: 这些变量的初始值
100和50其实是先存放在Flash里的。MCU上电后,在执行main函数之前,会有一段启动代码先把这些值从Flash拷贝到SRAM的.data段。就像上班前,员工从仓库档案柜里拿出今天要用的资料,放到自己的办公桌上。
-
-
.bss段 (未初始化数据段)
-
存放什么: 全局变量和静态变量,但它们没有被初始化或被初始化为0。
-
C语言示例:
int global_uninit_var;static int static_zero_var = 0; -
工作流程: 启动代码在初始化
.bss段时,会直接把它整块区域清零。这样做可以节省Flash空间,因为不需要在Flash里存储一大堆的0。
-
-
Heap (堆)
-
存放什么: 程序动态申请的内存。
-
C语言示例: 通过
malloc(),calloc()等函数申请的内存空间,用完后需要free()手动释放。int* p = (int*)malloc(4);这里的p本身是个局部变量在栈上,但它指向的那4个字节的内存在堆上。 -
特点: 堆是动态的、不连续的,使用不当容易产生内存碎片或泄漏。在很多裸机MCU项目中,为了稳定性和实时性,会尽量避免使用堆。
-
-
Stack (栈)
-
存放什么:
-
函数的局部变量:
void func() { int local_var = 10; } -
函数的参数
-
函数的返回地址
-
-
工作流程: 栈是一个“先进后出”的结构。每调用一个函数,系统就会在栈顶为它分配一块空间(称为“栈帧”)来存放它的局部变量等信息。函数返回时,这块空间会被自动释放。这是由编译器自动管理的,效率极高。
-
存放在Flash (仓库) 的内容:
-
.text段 (代码段)
-
存放什么: 程序的二进制代码。你编写的所有函数,最终都会变成机器指令存放在这里。
-
-
.rodata段 (只读数据段)
-
存放什么: 只读的常量数据。
-
C语言示例:
const int const_var = 10;以及字符串字面量,如printf("Hello World");里的"Hello World"。
-
// a.c 文件
#include <stdlib.h>
const int g_const_var = 10; // 存放在 Flash 的 .rodata 段
char* g_str = "Hello"; // g_str 指针本身在 .data 段 (SRAM)
// "Hello" 字符串常量在 .rodata 段 (Flash)
int g_init_var = 100; // 存放在 SRAM 的 .data 段 (初始值100来自Flash)
static int s_uninit_var; // 存放在 SRAM 的 .bss 段 (上电后被清零)
void my_func(int param) { // 函数代码本身在 Flash 的 .text 段
int local_var = 20; // local_var 和 param 都在 SRAM 的 栈(Stack)上
int* heap_var = (int*)malloc(4); // heap_var指针在栈上, 指向的那4字节在堆(Heap)上
*heap_var = 30;
free(heap_var);
}
int main(void) {
my_func(g_init_var);
return 0;
}
全局变量存储在哪里?
全局变量的存储位置取决于它是如何被定义的,主要分为以下三种情况:
1. 已初始化的全局变量 (Initialized Global Variables)
这种变量在定义时就被赋予了一个非零的初始值。
-
C语言示例:
Cint g_init_var = 100; char g_char_array[] = "Hello"; -
存储位置: 运行时存储在 SRAM 的
.data段。 -
详细说明: 它的初始值(比如
100和"Hello")是固化在Flash里的。当MCU上电启动时,在进入main函数之前,一段特殊的启动代码会负责把这些初始值从Flash拷贝到SRAM中的.data段区域。程序在运行时,实际读写的是SRAM中的这个变量。
2. 未初始化 或 初始化为0的全局变量 (Uninitialized or Zero-Initialized Global Variables)
这种变量在定义时没有初始值,或者初始值为0。
-
C语言示例:
Cint g_uninit_var; static int g_zero_var = 0; -
存储位置: 运行时存储在 SRAM 的
.bss段。 -
详细说明: 为了节省Flash空间,编译器并不会在Flash里存储这些变量的初始值(0)。启动代码在初始化
.bss段时,会直接将这整块内存区域全部清零。
3. const 修饰的全局常量 (const-qualified Global Constants)
这种变量被const关键字修饰,意味着它的值在程序运行期间不能被修改。
-
C语言示例:
Cconst int g_const_var = 10; const char* message = "This is a constant string"; -
存储位置: 直接存储在 Flash 的
.rodata(只读数据) 段。 -
详细说明: 因为它的值是只读的,没有必要在运行时把它拷贝到宝贵的SRAM中。CPU在需要读取这个常量时,会直接从Flash中读取。
总结表格
为了方便您记忆,这里有一个清晰的总结:
| 变量定义示例 | 运行时存储位置 | 内存区域 (Section) |
int g_init_var = 100; |
SRAM | .data |
int g_uninit_var; |
SRAM | .bss |
const int g_const_var = 10; |
Flash | .rodata |
核心要点回顾: 只要不是const修饰的全局变量,它在程序运行时最终都会存在于SRAM中,以便于程序进行快速的读写操作。区别仅在于它的初始值是如何被加载的。
更多推荐
所有评论(0)