c programming printf format searches expressions

前端 未结 3 833
执念已碎
执念已碎 2021-01-24 22:40

I have a small question. I have this piece of code:

#include 

int main(){
    printf(\"%d, %f, %d\\n\", 0.9, 10, \'C\');
}

And

相关标签:
3条回答
  • 2021-01-24 22:43

    But I would have expected: …

    When the format string does not match the types of the arguments in order, the behavior is undefined. Anything can happen. You cannot expect anything (and I do not see why you would expect 0. Maybe you expect printf to use the format string to convert the arguments between floating-point and integer. It just doesn't. It's a variadic function like you could write your own, and the fact that the format string encodes the types of the trailing arguments is not used to convert them).

    Modern calling conventions use registers for the first few arguments, and can use registers even for variadic functions. On x86-64, the convention can be for instance that the first floating-point argument to a variadic function can always be expected in the floating-point register xmm0, whereas integer arguments are passed in general-purpose registers %rdi, %rsi, %rdx, … This has the effect that printf("%f %d", 1, 1.0) prints the floating-point argument followed by the integer argument.

    As an illustration, here is a short program:

    #include <stdio.h>
    
    int main(){
      printf("%d, %f, %d\n", 0.9, 10, 'C');
    
      printf("%d, %f, %d\n", 10, 0.9, 'C');
    }
    

    This is how my compiler (Clang on Mac OS X 10.6) compiles the program:

    leaq <memory location of format string>, %rbx
    
    movq    %rbx, %rdi
    movsd   <memory location of 0.9 constant>, %xmm0
    movl    $10, %esi
    movl    $67, %edx
    movb    $1, %al
    callq   _printf
    
    movq    %rbx, %rdi
    movl    $10, %esi
    movsd   <memory location of 0.9 constant>, %xmm0
    movl    $67, %edx
    movb    $1, %al
    callq   _printf
    
    …
    

    It is clear that the two calls produce the same result. But in the case of one, it is accidental and only “works” for this particular compiler version and ABI, whereas the other one respects the standard and has to work anywhere.

    Again, printf("%d %f", 0.9, 10) is undefined behavior and you should not use it in any circumstances.

    0 讨论(0)
  • 2021-01-24 22:45

    The only correct expectation is getting 67 for printing a character using %d format specifier*. The other two printouts are undefined behavior.

    it looks like the printf searches for the corresponding type in the expressions

    This is only a coincidence. printf has no idea of the types of the actual parameters that you pass. It trusts the format string, and interprets the data sequentially. You can tell what's going on by supplying different numbers, and observing how the output changes.

    The numbers that you see is garbage - a double re-interpreted as an int, and an int re-interpreted as a double. Moreover, if the sizes of double and int are different, the first two parameters cross each others' boundaries.

    To produce the output that you want add a cast to the first two parameter expressions:

    printf("%d, %f, %d\n", (int)0.9, (double)10, 'C');
    

    Note that you do not need to cast the last parameter, because char is promoted to an int as part of processing variable length argument list of printf.

    * This produces correct behavior only when there are no mismatches between other parameters and format specifiers; your program has UB even for the last parameter which would be correct if used by itself.

    0 讨论(0)
  • 2021-01-24 23:03

    It is undefined behaviour, so anything can happen.

    %d requires an int, but you are passing a double (not a float), so printf takes 4 bytes of the double value and interprets it as an int.

    %f requires a double, but you are passing an int. So it takes the 4 byte of the int and 4 bytes from the next memory and interprets it as a double.

    You are lucky to pass 16 bytes while printf expects 16 bytes, so the last value is the correct one.

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