tags:

views:

464

answers:

4

Take the following :

#include <stdio.h>

main() {
    unsigned long long verybig = 285212672;

    printf("Without variable : %llu\n", 285212672);
    printf("With variable    : %llu", verybig);
}

This is the output of the above program :

Without variable : 18035667472744448
With variable    : 285212672


As you can see from the above, when printf is passed the number as a constant, it prints some huge incorrect number, but when the value is first stored in a variable, printf prints the correct number.

What is the reasoning behind this?

+3  A: 

285212672 is an int value. printf expects an unsigned long long and you're passing it an int. Consequently, it'll take more bytes off the stack than you passed a real value and prints garbage. When you put it in an unsigned long long variable before passing it to the function, it'll be promoted to unsigned long long in the assignment line and you pass that value to printf which works correctly.

Mehrdad Afshari
+22  A: 

Try 285212672ULL; if you write it without suffixes, you'll find the compiler treats it as a regular integer. The reason it's working in a variable is because the integer is being cast up to an unsigned long long in the assignment, so that the value passed to printf() is the right type.

And before you ask, no, the compiler probably isn't smart enough to figure it out from the "%llu" in the printf() format string. That's a different level of abstraction. The compiler is responsible for the language syntax, printf() semantics are not part of the syntax, it's a runtime library function (no different really from your own functions except that it's included in the standard).

Consider the following code for a 32-bit int and 64-bit unsigned long long system:

#include <stdio.h>

int main (void) {
    printf ("%llu\n",1,2);
    printf ("%llu\n",1ULL,2);
    return 0;
}

which outputs:

8589934593
1

In the first case, the two 32-bit integers 1 and 2 are pushed on the stack and printf() interprets that as a single 64-bit ULL value, 2 x 232 + 1. The 2 argument is being inadvertently included in the ULL value.

In the second, you actually push the 64-bit 1-value and a superfluous 32-bit integer 2, which is ignored.

Note that this "getting out of step" between your format string and your actual arguments is a bad idea. Something like:

printf ("%llu %s %d\n", 0, "hello", 0);

is likely to crash because the 32-bit "hello" pointer will be consumed by the %llu and %s will try to de-reference the final 0 argument. The following "picture" illustrates this (let's assume that cells are 32-bits and that the "hello" string is stored at 0xbf000000.

What you pass     Stack frames     What printf() uses
                 +------------+
0                | 0          | \
                 +------------+  > 64-bit value for %llu.
"hello"          | 0xbf000000 | /
                 +------------+
0                | 0          |    value for %s (likely core dump here).
                 +------------+
                 | ?          |    value for %d (could be anything).
                 +------------+
paxdiablo
but i think compiler is smart enough to figure out %u in printf format spec, try printf("%d %u",~0,~0).. both will print values as expected..
Neeraj
No - those data types are the same size - it's printf() figuring that out - try %d with 'a'.
paxdiablo
Pax: That's fine too, character literals are integer constants.
caf
Right you are @caf, but the compiler is *still* not looking inside printf arguments. And it shouldn't since that's a different abstraction level (language vs. runtime library). I've updated the answer with more info.
paxdiablo
In the code you use 1 and 2, but in the text you use 0 and 1 :)
Johannes Schaub - litb
Thanks, @litb, fixed. I originally had 0/1 but the behavior seemed a little ambiguous, which is why I changed [parts of] it.
paxdiablo
A: 

Datatype is simply a way of interpreting contents of a memory location.
in first case the constant value is stored in read only memory location as an int, the printf tries to interpret this address as 8 byte location as it is instructed that the value stored is long long in process of which it prints garbage value.
In the second case printf tries to interpret a long long value as 8 bytes and it prints what is expected.

GG
+5  A: 

It's worth pointing out that some compilers give a useful warning for this case - for example, this is what GCC says about your code:

x.c: In function ‘main’:
x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’
caf
Yet another reason not to ignore compilation *warnings*.
Reuben