我在学习使用C ++进行游戏制作的状态时,遇到过调用惯例。
在上一个问题中,有人说MSDN没有很好地解释_stdcall - 我同意。
调用_stdcall等约定的主要目的是什么?参数放在堆栈上的顺序是否重要?它如何减少X86中的代码大小(正如其他人所说)?
答案 0 :(得分:8)
某些调用约定的原因非常简单:这样调用者和被调用者就如何工作达成一致。没有它,调用者在调用特定函数时不知道在哪里放置参数。
至于微软为什么决定_stdcall
的具体细节,那就是在很大程度上历史。在MS-DOS上,所有调用都是基于寄存器的,因此所有OS调用都需要汇编语言,或大多数高级语言的奇怪扩展。
当他们第一次使用Windows时,他们使用cdecl
调用约定,主要是因为这是编译器默认执行的操作。至少根据谣言,在他们准备发布Windows 1.0之前不久,他们改用了Pascal调用约定,因为它足够高效(除其他外)它允许Windows适合少一张软盘。无论精确的细节如何,Pascal调用约定确实使代码变得更小,因为被调用的函数清除了堆栈中的参数,而不需要在调用函数的任何地方清理它们。对于从至少2个不同位置调用的任何函数,这是一个胜利(如果它在其他任何地方都有关系)。
然后他们开始研究OS / 2,并发明了另一个调用约定(syscall)。
然后,当然,来了Win32。从技术角度来看,系统调用并没有太多问题,但是(我猜)与OS / 2相关的所有内容都被认为是污点,所以系统调用必须要去。结果只是足以证明新名称的合理性。公平地说,这有点夸张:他们确实添加了一个真正有用的补充:他们将参数的字节数编码到每个函数名中,所以如果(例如)你为函数提供了一个不正确的原型,那么代码就不会链接而不是最终导致调用者和被调用者之间的不匹配,这可能会导致更严重的问题。
在大多数情况下,它确实回到了最初的观点:调用约定的确切细节并不重要,只要你不完全弄乱它。大多数重要的是调用者和被调用者同意相同的事情,所以如果编译器知道函数接受什么参数,它知道如何生成代码以正确地获取这些参数(同样,他们都同意如何处理堆栈清理等。)