1

Someone has asked me, why this code produces a random number:

double a = 75.0;
printf("%d\n", a);

I thought the reason was that 4 bytes of the double are interpreted as an integer, but the printed value was different each time the program was run. So i started to try some more things and found that this:

printf("%d\n", 75.0, 6);

actually prints out number 6. So i thought that the compiler is trying to fix the arguments so that they match the format string, but then i tried this:

const char *formats[] = { "%d %.1f\n", "%.1f %d\n" };
int whichFormat = 0;
scanf("%d", &whichFormat);
printf(formats[whichFormat&1], 2.5, 7, 1.2);

The formatting string now isn't even known at compile time, but it still somehow manages to match the argument types to the formatting string, printing either 7 2.5 or 2.5 7 depending on the input. The last value (1.2) was not printed.

All of this can be reproduced at compileonline.com which claims to be using GNU GCC 4.8.1.

What is going on here?

Detheroc
  • 1,833
  • 15
  • 19
  • Check out [Passing too many arguments to printf](http://stackoverflow.com/questions/3578970/) – HostileFork says dont trust SE Oct 22 '14 at 14:54
  • 4
    The ABI for your platform probably specifies that integer and floating-point arguments are passed in different sets of registers, which causes it to appear to be able to match the correct type. – interjay Oct 22 '14 at 14:58
  • It's *extremely* unlikely that the compiler is trying to "fix" the arguments to match the conversion specifier; it's much more likely that this result is simply an artifact of how function arguments are passed on this particular architecture (via register as opposed to the stack). – John Bode Oct 22 '14 at 15:14

1 Answers1

5

Undefined behavior is going on.

Such behavior is undefined, so it's very hard to reason about, and also somewhat pointless to do experiments, since there's no guarantee that the behavior stays the same (i.e. well-defined) for the same input. The behavior is, after all, undefined.

The first example, for instance, might read an expected integer argument from one register, while the actual floating-point argument is in another. I'm not saying this is what happens on any known machine, but it could be like that.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • Inspection of GCC's assembler output on x86-64 suggests you're right. The `double` argument goes in an XMM register. – Fred Foo Oct 22 '14 at 15:00
  • 2
    It's far better to just correct the format specification and move on, although understanding where the rogue results are coming from can be educative. – Weather Vane Oct 22 '14 at 15:31
  • On a X86_64, there is definitively something like that going on. If they are integers or pointers the first four function arguments are passed in registers. Here, the second argument would just be what `printf` finds in the `%esi` register, I think, completely unrelated to wherever `double` function arguments are stored. – Jens Gustedt Oct 22 '14 at 19:57