2

Consider the following code snippet

    float a=12.2;
    printf("%f %d",a,a); //output 12.200000 Garbage value

but

    printf("%d %f",a,a);//Output Garbage value Garbage Value

my question is why in the second printf, both %d and %f gives garbage value. I know it is because I have used %d first?..But can't find any proper explanation..

Vaibhav Jain
  • 3,729
  • 3
  • 25
  • 42

3 Answers3

4

Using a wrong conversion specifier for a data type invokes undefined behavior. You may get anything, either expected or unexpected result.

C11: 7.21.6 Formatted input/output functions:

If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Using %d to print the value of a (float) results in undefined behavior of the program.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
2

When you use the wrong specifier in printf, things such the following can happen.

The caller does this:

  • Suppose, for illustration, the stack pointer is currently at 100, and arguments are pushed onto the stack in reverse order.
  • For functions with variable argument types, float arguments are converted to double.
  • For printf("%d %f"), a, a), the caller first puts the last a on the stack. The caller makes eight bytes of space by changing the stack pointer to 92, converts a to double, and writes eight bytes to the stack.
  • For the first a, the caller changes the stack pointer to 84 and writes eight bytes to the stack.
  • For the format string, "%d %f", the caller changes the stack pointer to 80 and writes a four-byte pointer to the stack.

Then printf does this:

  • printf initializes a pointer to where the arguments start, 80.
  • It reads the pointer to the string at 80.
  • Since a pointer is four bytes, printf updates its argument pointer to point to 84.
  • The string says the next argument is %d, so printf expects a four-byte integer, and it reads four bytes from 84.
  • Since an int is four bytes, printf updates its argument pointer to point to 88.
  • Since the eight bytes at 84 to 91 contain the encoding of a double, the four bytes from 84 to 87 do not make any sense as an int, and printf prints whatever int value the bits happen to represent.
  • The string says the next argument is %f, so printf expects an eight-byte double, and it reads eight bytes from 88.
  • The bytes from 88 to 95 contain four bytes from the end of the first double and four bytes fro the start of the second double, so they do not make any sense as a double, and printf prints whatever value the bits happen to represent.

This is not the way all C implementations work, but it is one thing that may happen when you use incorrect arguments with printf. The C standard makes the behavior undefined because it does not control how implementations work, so they might behave as described as above, but they might instead be passing some of the arguments in registers. In the latter case, you would see different behaviors, such as printf printing garbage for %d because nothing had set the corresponding integer register to an appropriate value but printf printing the correct value for %f because it looked for it in a floating-point register, and the caller did place the value of a in a floating-point register.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
1

Because you can't know what will happen. If sizeof(float) != sizeof(int), you will definitely get a garbage value twice in the second case, since printf read either too many bytes or too few bytes from the first argument, so it will screw up when reading the next argument (assuming arguments were passed on stack).

Another issue that arises is that the argument passed is being converted to double, which usually has a different size from int, and then printf tries to pick an int from something that is double. There is a size mismatch (see comments below).

Either way, this invokes undefined behaviour, so you really can't "expect" anything meaningful to happen.

Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • 1
    Mind you, though, that on both x86 and x86_64, `sizeof(float) == sizeof(int)`, since both are 32-bit. What is more likely to happen is that the `float` argument is being promoted to `double` for argument passing, which causes the size mismatch. Also, on x86_64, the code actually "works" for me (in the sense of the `%f` in the second case printing the correct value rather than garbage). – Dolda2000 Jan 24 '14 at 10:10
  • @Filipe But in my computer sizeof(int)==sizeof(float) –  Jan 24 '14 at 10:16
  • @dividedbyzero360 See updated answer and Dolda's comment – Filipe Gonçalves Jan 24 '14 at 10:17
  • Conversion to `double` is not only "likely", it is imposed for `va_arg` functions such as `printf`. – Jens Gustedt Jan 24 '14 at 10:23
  • @Dolda2000 even if the first float in converted into double and is tried to be displayed using %d why is the second %f giving garbage value? –  Jan 24 '14 at 10:27
  • @dividedbyzero360 Because you lied to the `printf`. Function calculates location of second argument incorrectly. – user694733 Jan 24 '14 at 10:31
  • @dividedbyzero360: Because, after having consumed four bytes for the `int` printed in the first `%d` field, the pointer into the stack that `printf` uses into to fetch arguments now points 4 bytes into the 8-byte `double` on the stack, so the next `%f` fields causes it to read an 8-byte `double` from the second half of the first `double` and the four bytes of the `int` argument on the stack. – Dolda2000 Jan 24 '14 at 10:31
  • @dividedbyzero360 `double` is likely to be 64-bit, and `int` 32-bit. If this is the case, `printf` will see `%d` and pop a 32-bit value (and print it assuming it's an int) from the arguments list, even though the first argument (the promoted `float`) is 64-bit. Then, when it reads `%f`, it will pick 64-bit, but it will start picking it in the middle of `double` because that's where it stopped before. – Filipe Gonçalves Jan 24 '14 at 10:31
  • @FilipeGoncalves --- Your point is clear to me. But why in the first place is float prompted to double.?..Even float is 4 bit then the same –  Jan 24 '14 at 10:53
  • @Dolda2000 ...I got your point sir.But why in the first place float is prompted to double? –  Jan 24 '14 at 11:10
  • @dividedbyzero360 Because it's a function with variable arguments, meaning that arguments are subject to default promotions: `char` and `short` are promoted to `int`, `float` is promoted to `double`. This is mandatory for functions with optional arguments, as well as functions without a prototype (should be rare these days). For further details see http://stackoverflow.com/questions/1255775/default-argument-promotions-in-c-function-calls – Filipe Gonçalves Jan 24 '14 at 12:17