7

我知道 __stdcall 函数不能有省略号,但我想确保除了 __cdecl 或 __stdcall 之外,没有平台支持 stdarg.h 函数来调用约定。

4

5 回答 5

8

调用约定必须是调用者从堆栈中清除参数的约定(因为被调用者不知道将传递什么)。

不过,这不一定与微软所说的“__cdecl”相对应。举例来说,在 SPARC 上,它通常会在寄存器中传递参数,因为这就是 SPARC 的工作原理——它的寄存器基本上充当调用堆栈,如果调用足够深,就会溢出到主内存他们将不再适合注册。

虽然我不太确定,但我希望 IA64(安腾)上大致相同——它也有一个巨大的寄存器集(如果有记忆的话,有几百个)。如果我没记错的话,它对你如何使用寄存器更宽容一些,但我希望它至少在很多时候也能被类似地使用。

为什么这对你很重要?使用 stdarg.h 及其宏的目的是隐藏代码中调用约定的差异,因此它可以可移植地使用可变参数。

根据评论进行编辑:好的,现在我了解您在做什么(至少足以改善答案)。鉴于您已经(显然)有代码来处理默认 ABI 中的变化,事情就更简单了。这只留下了可变参数函数是否总是使用“默认 ABI”的问题,无论手头的平台碰巧是什么。使用“stdcall”和“default”作为唯一选项,我认为答案是肯定的。举个例子,在 Windows 上,wsprintf打破wprintf经验​​法则,使用 cdecl 调用约定而不是 stdcall。

于 2010-03-25T02:33:25.887 回答
2

确定这一点的最明确方法是分析调用约定。为了使可变参数函数起作用,您的调用约定需要几个属性:

  • 被调用者必须能够从堆栈顶部的固定偏移量访问不属于变量参数列表的参数。这要求编译器将参数从右到左压入堆栈。(这包括printf格式规范的第一个参数等内容。此外,变量参数列表本身的地址也必须从已知位置派生。)
  • 一旦函数返回,调用者必须负责从堆栈中删除参数,因为只有编译器在为调用者生成代码时,首先知道有多少参数被压入堆栈。可变参数函数本身没有此信息。

stdcall将不起作用,因为被调用者负责将参数从堆栈中弹出。在旧的 16 位 Windows 时代,pascal这是行不通的,因为它将参数从左到右推入堆栈。

当然,正如其他答案所暗示的那样,许多平台在调用约定方面没有给你任何选择,这使得这个问题与那些平台无关。

于 2010-03-25T03:35:51.987 回答
1
于 2016-04-13T07:24:46.013 回答
0

您的意思是“MSVC 支持的平台”还是一般规则?即使您将自己限制在 MSVC 支持的平台上,您仍然会遇到像IA64AMD64这样的情况,其中只有“一个”调用约定,并且调用约定是称为__stdcall,但它肯定与__stdcall你在 x86 上得到的不一样。

于 2010-03-25T02:38:36.013 回答
0

AFAIK,调用约定的多样性是 x86 上的 DOS/Windows 所独有的。大多数其他平台都有编译器随操作系统一起提供并标准化约定。

于 2010-03-25T03:31:30.967 回答