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

你有没有想过一个问题: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",感谢作者将函数调用的底层机制讲得如此通透。)


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

相关推荐
猫猫的小茶馆13 小时前
【Python】函数与模块化编程
linux·开发语言·arm开发·驱动开发·python·stm32
feifeigo12313 小时前
STM32矩阵键盘驱动(库函数版)实现
stm32·矩阵·计算机外设
嵌入式小站14 小时前
STM32 零基础可移植教程 05:按键消抖,为什么按一次会触发好几次
chrome·stm32·嵌入式硬件
czhaii14 小时前
跟我动手学FX系列PLC GX2环境
嵌入式硬件
拾知_H15 小时前
STM32/Delay延时函数编程思路
stm32·单片机·时钟·延时
2zcode16 小时前
基于STM32的智能扫地机器人设计与实现
stm32·嵌入式硬件·机器人
jllllyuz16 小时前
单相并网逆变器控制代码实现(STM32版)
stm32·单片机·嵌入式硬件
冉卓电子18 小时前
GD32C103RBT6 misc 内核驱动库极简解析
单片机·嵌入式硬件
yongui4783418 小时前
MAX6675 K型热电偶温度采集程序(Keil环境)
单片机·嵌入式硬件
豆包公子18 小时前
AUTOSAR CP XCP 移植到裸机 MCU-实践篇
单片机·嵌入式硬件