【发布时间】:2020-03-26 12:32:04
【问题描述】:
假设我有一个名为 func 的函数:
PROC func:
;Bla bla
ret
ENDP func
现在,假设我使用寄存器ax 和bx 为例,为了保存它们的初始值,我将它们推送到函数内部的堆栈中。
现在的问题是:在创建堆栈帧之前推送寄存器是否有很大不同:
PROC func:
push bp
push ax
push bx
mov bp, sp
;Bla bla
ret
ENDP func
还是之后?
PROC func:
push bp
mov bp, sp
push ax
push bx
;Bla bla
ret
ENDP func
我应该在我的程序中使用什么?一种方法比另一种更好或更“正确”吗?因为我目前用的是第一种方法。
【问题讨论】:
-
不,你会先
push bp然后push bp, sp。好处是,如果你在 开始 这样做,那么在 16 位代码中,第一个参数将始终位于[bp+4],第二个参数位于[bp+6]等,即使你做了更多的推送在做mov bp, sp之后在堆栈上。这对人类来说更容易维护代码。从高级编译器的角度来看,它并没有太大的区别。优点:可维护性。 -
不,从人类生成的汇编程序的可读性和可维护性的角度来看,您的朋友方法会更好恕我直言。事实上,编译器和人类生成的许多代码都遵循先推 bp,然后执行 mov bp,sp,然后在 ret 之前执行相反的模式。通常,开发人员会从 BP 的正偏移量和负偏移量的局部变量中找到读取参数。这是一个更常见的约定。
-
我认为您的困惑是
push bpmov bp, sp的情况在所有其他推送之前完成。所以它看起来像push bpmov bp, sppush axpush bx等等。我认识的大多数人都觉得这比push axpush bxpush bpmov bp, sp更可取。 -
@Kidsm:为了简化您的 asm,您可以编写简单地不保存它们使用的所有寄存器的函数。在 32 位调用约定中,允许函数销毁 EAX、ECX 和 EDX 而无需保存/恢复它们。拥有一些 call-clobbered aka volatile 暂存寄存器意味着对简单函数的推送/弹出更少。
-
不,我的意思是你推送 ONLY BP,然后是
mov bp, sp,然后你执行所有其他需要保存的推送。在您修改bp之前,您需要将其保存,以便可以将其恢复为原始值。你的第二个代码 sn-p 是错误的,因为它甚至不推送 bp。
标签: assembly x86 callstack stack-frame