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)数値を安全に 2 倍に昇格させるべきではありませんか?

  • calculateAverage(3,1,2,3)2 つの異なる場所で 2 つの結果を得るにはどうすればよいですか? 私は「未定義の動作」領域の中にいますか? はいの場合、どのように?

gcc バージョン 4.8.1 を使用しています。

4

3 に答える 3

2

int可変引数関数の引数昇格は、通常の規則に従います。 ( charshortなど)より小さい型は に昇格されintます。floatは に昇格しますがdoubleintに昇格するつもりはない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ます。にキャストするintdouble、正常に動作するはずです。

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.0a とはまったく異なって見えます11そして、 をとして取ると、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(両方ともリトルエンディアンマシン上)。

また、double などのスタック コンテンツを解釈すると、00 00 00 01 00 00 00 02奇妙な結果が生じます。

于 2013-10-26T10:08:18.517 に答える