STM32关于堆栈,局部变量全局变量内存分配的问题

STM32关于堆栈,局部变量全局变量内存分配的问题


  • 开发环境:IAR for ARM 8.30.1
  • MCU:STM32F103RCT6

以一个简单的bootloader程序为例,先来看看map文件中的内存分配:

*******************************************************************************
*** PLACEMENT SUMMARY
***

"A0":  place at 0x800'0000 { ro section .intvec };		//ROM的起始位置,从这里开始烧录程序
"P1":  place in [from 0x800'0000 to 0x800'4000] { ro };	//整个Bootloader代码的存储空间
define block CSTACK with size = 1K, alignment = 8 { };	//编译器中定义的栈大小,1K字节
define block HEAP with size = 0M, alignment = 8 { };	//没有用到malloc之类的函数,设置为0
"P2":  place in [from 0x2000'0000 to 0x2000'bfff] {		//RAM的地址空间,分三个部分
          rw, block CSTACK, block HEAP };
initialize by copy { rw };

RAM空间地址的三个部分划分:
.data:初始化的全局和静态变量;
.bss:未初始化的全局和静态变量,编译器自动初始化为0;
CSTACK:存放局部变量,局部变量超过栈大小将会发生堆栈溢出,程序崩溃;

"P2", part 1 of 3:                           0x89
  P2-1                        0x2000'0000    0x89  <Init block>
    .data            inited   0x2000'0000     0x4  stm32f1xx_hal.o [1]
    .data            inited   0x2000'0004     0x4  system_stm32f1xx.o [1]
    .data            inited   0x2000'0008    0x38  xlocale_c.o [2]
    .data            inited   0x2000'0040    0x48  xlocale_c.o [2]
    .data            inited   0x2000'0088     0x1  stm32f1xx_hal.o [1]
                            - 0x2000'0089    0x89

"P2", part 2 of 3:                         0x10dc
  .bss               zero     0x2000'008c  0x1000  boot.o [1]
  .bss               zero     0x2000'108c    0x58  spi.o [1]
  .bss               zero     0x2000'10e4    0x40  usart.o [1]
  .bss               zero     0x2000'1128    0x20  stm32f1xx_hal_flash.o [1]
  .bss               zero     0x2000'1148    0x10  spi_flash.o [1]
  .bss               zero     0x2000'1158     0x4  spi_flash.o [1]
  .bss               zero     0x2000'115c     0x4  spi_flash.o [1]
  .bss               zero     0x2000'1160     0x4  stm32f1xx_hal.o [1]
  .bss               zero     0x2000'1164     0x4  xfail_s.o [2]
                            - 0x2000'1168  0x10dc

"P2", part 3 of 3:                          0x400
  CSTACK                      0x2000'1168   0x400  <Block>
    CSTACK           uninit   0x2000'1168   0x400  <Block tail>
                            - 0x2000'1568   0x400

这里查看系统剩余的ROM和RAM空间:

Unused ranges:

         From           To    Size
         ----           --    ----
   0x800'3cbd   0x800'4000   0x344
  0x2000'0089  0x2000'008b     0x3
  0x2000'1568  0x2000'bfff  0xaa98

1.堆栈

  • 在IAR中的设置:
    CSTACK大小决定了你函数中局部变量的总大小,由系统自动分配和释放,如果定义的局部变量大小超过了栈大小,虽然编译能通过,但是程序无法运行;同样,将CSTACK设置为0,编译器也能编译通过,但程序无法运行
    在这里插入图片描述

2.全局变量初始化问题

这里的全局变量还包括static声明的静态变量,初始化与未初始化存放的位置不同,初始化为不同的值在占用ROM大小也是不同的

从下面的示例可以看出,全局变量不初始化和初始化为0占用内存一样的,而且他们都是分配在全局未初始化区,系统初始化的时候统一赋值为0;

而初始化为1或者其他值,就会额外占用ROM的空间来存放你要初始化的常数,而且还可以看到初始化为不同的值,占用的ROM和RAM还不一样,下面就有RAM占用反而变小了的情况,这是因为这个变量存放的位置变成了全局初始化区,会受到编译器优化和数据对齐的影响;

  • 未初始化
