函数调用时,栈的变化?
2026/8/21大约 2 分钟
函数调用与栈:LR 到底保存在哪里?
在 Cortex-M 中,函数调用和栈帧的关系可以一句话概括:
函数调用产生返回地址,返回地址先放在 LR;如果当前函数还要继续调用其他函数,就需要把 LR 保存到自己的栈帧中。
1. 函数调用:返回地址先进入 LR
例如:
void A(void)
{
B();
}执行:
BL BBL 做两件事:
PC → B()
LR ← B()执行结束后返回A的位置所以此时:
LR = 返回地址
PC = BLR 是寄存器,不是栈。
2. 为什么 LR 还要保存到栈?
假设:
void B(void)
{
C();
}进入 B() 后:
LR = 返回 A 的地址但是 B 又执行:
BL C这时:
LR = 返回 B 的地址原来“返回 A 的地址”就被覆盖了。
所以 B 通常会在进入函数时:
PUSH {r4, lr}把 LR 保存到B 自己的栈帧。

B 的栈帧
┌──────────────┐
│ 保存的 LR │ ← 返回 A 的地址
│ 保存的 R4 │
│ 局部变量 │
└──────────────┘于是 B 可以放心调用 C。
3. 函数返回时
B 执行完:
POP {r4, pc}恢复之前保存的 LR,同时把它恢复到 PC:
保存的 LR
↓
PC
↓
返回 A所以整个过程可以记成:
A 调用 B
↓
BL B
↓
返回地址 → LR
↓
B需要继续调用其他函数?
↓
是
↓
LR → 保存到 B 的栈帧
↓
B 调用 C
↓
B 返回
↓
恢复 LR → PC
↓
回到 A4. 和 FreeRTOS 任务切换的联系
普通函数调用:
LR
↓
必要时保存到当前函数栈帧FreeRTOS 任务切换:
CPU上下文
↓
保存到当前任务自己的栈
↓
切换 PSP
↓
恢复下一个任务的上下文所以本质上都是:
把“以后还要继续执行所需要的现场”保存起来。
区别只是:
函数调用保存的是函数调用现场;任务切换保存的是整个任务的运行现场。

