我们就用一个''开公司''的比喻,来帮你彻底搞懂它们的关系。

第一部分:硬件基础 - 公司的“仓库”与“办公室”

在MCU这个微型“公司”里,有两种最核心的物理资产:FlashSRAM

Flash (闪存) - 公司的“档案仓库”
  • 是什么? Flash是一种非易失性存储器 (Non-Volatile Memory)

  • 核心特性:“非易失”意味着断电后数据不会丢失。就像仓库里的档案柜,即使公司下班断电了,第二天来文件还在里面。

  • 作用:

    1. 存放程序代码 (Code): 你编译后生成的.bin.hex文件,也就是MCU要执行的所有指令,都烧录在这里。这是公司的**“操作手册和规章制度”**。

    2. 存放常量数据 (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)

  • 核心特性:“易失”意味着断电后数据会瞬间全部丢失。就像办公桌,下班人走清空,第二天来是干净的。

  • 作用:

    1. 存放变量 (Variables): 程序运行时,所有需要被修改、计算、暂存的数据,都放在SRAM里。这是员工(CPU)进行日常工作的**“办公桌面”**。

    2. 作为函数调用栈 (Stack): 函数调用、参数传递等,都在这里进行。

  • 特点:

    • 容量小: 因为其物理结构复杂且昂贵,容量通常比Flash小。

    • 速度极快: CPU可以直接高速读写SRAM,几乎没有延迟,保证了程序的运行效率。

    • 擦写次数近乎无限: 可以随意、频繁地读写。

总结一下硬件的区别:

特性 Flash (仓库) SRAM (办公室)
断电后数据 保留 (非易失) 丢失 (易失)
主要用途 存储程序常量 存储变量运行状态
读写速度 极快
容量大小
擦写寿命 有限 近乎无限

导出到 Google 表格


第二部分:C语言映射 - “员工”如何使用“仓库”和“办公室”

现在,我们来看看你写的C代码,是如何被分配到Flash和SRAM这两块地方的。MCU程序运行时的内存(主要是SRAM),通常被划分为以下几个区域:

存放在SRAM (办公室) 的内容:
  1. .data段 (已初始化数据段)

    • 存放什么: 全局变量和静态变量(static),且被初始化为非零值

    • C语言示例: int global_var = 100; static int static_var = 50;

    • 工作流程: 这些变量的初始值10050其实是先存放在Flash里的。MCU上电后,在执行main函数之前,会有一段启动代码先把这些值从Flash拷贝到SRAM的.data段。就像上班前,员工从仓库档案柜里拿出今天要用的资料,放到自己的办公桌上。

  2. .bss段 (未初始化数据段)

    • 存放什么: 全局变量和静态变量,但它们没有被初始化被初始化为0

    • C语言示例: int global_uninit_var; static int static_zero_var = 0;

    • 工作流程: 启动代码在初始化.bss段时,会直接把它整块区域清零。这样做可以节省Flash空间,因为不需要在Flash里存储一大堆的0。

  3. Heap (堆)

    • 存放什么: 程序动态申请的内存。

    • C语言示例: 通过malloc(), calloc()等函数申请的内存空间,用完后需要free()手动释放。int* p = (int*)malloc(4); 这里的p本身是个局部变量在栈上,但它指向的那4个字节的内存在堆上。

    • 特点: 堆是动态的、不连续的,使用不当容易产生内存碎片或泄漏。在很多裸机MCU项目中,为了稳定性和实时性,会尽量避免使用堆。

  4. Stack (栈)

    • 存放什么:

      • 函数的局部变量: void func() { int local_var = 10; }

      • 函数的参数

      • 函数的返回地址

    • 工作流程: 栈是一个“先进后出”的结构。每调用一个函数,系统就会在栈顶为它分配一块空间(称为“栈帧”)来存放它的局部变量等信息。函数返回时,这块空间会被自动释放。这是由编译器自动管理的,效率极高。

存放在Flash (仓库) 的内容:
  1. .text段 (代码段)

    • 存放什么: 程序的二进制代码。你编写的所有函数,最终都会变成机器指令存放在这里。

  2. .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语言示例:

    C

    int 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语言示例:

    C

    int g_uninit_var;
    static int g_zero_var = 0;
    
  • 存储位置: 运行时存储在 SRAM.bss

  • 详细说明: 为了节省Flash空间,编译器并不会在Flash里存储这些变量的初始值(0)。启动代码在初始化.bss段时,会直接将这整块内存区域全部清零。


3. const 修饰的全局常量 (const-qualified Global Constants)

这种变量被const关键字修饰,意味着它的值在程序运行期间不能被修改。

  • C语言示例:

    C

    const 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中,以便于程序进行快速的读写操作。区别仅在于它的初始值是如何被加载的。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