函数参数中的省略号使用与普通参数相同的调用布局

时间:2015-11-13 15:43:17

标签: c++ language-lawyer variadic-functions

我想拨打extern "C"电话,例如f1(int a, float f, double d, void* ptr)使用带有实际参数的前向声明,但在实际实现中,我想使用va_list和朋友来pop args。让我们想象一下,我有一个有效的用例,是允许的吗?

的main.cpp

extern "C" void f(int anchor, int a, float f, double d, void* ptr, char c);

int main(int, char*)
{
   f(0, 42, 1.08f, 3.14, reinterpret_cast<void*>(0xcafebabe), 'c');
   return 0;
}

impl.cpp

#include <cstdarg>
#include <iostream>
#include <iomanip>

using namespace std;

void f(int anchor, ...)
{
  va_list args;
  va_start(args, anchor);

  int a = va_arg(args, int);
  float f = va_arg(args, float);
  double d = va_arg(args, double);
  void* ptr = va_arg(args, void*);
  char c = va_arg(args, char);

  cout << a << ' '
       << f << ' '
       << d << ' '
       << hex << (std::ptrdiff_t)ptr << ' '
       << (int)c << endl;

  va_end(args);
}

代码在MSVC 2015至少运行并打印正确的值,现在的问题是:它是否可以保证工作,如果没有:它是否可能适用于最重要的平台和编译器?

2 个答案:

答案 0 :(得分:2)

理论上,您的代码受到未定义行为的影响。事实上,当我在用g ++构建之后尝试运行程序时,我得到了

Illegal instruction (core dumped)

原因是当一个函数具有变量参数参数时,它可以处理的唯一事情是int s,double s,指针。它无法处理floatchar s。

如果声明被指定为:

extern "C" void f(int anchor, ...);

float将提升为doublechar将提升为int

在执行方面,您需要使用:

 float f = (float)va_arg(args, double);

 char c = (char)va_arg(args, int);

有关此主题的更多信息,请参阅Default argument promotions in C function calls

答案 1 :(得分:0)

某些平台要求每个功能必须接收一组固定的 参数。这样一个平台的C编译器可能会处理一些事情 像:

printf("%d %f %s", 9, 23.7, "Fred");

代码相当于:

union long_or_double_or_ptr { unsigned long l; double d; void *p; };
union long_or_double_or_ptr INTERNAL_ARGS_92512[3];
__INTERNAL_ARGS_92512[0].l = 9;
__INTERNAL_ARGS_92512[1].d = 23.7;
__INTERNAL_ARGS_92512[2].p = "Fred";
printf("%d %f %s", INTERNAL_ARGS_92512);

从运行它的机器的角度来看,printf函数会 总是接受两个参数:a&#34; const限制char *&#34;和一个&#34;联盟 long_or_double_or_ptr []&#34 ;. stdarg.h中定义的内在函数会知道 如何从该类型获取数据,从而允许程序使用它 没有特别的困难。

在这样的平台上,因此可变方法的调用约定 看起来不像接受多个方法的调用约定 离散参数,并且不应该与这些参数兼容。

请注意,您的方法编写为:

void f(anchor, a, f, d, ptr, c)
  int anchor;
  int a;
  float f;
  double d;
  void* ptr;
  char c;
  {  /* Note parameters go before the brace! */
     /* code goes here */
  }

并没有用原型声明,可能更有可能得到处理 与变量参数场景相同(因为旧C允许调用者 如果被调用的函数从不读取它们,则省略参数 没有原型编译器就无法知道有多少 函数需要的参数)但该语法已被弃用 几十年了,我不知道有多少编译器会接受它。