ROM&RAM

第一部分:static 一般在哪里用?

static 在 C 语言里是个"多面手",它的核心作用是限定作用域 和延长生命周期。在嵌入式开发中,通常用在以下三个地方:

1. 修饰局部变量(延长生命周期)
  • 用法:写在函数内部的变量前。

  • 效果 :这个变量不再存放在栈(Stack)里,而是存放在静态存储区(全局区)。函数退出后,它的值不会丢失,下次调用该函数时,它还是上次的值。它只会被初始化一次。

  • 场景:状态机记录当前状态、计数器、或者防止重复初始化的标志位。

  • 你的代码里就有 :你之前发的 static uint16_t u16CurrentRefVol_X100 = EXTERNAL_REF_VOLT_X100;。它放在函数外面(其实属于下面第 2 点),但保留了上次运行的值。

2. 修饰全局变量(限定作用域)
  • 用法:写在函数外部的变量前。

  • 效果 :这个变量只能被当前 .c 文件内的函数访问 。其他文件就算用 extern 声明也找不到它。

  • 场景 :模块内部的私有变量。比如蓝牙模块里的"接收缓冲区",你不想让主函数随便乱动,就加上 static。这体现了封装思想 ,防止命名冲突(两个文件里都可以有叫 static int flag; 的变量,互不干扰)。

3. 修饰函数(限定作用域)
  • 用法:写在函数定义前。

  • 效果 :这个函数只能被当前 .c 文件内的代码调用。

  • 场景 :模块的内部辅助函数。比如你在 app_battery.c 里写了一个计算平均值的函数,只有这个文件里的 App_Battery_Task() 会用它,那就加上 static。这样能防止别的文件瞎调用,也减少了全局符号表的长度,节省编译器链接时间。

总结 :在工程架构中,除了你要对外暴露的接口(放在 .h 里),其他所有的全局变量和内部函数,统统加上 static。 这是高手的标配。


第二部分:ROM 和 RAM 一般怎么占用的?

单片机内部通常有 Flash(ROM,掉电不丢失) 和 SRAM(RAM,掉电丢失)。

当你写下一行代码,编译器会把它大卸八块,分别塞进 Flash 和 RAM 里。为了搞懂这个,你需要记住几个核心英文单词:

  • Code (代码):你写的所有函数逻辑。

  • RO-data (Read Only) :只读数据(如 const 修饰的常量、字符串 "Hello"、宏定义的常量表)。

  • RW-data (Read Write) :已初始化的全局/静态变量(比如 int a = 10;)。

  • ZI-data (Zero Init) :未初始化或初始化为 0 的全局/静态变量(比如 int b = 0; 或 int c;)。

1. Flash (ROM) 被谁占了?

Flash 是存放代码的地方,只要掉电不丢失。

  • Code:所有函数的机器指令。

  • RO-data :所有的常量和字符串(比如你代码里的 "HELLO")。

  • RW-data 的初始值 :变量 a = 10 中的那个 10,必须存在 Flash 里,否则掉电就忘了。

  • (注意:ZI-data 和真正的 RW-data 变量本体不在 Flash 里)。

2. RAM (SRAM) 被谁占了?

RAM 是程序运行时的工作台,速度极快,但容量很小(通常几 KB 到几百 KB)。

  • 栈 (Stack) :存放函数的局部变量、函数调用的返回地址。(如果不加 static,局部变量都在这里)。

  • 堆 (Heap) :存放 malloc 动态分配的内存。(嵌入式里极度不推荐使用 malloc,容易产生碎片)。

  • RW-data 的运行体 :程序刚上电时,会把 Flash 里存的初始值(10)复制到 RAM 里,让变量 a 真正拥有实体。所以 a 既占 Flash,又占 RAM。

  • ZI-data :变量 b 本体在 RAM 里,上电时系统直接把这块区域清零(不占 Flash 空间)。

💡 一个经典公式与例子

我们假设定义如下代码:

cpp 复制代码
const char str[] = "Hello"; // 占 Flash (RO)
int a = 10;                 // 占 Flash (RW初值) + 占 RAM (RW运行体)
int b;                      // 占 RAM (ZI)
void main() { 
    int c = 5;              // 占 RAM (栈 Stack)
}

编译后的结果(通常看 .map 文件):

  • 占用 Flash (ROM) = Code + RO-data + RW-data (初值)

  • 占用 RAM = RW-data (运行体) + ZI-data + Stack + Heap


⚠️ 嵌入式开发中的"血泪"经验(避坑指南)

  1. 大数组、大字符串,一定要加 const!

    • 如果你写了 char buf[1024] = ".......";(没加 const),这 1024 个字节不仅要在 Flash 里占 1KB,上电后还会被复制到 RAM 里再占 1KB。很多单片机 RAM 只有 20KB,你搞几个这种数组,程序直接就崩了(HardFault)。加上 const 后,它只占 Flash,不占 RAM。
  2. 防止栈溢出 (Stack Overflow):

    • 不要在函数里定义巨大的局部数组(比如 uint8_t temp[2048];)。局部变量在栈里,单片机的栈通常很小(启动文件里一般配置为 1KB 或 2KB)。定义这么大的局部数组,一运行就把栈冲垮了,导致程序跑飞。巨大的数组一定要放在全局区(或加 static)。
  3. 慎用 malloc:

    • 除非你跑 Linux 或者高性能 RTOS,在裸机单片机里,尽量用静态分配 (定义固定大小的全局数组 + 环形缓冲区 FIFO),完全不用 malloc。
  4. 区分 static 和 const:

    • const 管的是只读(放 Flash)。

    • static 管的是作用域和生命周期(放全局区 RAM/Flash)。

    • 如果写 static const uint8_t table[] = {...},它就是一个:只能在当前文件访问的、存在 Flash 里的只读数据(完美!)。

掌握了这些,在写代码时,脑子里就会有一张内存地图,知道变量到底去了哪里。下次在 Keil 或 VS Code 编译完成后,可以看看底部的输出信息(比如 Program Size: Code=1234 RO-data=56 RW-data=12 ZI-data=100)

相关推荐
创界工坊工作室2 小时前
41-基于STC89C52的避障循迹小车
stm32·单片机·嵌入式硬件·51单片机·proteus
库玛西2 小时前
外部排序全解析:败者树、置换-选择排序与最佳归并树
c语言·数据结构·c++·笔记·考研·算法
en.en..2 小时前
第一篇:位域打包:看懂 I2C 地址左移的底层逻辑
stm32·嵌入式·位运算·i2c·底层驱动
Zw-awa3 小时前
工程师的词典:缺省值、鲁棒性,和那些名不副实的怪词
stm32
youcans_3 小时前
【嵌入式软件AI编程】21. 整理通用提示词模板
stm32·单片机·嵌入式·ai编程
优化Henry3 小时前
LTE小区光路降质告警排查与处理实录
运维·网络·学习·5g·tdd
暖馒3 小时前
运动控制框架相关知识
学习
stereohomology3 小时前
我对DeepSeek4.1flash能力变化的感觉和第三方评估相当
人工智能·学习·llm
zhangzhangkeji4 小时前
stm32, HAL 库,定时器
stm32