4

I have written a small C program which involves variable arguments. See below:-

#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));
}

Output is:

0.000000
0.000000
5.000000
5.333333

Only calculateAverage(4,2.0,4.0,6.0,8.0) is giving expected output, i.e. when I specifically represent them with the decimal point.

  • Shouldn't va_arg(argumentList,double) safely promote the numbers to double?

  • How can calculateAverage(3,1,2,3) give 2 results in 2 different places? Am I inside some 'undefined behavior' territory? If yes, how?

I am using gcc version 4.8.1.

3 Answers 3

2

No, it shouldn't. With va_arg(), you tell what format the data should be assumed to have. Concerning the internal representation, 1.0 looks completely different than a 1. And if you take a 1 as a double, you get something completely wrong.

Here is what happens on the stack when running your function calls:

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);
}

Then,

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

gives

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

whereas

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

gives

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

because an integer 1 internally looks like 00 00 00 01 whereas 1.0 internally looks like 00 00 00 00 00 00 f0 3f (both on a little endian machine).

And interpreting a stack content such as 00 00 00 01 00 00 00 02 as a double will lead to strange results.

Sign up to request clarification or add additional context in comments.

Comments

2

Issue is with this statement sum += va_arg(argumentList,double);

Since you are trying to interpret as double - the problem will happen with Integer literals. Cast your ints to double and it should work fine.

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 cannot determine the actual type of the argument passed to the function, but uses whatever type is passed as the type macro argument as its type.

2 Comments

Alternatively, you could just add decimal places to the numbers to improve readability, rather than having a number of type casts. e.g. 3 -> 3.0
@PatTeen - that would be short and sweet yes.
2

The argument promotion for variable argument function follows the normal rule: types smaller than int(char, short, etc) are promoted to int; float are promoted to double, but int isn't going to be promoted to double, so this

 va_arg(argumentList,double)

tells the compiler the argument is of type double, but in some calls they are not. Note that you can pass float though, because they will be promoted to double.

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

The solution is either make sure you are passing double parameters, or use a different kind of parmN, for example, a format string. (Like the one of printf)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.