Fog Creek Software
Discussion Board




What's the deal with printf and %x in C?

I ran into this problem recently, and then I actually found an interview question that exposed it.  You would think %x would print out whatever %d does in hex, but that is apparently not the case.

The interview question was this:

I thought it had to do with assuming ints are 32 bits, or using + instead of |, but it doesn't...


given this function:

int make_int(int hi, int lo)
{
    return (hi << 16) + lo;
}

why does this print the expected value of 0xF000F,

    int a = make_int(0xF,0xF);
    printf( "value = 0x%x\n", a );

while this prints 0x80000000?

    printf( "value = 0x%x\n", make_int(0xF,0xF) );

Roose
Tuesday, May 18, 2004

gcc v2.6, both return

value = 0xf000f


Even it you use 16 bit ints (subst int16_t for int using stdint.h header), then you get 0xf.

I don't see how you ever get your second result.

hoser
Tuesday, May 18, 2004

Same result on Visual Studio .Net 2003.

Are you sure the example is right?

Mark L. Smith
Tuesday, May 18, 2004

Poking through the source code for output.c (which is what printf uses) leaves me w/o any clue why it doesn't work.

<output.c source code from C++.Net 7.1>
                while (precision-- > 0 || number != 0) {
                    digit = (int)(number % radix) + '0';
                    number /= radix;                /* reduce number */
                    if (digit > '9') {
                        /* a hex digit, make it a letter */
                        digit += hexadd;
                    }
                    *text.sz-- = (char)digit;      /* store the digit */
                }
</output.c source code from C++.Net 7.1>

Elephant
Tuesday, May 18, 2004

Is the premise that the vararg pulled the pointer to the function make_int?

.
Tuesday, May 18, 2004

good idea, but that would be this:

printf( "value = 0x%x\n", make_int );

and more nicely written as:

printf( "value = %p\n", make_int );

hoser
Tuesday, May 18, 2004

Both the examples will generate the same output. How could anything else happen? In both cases the printf parameter is an int. (%X might be expecting an unsigned int, but unsigned int and int are the same size, for obvious reasons.)

I might be inclined to believe it if make_int returned something other than int, because then your assignment would truncate the result while passing it directly to printf would invoke the varargs variable rules, giving two different results. (the rules are things like, floats become doubles (?), chars become ints (?) -- I can't remember, anyway probably best to commit them to memory if you're being interviewed with questions like this :)

Please don't leave us in suspense!

Tom
Tuesday, May 18, 2004

Sorry, I am an idiot.

I actually figured out the interview question like 3 DAYS ago, but I forgot I figured it out... I had copied my SOLUTION into the post above.

the trick was that the original function was

return hi << 16 + lo;

But the operator precedence is not what is intended, the fix is:

return (hi << 16) + lo;

Which is somewhat of a stupid interview question.

BUT I swear (in a separate instance) I ran into a problem where I did:

printf( "%d 0x%x", val, val );

And %x did not print what was printed by %d in hex, it printed something else.  Or that could be another case of too many 12 hour days recently... doh...

Roose
Wednesday, May 19, 2004

Heh, I was beginning to think it was one of those trick interview questions where the interviewer tells you that you are wrong, knowing that you're in fact right, simply so he can see how you will handle the situation and whether you can defend your position effectively...

John C.
Wednesday, May 19, 2004

Roose, that *still * doesn't make sense, because with the precedence wrong, printf would be showing 0x80000000 in both cases.

Danil
Wednesday, May 19, 2004

If your 'val' variable was larger than an int, it will get passed as larger than an int. So printf will print for the %d the first sizeof(int) chars'-worth of the data, and then for %X the following sizeof(int) chars'-worth. (A char is usually a byte.) On a little-endian system this will probably print out the number you were thinking of for the %d, followed by a number you didn't expect for the %X, but what you get depends on the stack layout and so on.

gcc with -Wall and the Intel C++ compiler both look for format string mismatches. gcc is better in this respect at least, because you can, or at least could, designate your own functions as being like printf too.

Tom
Wednesday, May 19, 2004

*  Recent Topics

*  Fog Creek Home