Why can one printf() in C not print two 64-bit values at the same time?

后端 未结 5 1384
走了就别回头了
走了就别回头了 2020-12-29 00:14

I am working on a 32-bit system. When I try to print more than one 64 bit value in a single printf, then it cannot print any further (i.e. 2nd, 3rd, ...) variable values.

相关标签:
5条回答
  • 2020-12-29 00:28

    You need to use the correct format:

    #include <stdio.h>
    #include <stdlib.h>
    #include <inttypes.h>
    
    int main(void)
    {
        uint64_t a = 0x12345678;
        uint64_t b = 0x87654321;
        uint64_t c = 0x11111111;
    
        printf("a is %#" PRIx64
                " & b is %#" PRIx64
                " & c is %#" PRIx64 "\n",
                a, b, c);
        return EXIT_SUCCESS;
    }
    

    Output:

    a is 0x12345678 & b is 0x87654321 & c is 0x11111111
    
    0 讨论(0)
  • 2020-12-29 00:34

    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.

    0 讨论(0)
  • 2020-12-29 00:38

    Use:

    "%lli" for int64_t
    "%llu" for uint64_t
    "%llx" for hex
    "%llX" for HEX
    

    Have a look inside "inttypes.h".

    0 讨论(0)
  • 2020-12-29 00:42

    It prints them all on my computer, but there are three compile time warnings since %llx expects a long long unsigned int.

    Are you sure you need to be using 64 bit types though? All three of your hexcodes are only 32 bits. Maybe you could just use 32 bits and do:

    unsigned int a = 0x12345678;
    unsigned int b = 0x87654321;
    unsigned int c = 0x11111111;
    
    printf("a is %x & b is %x & c is %x",a,b,c);
    

    (Or use the stdint equivalent of 32bit unsigned int)

    Unless you need them to be 64 bits so you can add more bits to them later.

    0 讨论(0)
  • 2020-12-29 00:43

    You should use the macros defined in <inttypes.h>

    printf("a is %"PRIx64" & b is %"PRIx64" & c is %"PRIx64"\n",a,b,c);
    

    It is ugly as hell but it's portable. This was introduced in C99, so you need a C99 compliant compiler.

    0 讨论(0)
提交回复
热议问题