To the best of my knowledge, in C auto type conversion or type promotion takes place when I try to store small data type in larger data type. For example, int is promoted to float or double. But in the code below, I am getting garbage value:
#include <stdio.h>
#include<stdarg.h>
double average(int num,...)
{
va_list a_list;
double sum=0.0;
va_start(a_list,num);
for(int i=0;i<num;i )
sum =va_arg(a_list,double);
va_end(a_list);
return sum/num;
}
int main()
{
printf("%.2lf\n",average(5,4.0,7,8,9,10));
printf("%.2lf\n",(4.0 7 8 9 10)/5);
return 0;
}
The first printf function does not give correct output, whereas the second one gives.
CodePudding user response:
va_list
has non-existent type safety so if you mix several different types in the variable argument list, you need to keep track of their types somehow. That's what printf
does with the format string.
The argument list of a variadic function is promoted according to an oddball rule called the default argument promotions (C17 6.5.2.2/6):
If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type
float
are promoted todouble
. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis(, ...)
or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined.
Meaning that small integer types get promoted to int
, but constants like 5
that are already int
do not get promoted, particularly not to a floating point type. So your code has undefined behavior as per the above quote, since the function treats int
arguments as double
.
Variadic functions are not recommended to be used for any purpose, they are almost certainly the wrong solution to any problem you are having. Use arrays and/or structs instead.
CodePudding user response:
When arguments are passed for the ...
portion of a function’s parameters, only the default argument promotions are performed. These include the integer promotions and converting float
to double
. They do not convert integer types to floating-point types.
The conversions that occur in passing arguments to functions with declared parameter types rely on knowing the types in advance, when the program is compiled, so that the arguments can be passed in a specified way. Each computing platform has some rules for passing arguments, called the application binary interface [ABI]. A common characteristic of ABIs is that integer and pointer arguments are passed in general registers of the processor (or on a stack), while floating-point arguments are passed in floating-point registers.
When there is a variable argument list and va_arg
is used, the called function is requesting arguments during program execution. When the called function requests an integer value with va_arg
, va_arg
attempts to get it from where an integer argument would be passed. When the called function requests a floating-point value, va_arg
attempts to get it from where a floating-point argument would be passed. The called function has no way of knowing whether you passed an integer argument or a floating-point argument, so it has no way of directing va_arg
to fetch the value from where you passed the argument rather than from where it expects the argument to be. You must pass the correct type of arguments.