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

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


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

相关推荐
碧海银沙音频科技研究院4 小时前
基于炬芯ATS308X 设计的上位机usb 串口音乐EQ调节工具
stm32·单片机·嵌入式硬件
三佛科技-134163842127 小时前
过认证72W碳化硅方案(12V6A)主控HN1562+HN36228V 典型应用电路、变压器参数分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
Zwawa8 小时前
串口为什么会丢字节:从轮询到中断和环形缓冲区
c语言·单片机·嵌入式
Brilliantwxx8 小时前
【STM32】 内部 Flash 详解:寄存器逐位拆解 + 工作流程顺序全讲 + EEPROM再实现
stm32·单片机·嵌入式硬件·1024程序员节
木子单片机8 小时前
基于单片机的DS18B20温度传感器测温系统设计
单片机·嵌入式硬件·51单片机·keil
华清远见IT开放实验室9 小时前
实验室建设案例 | 安徽三联学院物联网实验室
stm32·单片机·嵌入式硬件·物联网·华清远见
Zw-awa9 小时前
串口为什么会丢字节:从轮询到中断和环形缓冲区
stm32
深圳光特通信豆子10 小时前
TTL 光模块在工业控制与串口通信中的应用指南(10M BIDI 单模系列)
单片机·嵌入式硬件
桀人10 小时前
SPI概念引入
stm32·单片机·嵌入式硬件
离凌寒11 小时前
一、基于stm32+freertos下LD2410D和R60ADB1两款雷达的使用效果
stm32·单片机·嵌入式硬件