I had this question while reading C Standart, but it basically just references C Standart, so i guess this question applies to both languages.
From [cstdarg.syn]
If the parameter parmN is of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
I dont understand the rule about "compatible" types. Compatible types are pretty much the same types in C, so what does this rule means exactly? parmN
cant be float
, bool
, char
, short
?
int add_nums(short count, ...)
{
int result = 0;
std::va_list args;
va_start(args, count); // undefined behavior ?
for (short i = 0; i < count; i) {
result = va_arg(args, int);
}
va_end(args);
return result;
}
Also, what is the reasoning behind this rule? I get how parmN
cant be reference, but I dont see how its type is related to types of variadic arguments.
CodePudding user response:
The rule regarding types has to do with promotion of arguments.
For variadic arguments, passed arguments of type float
are promoted to double
and integer arguments with a type smaller than int
are promoted to int
or unsigned int
. This means that va_arg
cannot expect arguments of these types, otherwise you trigger undefined behavior.
This behavior with regard to variadic functions is documented in section 6.5.2.2p7 of the C11 standard:
If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.
And the term default argument promotions is defined in 6.5.2.2p6:
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.
CodePudding user response:
Have a look at:
In the C reference there is a good example for what is meant by the (confusing) statement:
int add_nums(int count, ...);
int sum = add_nums(2, 'c', true); // add_nums is called with three ints: (2, 99, 1)
And the C reference makes it very clear, that:
When a variadic function is called, after lvalue-to-rvalue, array-to-pointer, and function-to-pointer conversions, each argument that is a part of the variable argument list undergoes additional conversions known as default argument promotions:
...
Only arithmetic, enumeration, pointer, pointer to member, and class type arguments (after conversion) are allowed. However, ... (some other stuff)