函数调用时,返回地址和局部变量都存“栈”里

你有没有想过一个问题:main函数调用func,func里调用sub。sub执行完,怎么回到func刚才的位置?func执行完,怎么回到main刚才的位置?**答案是:栈。**每次函数调用,CPU把"怎么回来"的地址(LR)和局部变量都塞进一块叫"栈"的内存里。函数返回时,再从栈里弹出来。这就是函数能够"嵌套调用、正确返回"的秘密。

那个"LR"寄存器的角色 ARM32有一个专门的寄存器叫LR(链接寄存器,R14)。

调用函数时:

main: BL func ; 跳转前,硬件自动把返回地址存入LR

BL指令执行时,CPU把BL的下一条指令地址(main里func后面的代码)存入LR。

然后跳转到func函数。func执行完,执行BX LR------跳转到LR指向的地址,回到main继续执行。

那个"嵌套"的问题, 如果func里又调用了sub:func中的BL sub会覆盖LR。问题:sub执行完后,LR存的是sub的返回地址(func里sub后面的代码)。那func自己的返回地址(main里的地址)怎么办?必须存到栈里。

那个"压栈"的时机, 编译器会自动在每个函数开头插入PUSH指令,把LR压入栈。

func:

PUSH {LR} ; 保存返回地址

BL sub ; 调用sub

POP {PC} ; 恢复LR到PC,返回

嵌套多层调用,返回地址就被一层层压在栈里。后调用的先返回------栈的特性。

那个"局部变量"也存栈里函数内部的局部变量,也分配在栈里。

void func(void) {

int a = 1; // 栈上分配4字节

int b = 2; // 栈上分配4字节

int c = a + b; // 栈上的变量参与运算

}

编译后,函数开头会SP减去若干字节 ,为局部变量腾出空间(PUSH或SUB SP)。函数返回前,SP加回相同的字节数 ,释放栈空间。局部变量的生命周期 = 函数的生命周期。

**那个"栈帧"的概念,**每次函数调用,都在栈上开辟一块区域:

  • 返回地址(LR)
  • 局部变量
  • 保存的寄存器(R4-R11等)

这块区域叫栈帧 (Stack Frame)。多层调用,栈帧一个叠一个。那个"溢出"的风险, 栈空间不是无限的。如果函数嵌套太深,或者局部变量太大,栈可能越界。栈溢出后,数据覆盖了其他内存区域(如全局变量、堆)。Bug诡异,难以排查。

这个故事的启示, 为什么函数能正确返回?因为栈记住了"来时的路" 。LR只能存一个返回地址,栈可以存无数个。函数嵌套多深,栈就能记多远。

写在最后, 下次你写函数,别只管业务逻辑。想想背后那个叫"栈"的东西。它在默默帮你记住"怎么回来"。栈,是函数的"记忆体"。


(本文灵感源于于振南《新概念ARM32单片机》教程第5.3节"程序现场的存储与恢复:栈与MSP",感谢作者将函数调用的底层机制讲得如此通透。)


如果您觉得这个故事对您有启发,欢迎点赞、转发,让更多工程师看到这个藏在函数调用背后的"栈"智慧。

相关推荐
xx~t1 小时前
嵌入式学习——ARM1
linux·arm开发·嵌入式硬件·arm
xx~t1 小时前
嵌入式——ARM1(详细篇)
linux·arm开发·嵌入式硬件·arm
周洲08302 小时前
STM32 ADC 采集实战|电压采集、分压计算、数据滤波
stm32·单片机·嵌入式硬件
狂奔蜗牛(bradley)3 小时前
搞定100ns抖动:ARM+FPGA混合EtherCAT主站实战方案
嵌入式硬件·fpga开发
SKH.3 小时前
单片机(2)
单片机
HZZD_HZZD3 小时前
CSDN_批发市场水电漏损归因算法LAM的原理与落地
嵌入式硬件·物联网·算法
DaTou大头3 小时前
STM32第三篇
单片机·嵌入式·电路基础
H_oRIZoN_4 小时前
Linux入门DAY40(51 单片机入门:GPIO、中断、定时器、PWM、蜂鸣器完整梳理)
单片机·嵌入式硬件
LCG元5 小时前
STM32F103 复位来源诊断实战:RCC_CSR 标志位判优、BKP 复位计数持久化与六种复位源注入实测
stm32·单片机·嵌入式硬件
HUI-4746 小时前
EG1177|SOP‑7 内置 100V‑NMOS 异步降压,20‑90V 高压恒压恒流小功率电源优选
嵌入式硬件·硬件工程