栈的本质就是一段内存,程序运行时用于保存一些临时数据。栈大体可以分为四种类型空增、空减、满增、满减。ARM处理器一般使用满减栈。
目录
1、栈的分类
2、压栈/出栈的两种实现方式
(1) 方式一:使用后缀DB / IA
(2) 方式二:使用后缀FD
3、应用场景
1、栈的分类
从压栈的方向来看,可以分为增栈和减栈。所谓增栈指的是入栈时,数据是由低地址往高地址的方向存储的。减栈正好相反。
从指针移动和压栈的顺序来看,可以分为满栈和空栈。所谓满栈,指的是先移动栈顶指针,再存数据,栈顶指针最终指向的内容一定不为空;空栈恰恰相反,先存储数据,然后再把栈顶指针移动到下一个位置,此时栈顶指针最终指向的内容一定为空。
增/减栈 与 空/满栈两两组合,就得到了栈的四种基本类型:空增(EA)、空减(ED)、满增(FA)、满减(FD)。ARM处理器一般使用满减栈。
2、压栈/出栈的两种实现方式
(1) 方式一:使用后缀DB / IA
压栈其实就是将某个寄存器的数据保存到内存中,出栈其实就是从内存中取出数据保存到寄存器。因为ARM处理器是满减栈,入栈的时候,要先让地址自减然后再存数据,这就对应后缀 DB(Decrease Before)
出栈的时候,因为是满减栈,栈顶指针指向的内容一定不为空,此时应该是先取出数据,然后再让地址自增,这就对应后缀 IA(Increase After)
MOV SP!, #0x40000020 @ 初始化栈顶指针
MOV R1, #1
MOV R2, #2
MOV R3, #3
STMDB SP!, {R1-R3} @ 入栈
LDMIA SP!, {R4-R6} @ 出栈
入栈测试:
出栈测试:
(2) 方式二:使用后缀FD
上面的方法虽然可以达到效果,但是入栈和出栈使用不同的后缀,难免会不大方便,所以栈的四种类型空增(EA)、空减(ED)、满增(FA)、满减(FD)都可以直接作为后缀使用,达到的效果和方式一相同。
MOV SP!, #0x40000020 @ 初始化栈顶指针
MOV R1, #1
MOV R2, #2
MOV R3, #3
STMFD SP!, {R1-R3} @ 入栈
LDMFD SP!, {R4-R6} @ 出栈
本质还是会被转换成方式一的指令
3、应用场景
发生函数调用跳转时,我们需要把寄存器中的数据暂时保存到内存,等函数调用结束,再把数据从内存中取出来。
MOV SP, #0x40000020 @ 初始化栈顶指针
MAIN:
MOV R1, #1
MOV R2, #2
BL FUNC @ 函数跳转
ADD R3, R1, R2
B STOP
FUNC:
STMFD SP!, {R1-R2} @ 保存原本的R1-R2寄存器的值
MOV R1, #4
MOV R2, #3
SUB R4, R1, R2
LDMFD SP!, {R1-R2} @ 恢复R1-R2寄存器的值
MOV PC, LR @ 跳转以后不会自动回去,需要自己手动设置PC
STOP:
B STOP
发生跳转以后R1-R2的值
回到回到跳转位置的下一个位置时,R1-R2的值