【问题标题】:Plainly and simply, why do we use _stdcall?说白了,我们为什么要使用_stdcall?
【发布时间】:2012-11-19 19:41:02
【问题描述】:

我在研究使用 C++ 制作游戏的状态时遇到了调用约定。

在之前的问题中,有人说 MSDN 没有很好地解释 _stdcall - 我同意。

调用像 _stdcall 这样的约定的主要目的是什么?参数放在堆栈上的顺序是否重要?它如何减少 X86 中代码的大小(正如其他人所说)?

【问题讨论】:

  • 如果一个函数有很多参数和很多调用者,那么被调用者在一个地方清理堆栈比每个调用者在多个地方清理堆栈更有意义。
  • 您是在询问是什么推动了不同调用约定的设计,或者为什么需要指定它们?
  • 更多关于它们实际使用的原因,在简单人的谈话中:P MSDN 非常复杂,似乎让很多人感到困惑!

标签: c++ calling-convention stdcall


【解决方案1】:

some 调用约定的原因很简单:以便调用者和被调用者就事情的工作方式达成一致。没有它,调用者在调用特定函数时不知道将参数放在哪里。

至于微软为何决定_stdcall 的具体细节,这很大程度上是历史性的。在 MS-DOS 上,所有调用都是基于寄存器的,因此所有操作系统调用都需要汇编语言,或者对大多数高级语言进行奇怪的扩展。

当他们第一次做 Windows 时,他们使用 cdecl 调用约定,主要是因为这是编译器默认的做法。至少根据谣言,在他们准备发布 Windows 1.0 之前不久,他们切换到 Pascal 调用约定,因为它足够高效,(除其他外)它允许 Windows 安装在更少的软盘上。不管精确细节如何,Pascal 调用约定确实使代码更小,因为被调用函数从堆栈中清理了参数,而不是在调用函数的任何地方都需要清理它们。对于从至少 2 个不同的地方调用的任何函数,这是一个胜利(如果它在其他任何地方打成平手)。

然后他们开始在 OS/2 上工作,并发明了另一种调用约定 (syscall)。

然后,当然是 Win32。从技术角度来看,系统调用并没有太多问题,但是(我猜)与 OS/2 相关的所有内容都被认为是受污染的,所以系统调用必须离开。结果是不同的东西足以证明一个新名字的合理性。公平地说,这有点夸张:他们确实添加了一个真正有用的添加:他们将参数的字节数编码到每个函数名称中,因此如果(例如)您为函数提供了不正确的原型,代码将'不链接而不是最终导致调用者和被调用者之间的不匹配,这可能导致更严重的问题。

在大多数情况下,它确实回到了原始点:调用约定的确切细节并不重要,只要你不把它弄得一团糟。最重要的是调用者和被调用者在同一件事上达成一致,因此如果编译器知道函数接受哪些参数,它就知道如何生成代码以正确地将这些参数传递给函数(同样,它们都同意如何处理堆栈清理等)

【讨论】:

  • 啊,这很好地解释了为什么 5 甚至首先存在;我原以为他们会保留看起来最有效和更新的那个,而放弃其余的!干杯。这仍然不能完全解释为什么我们需要使用一个?纯粹是因为不同的编译器使用不同的默认调用约定,所以通过指定一个我们在所有编译器/操作系统中使用统一的调用方法?如果是这样,我使用它们是一个好习惯吗?还是我在教育期间不应该担心它们?
  • 调用约定是编译器编写者争论的话题之一,因此他们不会感到无聊 :) 这是一个低级细节,您可以放心地忽略很长时间。
  • 此外,一些调用约定有助于提供其他调用约定无法提供或难以提供的附加“功能”。例如,cdecl 将堆栈清理放在调用者而不是被调用者身上。这更容易促进可变长度参数,因为只有调用者知道堆栈有多大。在这种情况下,调用者是唯一知道如何清理堆栈并对此负责的人。对于stdcall,唯一可行的方法是调用者通知callee 堆栈有多大,但我还没有看到以这种方式实现。
  • “我原以为他们会保留看起来最高效和更新的那个,而放弃其余的!”做某事的正确方法通常不止一种。
  • @Chopnut:主要是把栈指针恢复到它开始的地方,所以返回地址在栈顶(所以当它返回时,它会到正确的地方)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 2016-02-01
  • 2021-01-01
相关资源
最近更新 更多