Appearance
栈的类型
- 增栈:压栈之后,栈指针向高地址方向移动
- 减栈:压栈之后,栈指针向低地址方向移动
- 空栈:栈指针指向的是最后一次压入到栈中的数据相邻的内存空间,所以再压栈的时候可以直接压栈在移动指针
- 满栈:栈指针指向的是最后一次压入到栈中的数据,再压栈的时候需要先移动指针后压栈
- 所以栈可以分为满增(
FA)/满减(FD)/空增(EA)/空减(ED)四类 - ARM 使用满减栈:先移动有压栈,向低地址移动
满减栈的实现
- 可以使用多寄存器读写的方式实现满减栈
asm
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
LDR r11,=0x40000800
stmdb r11!,{r1-r5}
ldmia r11!,{r6-r10}- 可以采用
fd后缀实现满减栈
asm
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
LDR sp,=0x40000800
stmfd sp!,{r1-r5}
ldmfd sp!,{r6-r10}利用栈的函数调用
- 叶子函数调用过程
asm
.text
.global start
_start:
@初始化栈
LDR sp,=0X40000800
b main
main:
mov r1,#1
mov r2,#2
bl func
add r3,r1,r2
b main
func:
@压栈保护现场
stmfd sp!,{r1,r2}
mov r1,#3
mov r2,#4
sub r4,r2,r1
@出栈恢复现场
ldmfd sp!,{r1,r2}
mov pc,lr
.end- 每次的函数调用通过压栈备份寄存器
r1和r2的数据,等函数运行完毕,再通过弹栈把数据读回r1和r2
- 非叶子函数调用过程
asm
.text
.global start
_start:
@初始化栈
LDR sp,=0X40000800
b main
main:
mov r1,#1
mov r2,#2
bl func
add r3,r1,r2
b main
func:
@压栈保护现场
stmfd sp!,{r1,r2,lr}
mov r1,#3
mov r2,#4
bl fun
sub r4,r2,r1
@出栈恢复现场
ldmfd sp!,{r1,r2,lr}
mov pc,lr
fun:
@压栈保护现场
stmfd sp!,{r1,r2}
mov r1,#5
mov r2,#6
add r5,r1,r2
@出栈恢复现场
ldmfd sp!,{r1,r2}
mov pc,lr
.end- 上面代码中的
func是一个非叶子函数,它的区别在于压栈的时候保存了lr寄存器的数据,如果不保存lr的话,当它调用fun后,lr的数据就被改变了,func就无法回到main函数