I just happened to have similar situation like in this question from two years:
Variadic function (va_arg) doesn't work with float?
There is said that the problem is promoting float to double when we call things like
va_arg(arg, float)
My question is at the end of this post, but first let's take a look at @Jack's answer below the question linked above:
#include <stdio.h>
#include <stdarg.h>
void foo(int n, ...)
{
va_list vl;
va_start(vl, n);
int c;
double val;
for(c = 0; c < n; c++) {
val = va_arg(vl, double);
printf("%f\n", val);
}
va_end(vl);
}
int main(void)
{
foo(2, 3.3f, 4.4f);
return 0;
}
Output:
3.300000
4.400000
Now, if we change val = va_arg(vl, double)
into val = va_arg(vl, float)
, we will get (at least I get in MSVS 2012):
36893488147419103000.000000
2.162500
Let's go into my question now.
In this topic: C/C++ va_list not returning arguments properly
most voted answer and it's comment says that printf
also promotes float
's to double
's.
But what is difference? If both of them promotes float
into double
, why printf
writes values correctly, while va_arg
gives us such a nasal demons?
It's not printf
that promotes the float argument to double
, it is compiler that does it. In other words, by the time your va_arg
or printf
or any other function with variable number of arguments gets the control, all the float
s are already promoted to double
s; the original float
s are not available for retrieval.
The difference between printf
and va_arg
is that printf
follows the rules set up by the standard, and request the parameter of a promoted type (i.e. double
) when it sees the corresponding format specifier in the format string. Hence it successfully gets a double
with the promoted value of the float
in it, and produces the desired output.
On the other hand, when va_arg
calls val = va_arg(vl, float)
it ignores the promotion rule, and gets an invalid representation in return.
printf
does not take arguments of type float
.
The "%f"
format specifier, for example, requires an argument of type double
. "%Lf"
requires an argument of type long double
. There is no format that requires an argument of type float
(which would be promoted to double
anyway, not by printf
itself but simply because of the semantics of calls to variadic functions).
So assuming printf
is implemented in C, and that it uses the <stdarg.h>
mechanisms to read its arguments, there is no invocation of va_arg()
for type float
in the implementation of printf
.
Any variadic function that does attempt to invoke va_arg()
for type float
will have undefined behavior, since there can be no float
arguments to such a function. printf
works because it doesn't do that.
Arguments to variable argument functions gets special promotion rules.
The one relevant here is that passing a float as a variable argument gets promoted to a double. Meaning you cannot extract the argument as a float, as it has been passed as a double. This is done by the compiler, it has nothing to do with printf
.
This means the code val = va_arg(vl, float)
is not valid, as the argument is not a float, it's a double.
If you really need to deal with the passed in values as a float, at best you can do
float val = (float) va_arg(vl, double)
Note that the %f
specifier for printf expects an argument of type double
, not a float
But what is difference? If both of them promotes
float
intodouble
, whyprintf
writes values correctly, whileva_arg
gives us such a nasal demons?
There's no difference, except the fact (stated in the question itself), that printf
is coded in a way to treat float
as double
. In other words, somewhere inside printf
, when the format string contains information that there should be a floating point number, the function does va_arg(vl, double)
, just like you did.
来源:https://stackoverflow.com/questions/23836118/variadic-function-va-arg-doesnt-work-with-float-while-printf-does-what-the