4
#include<stdio.h>
main()
{
    float x=2;
    float y=4;
    printf("\n%d\n%f",x/y,x/y);
    printf("\n%f\n%d",x/y,x/y);
}

Output:

0 
0.000000
0.500000 
0

compiled with gcc 4.4.3 The program exited with error code 12

mskfisher
  • 3,291
  • 4
  • 35
  • 48
blacktooth
  • 176
  • 3
  • 16
  • 2
    I have an urge to mod-down the question - only for the use of "\n" at the beginning of the string. Otherwise, this is why it is always good idea to keep compiler warnings activated. – Dummy00001 Aug 01 '10 at 19:48
  • @Dummy: agree about the newline, but I haven't been able to get `gcc` to complain about anything accept the lack of properly specified return type on `main`. – dmckee --- ex-moderator kitten Aug 01 '10 at 19:51
  • 4
    Call me stupid, but what's the matter with newlines at the start of strings? – Brian Hooper Aug 01 '10 at 19:52
  • @Brian: The idiom in c (and thus inherited by c++) is that newlines go at the *end* unless you have pressing reason to put them t the front. Why? Because K&R did it that way. – dmckee --- ex-moderator kitten Aug 01 '10 at 19:57
  • 2
    @dmckee Nonsense, you can put a newline anywhere in a string. Hell, you can have multiple newlines interspersed in the string - perfectly good C code. –  Aug 01 '10 at 20:09
  • 1
    @Neil: You can. And then you forget to add the cleanup line like the code above and you leave me with `0dmckee@thorin$ ` for my prompt as well as sticking a wholly unnecessary extra line in the beginning of the output. Put as many newlines in the middle as you need, use leading newlines when it makes a loop cleaner to code, but *prefer ending newlines to leading ones* when they are equivalent. – dmckee --- ex-moderator kitten Aug 01 '10 at 20:14
  • @dnckee I routinely embed all sorts of control characters in strings, so we will have to disagree. Certainly K&R never said anything about this. And I can't see how leading and trailing newlines can possibly be equivalent. –  Aug 01 '10 at 20:17
  • 1
    ISO C99 says in section 7.19.2p2 that if a text file doesn't end with a newline, there is no guarantee that the last line appears in the output at all. – Roland Illig Aug 01 '10 at 21:00
  • I believe gcc normally screams warnings about passing args that don't match format specifiers to printf. Did you ignore those warnings? Did you suppress them? Don't. – Russell Borogove Aug 01 '10 at 21:06

6 Answers6

16

As noted in other answers, this is because of the mismatch between the format string and the type of the argument.

I'll guess that you're using x86 here (based on the observed results).

The arguments are passed on the stack, and x/y, although of type float, will be passed as a double to a varargs function (due to type "promotion" rules).

An int is a 32-bit value, and a double is a 64-bit value.

In both cases you are passing x/y (= 0.5) twice. The representation of this value, as a 64-bit double, is 0x3fe0000000000000. As a pair of 32-bit words, it's stored as 0x00000000 (least significant 32 bits) followed by 0x3fe00000 (most significant 32-bits). So the arguments on the stack, as seen by printf(), look like this:

0x3fe00000
0x00000000
0x3fe00000
0x00000000  <-- stack pointer

In the first of your two cases, the %d causes the first 32-bit value, 0x00000000, to be popped and printed. The %f pops the next two 32-bit values, 0x3fe00000 (least significant 32 bits of 64 bit double), followed by 0x00000000 (most significant). The resulting 64-bit value of 0x000000003fe00000, interpreted as a double, is a very small number. (If you change the %f in the format string to %g you'll see that it's almost 0, but not quite).

In the second case, the %f correctly pops the first double, and the %d pops the 0x00000000 half of the second double, so it appears to work.

Matthew Slattery
  • 45,290
  • 8
  • 103
  • 119
7

When you say %d in the printf format string, you must pass an int value as the corresponding argument. Otherwise the behavior is undefined, meaning that your computer may crash or aliens might knock at your door. Similar for %f and double.

Roland Illig
  • 40,703
  • 10
  • 88
  • 121
  • 1
    Life learned me to always cast all printf arguments like this: const char*p = "stop"; int i=7; printf("Hello %s %d", (const char*)p, (int)i); – danatel Aug 01 '10 at 19:50
  • That's dangerous, too. I prefer to leave away the casts and let the compiler check that the types match. GCC does an excellent job at this. In C, you can never be sure that the `(const char *)` doesn't accidentally convert an `int` to a pointer. Syntactically it would be possible. – Roland Illig Aug 01 '10 at 20:56
4

Yes. Arguments are read from the vararg list to printf in the same order that format specifiers are read.

Both printf statements are invalid because you're using a format specifier expecting a int, but you're only giving it a floatdouble.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
  • But why does its behavior change in both statements? – blacktooth Aug 01 '10 at 19:41
  • 4
    @blacktooth: No idea. Compilers do not need to do sensible things when you invoke undefined behavior. – Billy ONeal Aug 01 '10 at 19:46
  • @Billy I told the guy who asked me this question same way. :D He told me that its an interview question! – blacktooth Aug 01 '10 at 19:49
  • @blactooth Many interviews I've ever gone to, I've re-written their "tests" for them so they were correct, and then told them "no thanks" - a company that asks a bad interview question is a bad one to work for. –  Aug 01 '10 at 20:11
  • @Billy, to nitpick, you are actually passing a double to the printf function due to default promotion rules. – ergosys Aug 01 '10 at 20:16
3

What you are doing is undefiend behaviour. What you are seeing is coincidental; printf could write anything.

You must match the exact type when giving printf arguments. You can e.g. cast:

printf("\n%d\n%f", (int)(x/y), x/y);
printf("\n%f\n%d", x/y, (int)(x/y));
strager
  • 88,763
  • 26
  • 134
  • 176
2

This result is not surprising, in the first %d you passed a double where an integer was expected.

ergosys
  • 47,835
  • 5
  • 49
  • 70
  • @Strager: They're all correct answers; I upvoted all of them. Just couldn't think of an additional reason for the others. – Billy ONeal Aug 01 '10 at 19:38
0

http://en.wikipedia.org/wiki/Format_string_attack

Something related to my question. Supports the answer of Matthew.

blacktooth
  • 176
  • 3
  • 16