As appropriate for a forum named Stack Overflow, the cause of the unexpected printf()
output is due to stack misalignment. The size mismatch between the %x
conversion specification and function argument a
causes the misalignment.
When compiling the statement
printf("a is %x & b is %llx & c is %llx",a,b,c);
the compiler generates machine code that pushes the function arguments on the stack in right-to-left order.
The compiler uses the variable declarations, not the format string, to determine the data size of each argument on the stack (except possibly for generating warnings). In an x86 CPU (as in most machines) the stack pointer decrements with every push. When entering the printf()
library function, the stack therefore has the following layout:
00A4: ...
00A0: 00000000
009C: 11111111 Variable 'c' pushed on the stack as uint64
0098: 00000000
0094: 87654321 'b' pushed on the stack as uint64
0090: 00000000
008C: 12345678 'a' pushed on the stack as uint64
0088: <pointer to format string>
0084: <return address>
The top-of-stack address of 0084 is arbitrary for this example.
Since all three variables are declared as uint64_t
, the compiled code pushes these variables on the stack as 64-bit values. For a little-endian machine such as an x86 CPU, the high bytes of each uint64
value end up in the higher addresses.
The implementation of printf()
uses the format string to determine the number of and sizes of the arguments on the stack. Unlike the compiler, printf()
receives no information about the original variable declarations. The first conversion specification is %x
, so printf()
expects a
to be a 32-bit value, and therefore, printf()
parses the stack layout as follows:
00A4: ...
00A0: 00000000
009C: 11111111
0098: 00000000 '%llx' reads 'c' as uint64, but from the wrong address
0094: 87654321
0090: 00000000 '%llx' reads 'b' as uint64, but from the wrong address
008C: 12345678 '%x' causes printf() to read 'a' as a uint32
0088: <pointer to format string>
0084: <return address>
The stack misalignment explains why a
prints 12345678 as expected, but b
and c
have been effectively left-shifted by 32 bits to 8765432100000000 and 1111111100000000.
Correcting the first %x
conversion specification or casting argument a
to uint32 should fix the problem.