Reputation: 15
Hello everyone,
int main()
{
int a = 2;
double b=2;
printf( "with the d a = %d \n", a); // print 2
printf( "with the d a = %d \n", b); // **print 0 <== My interrogation?**
printf( "with the f a = %f", b); // print 2.000000
return 0;
}
I know that since we declare b as a double we have to use the %f in the printf function. My question is: deeply in the memory, why we print a 0 since we put in b an int (even if the program has already reserve a memory for a double) ??
Thank you
Upvotes: 0
Views: 2797
Reputation: 8475
This is undefined behavior (UB) since there is a mismatch between the format string and the argument. Having said that, and assuming that the compiler does not make some non-trivial transformations, then the UB has some typical behavior in this case.
To see what happens you have to check the calling convention on the system. Most likely the program run on a 64 bit x86 architecture, which passes arguments according to X86 calling conventions. On windows, the first floating point value is passed by a different register (XMM0) than the otherwise integer argument (one of RCX, RDX, R8, R9). This means that for:
printf( "with the d a = %d \n", b);
the double b
is passed to one place, while the "%d"
is reading from another. Also note that the relevant integers are considered volatile, and can be trashed by the earlier call to printf
. This means that the int (a=2) passed to the previous printf
, through the register, is trashed by the same printf
. In effect, the "%d" makes printf
read a junk value left over from the inner implementation of printf
.
As a result, on Linux and on mac, with clang and gcc I get random junk value instead of zero. I assume that the original question is referring Visual Studio on Windows, that might set the integer to zero in debug mode (or something similar).
Please note that there is no guarantee with undefined behavior. The compiler might do anything, including removing the offending line, or doing something completely unexpected. The analysis of this answer is correct for a given compiler, system, and optimization level. It was observed in the generated assembly, but any minor change (even in other function in the code) may cause a completely different behavior. Don't ever rely on undefined behavior.
Upvotes: 2
Reputation: 36399
The binary represenation of 2
as a IEEE 754 double is 0x4000000000000000
, passing the double
to printf that is expecting an int
presumably causes a cast to an integer and only looks at the bottom 4 bytes which are all 0
so 0
is printed.
All of this is undefined behaviour so shouldn't be relied upon and you need to make sure you use the correct format strings. Some modern compilers will even warn if your format strings don't match your arguments.
Upvotes: 4
Reputation: 5613
You have undefined behaviour.
You are asking C to look at the piece of memory occupied by your double and interpret it as if it was an int.
What is probably happening is that the double is being loaded onto the floating point registers, while printf is looking on the stack for the int value.
Any decent compiler these days should give you warnings when your format string does not match your parameter types. Always read those warnings!
Upvotes: 1