I was playing with C; check this out:
#include <stdio.h>
#include <stdlib.h>
void main() {
printf("%d\n", 1.5);
printf("%f", 0);
}
I expected the output:
0
0.000000
but it prints:
0
1.500000
Did the first printf()
pass the 1.5
to second printf()
?
PS: I'm aware of ( %d
for ints, %f
for floats ). As I mentioned, I was just messing with the code.
PS2: I'm using DevC++ & Code::Blocks.
The behavior is undefined as per the C Standard, here is what may be happening on your system:
printf("%d\n", 1.5);
main
passes the floating point value 1.5
as a double in the first XMM register and call printf()
.printf()
does not modify the XMM register because if does not perform any floating point operation to handle the format "%d"
. It retrieves the value to print from a different place: a register or the stack, and this value happens to be 0
.printf("%f", 0);
, main
does not change the XMM either because it passes an int
value of 0
via the other place, a register or the stack.printf()
finally gets the double
value for the format %f
from the XMM register where 1.5
has been stored earlier. Hence the output 1.500000
.None of the above is guaranteed in any way, but this is probably the explanation you are interested in. Different systems may handle parameter passing in different ways, they are part of the ABI (Application Binary Interface).
Just for fun, you might want to try this variation:
printf("first %d, second %f\n", 1.5, 42);
which on my system outputs first 42, second 1.500000
You've got some undefined behavior (so arbitrarily bad things could happen and you should not expect anything good). With %f
the printf function expects a double
(notice that when passed as argument a float
gets promoted to a double
) but 0
is a literal of type int
. Also, different compilers (even different versions of the same compiler) or different optimization flags could produce different bad effects.
Please read Lattner's blog on What Every C programmer should know about undefined behavior.
(a good attitude towards undefined behavior is to try hard to always avoid it; don't lose your time in trying to understand what concretely happens; but consider UB as something very dirty or "sick" that you always avoid)
To explain the observed behavior, you need to dive into the specifics of your particular implementation, notably the ABI and the calling conventions (for variadic functions à la printf
). Also, look into the generated assembler code (with GCC, compile with gcc -fverbose-asm -S -O1
); it might be possible that a double argument is passed in a register (or some call stack slot) different than an int
argument (so the printf
function is getting the garbage happening to sit in that location or register); notice also that quite often sizeof(int)
could be 4 but sizeof(double)
could be 8 (so the amount of data is not even right).
To avoid such mistakes, take the habit of compiling with a good compiler (such as GCC or Clang/LLVM in the free software realm) and enable all warnings and debug info (e.g. compile using gcc -Wall -Wextra -g
with GCC). The compiler would have warned you.
BTW, void main()
is illegal. It should be at least int main(void)
and preferably int main(int argc, char**argv)
and you should take care of the arguments.
With your example, gcc -Wall -Wextra
(using GCC 7) tells (for your source file april.c
):
april.c:4:10: warning: return type of ‘main’ is not ‘int’ [-Wmain]
void main() {
^~~~
april.c: In function ‘main’:
april.c:5:14: warning: format ‘%d’ expects argument of type ‘int’,
but argument 2 has type ‘double’ [-Wformat=]
printf("%d\n", 1.5);
~^
%f
april.c:6:14: warning: format ‘%f’ expects argument of type ‘double’,
but argument 2 has type ‘int’ [-Wformat=]
printf("%f", 0);
~^
%d
NB: Dev-C++ and CodeBlocks are not compilers, but IDEs. They both run some external compiler (perhaps GCC as MinGW on your system).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With