push EAX
push 8
call malloc
pop EBX
pop EBX
mov [EAX], 0
mov [EAX+4], EBX
为什么我们需要做2次pop EBX?每次EBX会获得什么价值?
答案 0 :(得分:4)
基本规则是,无论你推动什么,都必须弹出。否则,您将使堆栈失衡并导致代码崩溃或更糟。该规则的含义是您需要弹出相同大小(以字节为单位)的值。
所以在这种情况下,你在调用malloc:
之前将8个字节压入堆栈push EAX ; push a DWORD-sized register (4 bytes)
push 8 ; push a DWORD immediate (4 bytes)
要在函数调用之后清理堆栈(malloc
需要,因为它使用cdecl
调用约定,这是调用者清理),你需要弹出8个字节。碰巧这样做的一种方便的方法是弹出一个寄存器大小的值两次:
pop EBX ; pop 4 bytes
pop EBX ; pop 4 bytes
堆栈是LIFO。
第一个pop将8
放入EBX
(因为这是你推送的最后一件事),你不关心。下一个弹出窗口将EAX
的原始值放回EBX
(您推送的第一个内容),然后您将继续使用它。
如果您不关心保留从堆栈中弹出的任何值,您可以简单地使用ADD指令,向堆栈指针添加8个字节:
add esp, 8
这可能比两个pop有点快,但它实际上稍微大一点(3个字节而不是2个字节,as Jester points out)有时优化代码大小与优化代码速度同样重要,因为当代码更小,更多可以放入缓存中。但在这种情况下,我怀疑更重要的问题是获得推动的第一个价值。由于malloc只接受一个参数,因此第一次推送的唯一原因是保留EAX
的原始值,因为它被函数调用破坏(函数在EAX
中返回它们的结果)。因此,编写代码的另一种方法是:
; Save EAX by moving it into a caller-save register
; (that will not get clobbered by the malloc function).
mov EBX, EAX
; Call the malloc function by pushing a 4-byte parameter and then rebalancing the stack.
push 8
call malloc
add esp, 4
; EAX contains malloc's return value, and EBX contains the original value of EAX
; that we saved before calling malloc.
mov [EAX], 0
mov [EAX+4], EBX