4

我编写了一个涉及可变参数的小型 C 程序。见下文:-

#include <stdio.h>
#include <stdarg.h>

double calculateAverage(int num,...)
{
  va_list argumentList;
  double sum=0;
  int i;

  va_start(argumentList,num);

  for(i = 0; i < num; i++)
  {
    sum += va_arg(argumentList,double);
  }
  va_end(argumentList);
  return(sum/num);
}

int main()
{
  printf("%f\n",calculateAverage(3,1,2,3));
  printf("%f\n",calculateAverage(4,2,4,6,8));
  printf("%f\n",calculateAverage(4,2.0,4.0,6.0,8.0));
  printf("%f\n",calculateAverage(3,1,2,3));
}

输出是:

0.000000
0.000000
5.000000
5.333333

只有calculateAverage(4,2.0,4.0,6.0,8.0)给出预期的输出,即当我专门用小数点表示它们时。

  • 不应该va_arg(argumentList,double)安全地将数字提升一倍吗?

  • 如何calculateAverage(3,1,2,3)在 2 个不同的地方给出 2 个结果?我是否在一些“未定义的行为”领域内?如果是,如何?

我正在使用 gcc 版本 4.8.1。

4

3 回答 3

2

变量参数函数的参数提升遵循正常规则:小于int( char,short等) 的类型被提升为int; float被提升到double, 但int不会被提升到double, 所以这个

 va_arg(argumentList,double)

告诉编译器参数是 type double,但在某些调用中它们不是。请注意,您可以通过float,因为它们将被提升为double.

calculateAverage(4,2.0f,4.0f,6.0f,8.0f);

解决方案是确保您正在传递double参数,或者使用不同类型的parmN,例如格式字符串。(如 之一printf

于 2013-10-26T10:16:33.570 回答
2

问题在于这个声明sum += va_arg(argumentList,double);

由于您试图解释为double- 问题将发生在Integer literals. int将你的 s投射到double它应该可以正常工作。

printf("%f\n",calculateAverage(3,(double)1,(double)2,(double)3));
printf("%f\n",calculateAverage(4,(double)2,(double)4,(double)6,(double)8));
printf("%f\n",calculateAverage(4,2.0,4.0,6.0,8.0));
printf("%f\n",calculateAverage(3,(double)1,(double)2,(double)3));

va_arg 无法确定传递给函数的参数的实际类型,而是使用作为类型宏参数传递的任何类型作为其类型。

于 2013-10-26T10:07:44.000 回答
2

不,不应该。使用va_arg(),您可以告诉数据应该采用什么格式。关于内部表示,1.0看起来与 a 完全不同1。如果你把 a1作为 a double,你会得到一些完全错误的东西。

以下是运行函数调用时堆栈上发生的情况:

double calculateAverage(int num,...)
{
  va_list argumentList;
  double sum=0;
  int i;

  va_start(argumentList,num);

  printf("%p", &num);
  unsigned char * c =  &num;
  for(i = 0; i < num * sizeof(double) + 4; i++, c++)
  {
    printf(" %02x", *c);
  }
  printf("\n");
   for(i = 0; i < num; i++)
  {
    sum += va_arg(argumentList,double);
  }
  va_end(argumentList);
  return(sum/num);
}

然后,

printf("%f\n",calculateAverage(3,1,2,3));

0xbfc507d0 03 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 ...
-0.054776

然而

printf("%f\n",calculateAverage(3,1.0,2.0,3.0));

0xbfd15290 03 00 00 00 00 00 00 00 00 00 f0 3f 00 00 00 00 00 00 00 40 00 00 00 00 00 00 08 40
2.000000

因为整数1内部看起来像00 00 00 01,而1.0内部看起来像00 00 00 00 00 00 f0 3f(都在小端机器上)。

并且解释诸如00 00 00 01 00 00 00 02double 之类的堆栈内容会导致奇怪的结果。

于 2013-10-26T10:08:18.517 回答