我相信我理解 STDCALL 和 CDECL 之间的区别,但我想知道是否可以在这段代码中找到一些说明。
我理解在 STDCALL 中 CALLEE 负责清理堆栈,并且我理解在 CDECL 中 CALLER 负责清理堆栈。
我也理解“清理堆栈”基本上意味着重新设置堆栈指针,但我想我的混乱出现在这行代码中,其中 esp 的值被移动到基指针 ebp 中。如果该功能发生,这与“清理堆栈”是一样的吗?或者它必须是专门进入 ESP 的东西吗?
这是我正在查看的代码
main PROC
push 4
push 5
call sub_12
push 5
call sub_48
add esp, 4
INVOKE ExitProcess, 0
main endp
sub_12 PROC
push ebp
mov ebp, esp
mov eax, 10
mul DWORD PTR [ebp+12]
pop ebp
ret 8
sub_12 endp
sub_48 PROC
push ebp
mov ebp, esp
mov eax, [ebp+8]
mul DWORD PTR [ebp+8]
pop ebp
ret
sub_48 endp
我原来的答案是sub_12和sub_48都是CDECL,因为Caller负责清理堆栈。但现在我一直在查看 [mov ebp, esp] 指令,我想知道这是否实际上是 STDCALL 的示例。
有人对我有任何提示或一些我可能缺乏的额外信息吗?
这里有一个关于 CDECL 与 STDCALL 的很好的讨论:
无论调用约定如何,被调用者通常都会将当前堆栈指针保存到帧指针(EBP),以便他可以随意将局部变量推入堆栈或从堆栈中拉出。
当他准备返回时,被调用者必须然后恢复堆栈指针(ESP)以便“返回”成功。
有两个问题:1)调用子例程(这部分是“stdcall”与“cdecl”(除其他外 - 这不是唯一的两个选项),2)从子例程返回。
CDECL 和 STDCALL 之间的主要区别在于谁负责在“返回”时清理堆栈中的局部变量。
被调用者ALWAYS恢复堆栈指针。这是“返回”可以正常工作的唯一方法。
对于 STDCALL,被调用者 ALSO 清除其自己的局部变量的堆栈。
粗略地说:
STDCALL 可能使用稍少的空间,因为“清理代码”仅存在于一个位置:在被调用者中。对于 CDECL 调用,必须在调用子例程的所有地方重复清理操作。您的示例 sub_12 是“STDCALL”。
CDECL 更灵活:它允许您将可变数量的参数传递到子例程中。您的示例 sub_48 是“CDECL”。