Update: having done some more research on this issue, it seems that the differences between the memory representations of float and int not responsible for the behavior of the three programs.
I looked at the object code for the third program, and I found the reason for the odd behavior: floating-point arguments are sent to a different registry / stack than integers. And printf relies on this and looks for them in other places than those that the calling printf (i.e. the main method) places arguments.
Here is the corresponding disassembly of the third program (for x86_64 architecture):
0000000100000f18 leaq 0x71(%rip), %rdi ## literal pool for: "b = %f\n" 0000000100000f1f movsd 0x61(%rip), %xmm0 ## b variable gets sent to xmm0 0000000100000f27 movl $0x0, -0x4(%rbp) 0000000100000f2e movb $0x61, -0x5(%rbp) ## a variable gets placed on the callee stack 0000000100000f32 movsd %xmm0, -0x10(%rbp) 0000000100000f37 movsd -0x10(%rbp), %xmm0 0000000100000f3c movb $0x1, %al 0000000100000f3e callq 0x100000f66 ## symbol stub for: _printf 0000000100000f43 leaq 0x4e(%rip), %rdi ## literal pool for: "a = %f\n" 0000000100000f4a movsbl -0x5(%rbp), %esi 0000000100000f4e movl %eax, -0x14(%rbp) 0000000100000f51 movb $0x0, %al 0000000100000f53 callq 0x100000f66 ## symbol stub for: _printf
And printf relies on this, it assumes that the called user placed the %f arguments in the xmm0 / xmm1 / etc registers, and the behavior of the three programs is as follows:
- in the first program,
printf looks for the argument %f in the register xmm0, however, when we are at the beginning of the program, the register is clean and main placed a in eax , so xmm0 has a zero value, and this is what printf prints - in the second program,
main correctly places b in xmm0 , and printf takes it from there, printing the correct value - in the third program, because
b is printed first, the xmm0 register will hold this value, and since printf will not spoil the register when it calls a second time, when it selects again from xmm0 , which remained intact after the first printf call.
So, all about the caller / caller agreements, where integers and floats are sent by the caller, and where the caller is trying to pick them up from.
Original answer:. In the first program, you try to print a float, but you pass an int (char is a smaller int). Because ints and floats have different binary representations, int 97 (corresponding to the character โaโ) corresponds to a very small float: 1.36E-43, which prints as zero.
Here is the binary representation of 97 (the compiler extends a 1-byte char to a 4-byte argument when calling a function)
00000000 00000000 00000000 01100001
IEEE 754 is the standard format for binary representations of floating point / double numbers, you can play with the online converter here , and you can see how the same binary number has different meanings when it is interpreted as int or as float.