u32 update_file_mark;
void Boot_Initializes(void)
{
  u16 i,j;
//以下代码省略
占用内存情况:
  14 684 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 481 bytes of readwrite data memory
  • 初始化为0
u32 update_file_mark = 0;
占用内存情况:
  14 684 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 481 bytes of readwrite data memory
  • 初始化为1
u32 update_file_mark = 1;
占用内存情况:
  14 684 bytes of readonly  code memory
     873 bytes of readonly  data memory
   5 477 bytes of readwrite data memory
  • 初始化为0xFFFFFFFF
u32 update_file_mark = 0xFFFFFFFF;
占用内存情况:
  14 684 bytes of readonly  code memory
     872 bytes of readonly  data memory
   5 477 bytes of readwrite data memory

3.局部变量初始化的问题

还是那个变量,这次放到函数内部让它成为局部变量

可以看到,局部变量初始化与否不会影响到RAM的分配变化,这个从RAM的空间划分的三个部分也可以看出
仅仅影响ROM而且是readonly code memory的变化,其中:
不初始化最节省ROM,初始化为0其次,初始化为其他值可能占用更多的ROM

  • 未初始化
void Boot_Initializes(void)
{
  u16 i,j;
  u32 update_file_mark;
占用内存情况:
  14 676 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 477 bytes of readwrite data memory
  • 初始化为0
void Boot_Initializes(void)
{
  u16 i,j;
  u32 update_file_mark  = 0;
占用内存情况:
  14 680 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 477 bytes of readwrite data memory
  • 初始化为1
void Boot_Initializes(void)
{
  u16 i,j;
  u32 update_file_mark  = 1;
占用内存情况:
  14 680 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 477 bytes of readwrite data memory
  • 初始化为0xFFFFFFFF
void Boot_Initializes(void)
{
  u16 i,j;
  u32 update_file_mark  = 0xFFFFFFFF;
占用内存情况:
  14 684 bytes of readonly  code memory
     869 bytes of readonly  data memory
   5 477 bytes of readwrite data memory
<think>好的,我需要回答用户关于STM32全局变量局部变量和静态变量存储的问题。首先,我应该回顾一下这些变量类型的基本概念以及它们在C语言中的存储方式,然后再结合STM32的架构来具体说明。 首先,全局变量是在函数外部定义的变量,作用域是整个程序,生命周期从程序开始到结束。局部变量则是在函数内部定义的变量,作用域仅限于函数内部,生命周期在函数调用时创建,函数返回时销毁。静态变量分为静态全局变量和静态局部变量,静态全局变量的作用域仅限于定义它的文件,而静态局部变量在函数内部定义,但生命周期延长到整个程序运行期间,且作用域仍限于函数内部。 接下来,我需要将这些变量类型对应到STM32的内存结构中。STM32的内存主要分为Flash和RAM。Flash用于存储程序代码和常量数据,而RAM用于运行时数据,包括堆栈、堆以及全局和静态变量。 全局变量和静态变量(无论是静态全局还是静态局部)都存储在RAM中的静态存储区。这个区域在程序启动时分配,直到程序结束才释放。局部变量则存储在栈(Stack)中,栈是一种后进先出的数据结构,由编译器自动管理,函数调用时分配,返回时释放。动态分配的内存(如malloc分配的)则在堆(Heap)中,但用户的问题中没有提到堆,所以可能不需要深入讨论。 需要注意的是,静态局部变量虽然作用域在函数内,但生命周期是全局的,因此它们的存储位置和全局变量相同。静态全局变量与普通全局变量的区别在于链接属性,静态全局变量只在定义它的文件内可见,而普通全局变量可以被其他文件通过extern声明访问,但存储位置都在静态存储区。 在STM32的具体实现中,链接脚本(Linker Script)会定义内存区域的分配,例如指定.data段(已初始化的全局和静态变量)和.bss段(未初始化的全局和静态变量)的位置。启动文件(Startup File)负责在复位后将这些变量从Flash拷贝到RAM中,并进行初始化。对于未初始化的.bss段,则清零处理。 用户可能需要了解这些变量在内存中的具体地址分配,以及如何通过调试工具查看它们的位置。此外,优化存储空间的方法,比如减少全局变量的使用以节省RAM,或者使用const关键字将常量放入Flash,也是值得提及的。 在解释过程中,需要确保术语准确,比如区分静态存储区、栈、堆,以及不同变量的初始化和生命周期。同时,结合STM32的具体内存地址,例如Flash通常起始于0x08000000,RAM起始于0x20000000,可以举例说明变量的存储位置。 可能还需要提到编译器和链接器的作用,例如如何通过编译器指令(如static、extern)控制变量的存储和可见性,以及链接脚本如何配置内存区域的大小和位置。 最后,总结时应该简明扼要,强调不同变量类型的存储位置及其对程序设计和资源使用的影响,帮助用户优化代码和内存使用。</think>在STM32开发中,变量存储位置直接影响内存使用和程序性能。以下是全局变量局部变量和静态变量的存储方式及特点: --- ### 1. **全局变量(Global Variables)** - **存储位置**: - 已初始化的全局变量 → **`.data`段**(RAM) - 未初始化的全局变量 → **`.bss`段**(RAM,启动时自动清零) - **生命周期**:程序启动时分配,直到程序结束释放。 - **作用域**:整个工程(若未加`static`修饰,其他文件可通过`extern`访问)。 - **示例**: ```c int global_var = 10; // 存储在.data段 char uninit_global; // 存储在.bss段 ``` --- ### 2. **局部变量(Local Variables)** - **存储位置**:**栈(Stack)**(RAM中动态分配)。 - **生命周期**:函数调用时分配,函数返回时释放。 - **作用域**:仅在定义它的函数或代码块内有效。 - **注意事项**: - 栈空间有限(STM32默认栈大小通常为1~4KB,可在启动文件中配置)。 - 避免定义过大的局部变量(如大数组),可能导致栈溢出。 - **示例**: ```c void func() { int local_var = 20; // 存储在栈中 } ``` --- ### 3. **静态变量(Static Variables)** #### (1) **静态全局变量(Static Global Variables)** - **存储位置**:与普通全局变量相同(`.data`或`.bss`段)。 - **生命周期**:同全局变量。 - **作用域**:仅在定义它的文件内可见(通过`static`限制链接属性)。 - **示例**: ```c static int static_global = 30; // 其他文件无法通过extern访问 ``` #### (2) **静态局部变量(Static Local Variables)** - **存储位置**:`.data`或`.bss`段(与全局变量相同)。 - **生命周期**:程序启动时分配,直到程序结束释放。 - **作用域**:仅在定义它的函数内可见。 - **特点**: - 即使函数退出,变量的值仍保留。 - 首次调用时初始化,后续调用跳过初始化。 - **示例**: ```c void func() { static int static_local = 0; // 存储在.data段 static_local++; } ``` --- ### 4. **存储区域对比** | 变量类型 | 存储位置 | 生命周期 | 作用域 | 初始化特点 | |-------------------|------------------|--------------------|---------------------------|------------------------------| | 全局变量 | `.data`/`.bss` | 程序运行全程 | 全局(跨文件可见) | 启动时初始化(`.bss`段清零) | | 静态全局变量 | `.data`/`.bss` | 程序运行全程 | 文件内可见 | 同全局变量 | | 静态局部变量 | `.data`/`.bss` | 程序运行全程 | 函数内可见 | 首次调用初始化 | | 局部变量 | 栈(Stack) | 函数调用期间 | 函数或代码块内可见 | 每次调用重新初始化 | --- ### 5. **STM32内存分配示例** 假设STM32F103的RAM起始地址为`0x20000000`,Flash起始地址为`0x08000000`: - **`.data`段**:存放已初始化的全局/静态变量,**实际数据存储在Flash中**,程序启动时由启动文件(如`startup_stm32f103xe.s`)自动拷贝到RAM。 - **`.bss`段**:存放未初始化的全局/静态变量,启动时由启动文件清零。 - **栈和堆**:位于RAM末尾,通过链接脚本(`.ld`文件)定义大小(例如:`_estack = 0x20005000;`)。 --- ### 6. **优化建议** 1. **减少全局变量使用**:全局变量占用RAM全程,可能导致内存不足。 2. **使用`const`关键字**:将常量数据存入Flash(如`const uint8_t table[] = {1,2,3};`)。 3. **控制栈大小**:在启动文件中调整`Stack_Size`,避免溢出。 4. **优先使用局部变量**:函数退出后自动释放,节省内存。 --- ### 总结 - **全局变量/静态变量** → 存储在RAM的`.data`或`.bss`段,生命周期长。 - **局部变量** → 存储在栈,生命周期短暂。 - **静态局部变量** → 结合了局部作用域和全局生命周期,适用于需要保持状态的场景。 理解这些存储特性有助于优化STM32的内存使用,避免资源浪费和运行时错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值