Please do not use %x for pointers

2007-01-23 23:10:09 -08:00

I often see code like this:

printf(“Address of foo: %x\n”, &foo);

The intent here is to print the address in hexadecimal format. Good plan; bad execution.

First, here’s one possible output:

Address of foo: 123456

Is this decimal (%u), hex (%x), or octal (%o)? I can’t tell from the output; for all I know, the person who wrote that line is a real newbie and used %i or %d. (That would be even worse for really high addresses, as they will then be formatted as negative numbers.)

So the author changes the line to:

printf(“Address of foo: 0x%x\n”, &foo);

Now the output has the 0x prefix, making clear that it’s hex, but there’s still a bug here. The bug is that %x is not the correct formatter for pointers.

The type expected by %x is unsigned int. For the past decade or so, this has not been a problem because on all PCs, including Macs, the size of a pointer has been equal to the size of an int.

But over the past couple years and the next couple years, there’s a transition underway to LP64, wherein long ints and pointers are 64-bit. Plain ints won’t be; they’ll still be 32 bits. This means that you’ll get funky results, and possibly crashes (with %s, %n, and %@), if you’re using %x for your pointers.

You could use %lx (unsigned long int), but it’s still the wrong type. There is a formatter specifically for pointers: %p. It even provides the “0x” prefix for you.

printf(“Address of foo: %p\n”, &foo);

Address of foo: 0x1e240

So, in order to make your code both more stable and more future-proof, please use %p, not %x or %lx, for formatting your pointers.

6 Responses to “Please do not use %x for pointers”

  1. Colin Barrett Says:

    So true. %p forever.

    While we’re sharing development practices, I find it really helpful, especially on a large codebase with spammy log messages, to prefix my debug logs with the name of the file they’re in (and possibly the line number as well). Then I can use Console.app’s filtering support and just see my log messages.

  2. Peter Hosey Says:

    Even better, use __FILE__ and __LINE__:

    fprintf(stderr, “%s:%u: Address of foo: %p\n”, __FILE__, __LINE__, &foo);

    This makes it easy to copy and paste the debug line to some other place in the file, or to another file.

  3. Evan Says:

    Hm, we could make AILog() [and equivalents elsewhere] do that automatically… might be *too* noisy, though.

  4. Peter Hosey Says:

    Evan: I assume you mean the __FILE__/__LINE__ magic, as doing correct s/%x/%p/ on every AILog is impossible, and doing it unconditionally is unreasonable.

    (For those who don’t know, AILog is a macro in Adium’s source code that directs the output to a debug log window that only exists in betas and trunk builds, as opposed to stderr/console.log.)

  5. Evan Says:

    Hah, yes, ‘that’ referred to prepending __FILE__:__LINE__. On second thought, that would make a giant mess of our incoming libgaim debugging, because it all routes through a single function, adiumGaimDebug().

    Still, it makes sense in a sizable project to have an easy way to include such information in a log.

    (I failed the spam prevention, forgetting to fix it. Went back to try again and was told this was a duplicate comment. Guess it must have entered a verification queue — you can ignore the previous attempt).

  6. Peter Hosey Says:

    Currently, Negative Turing Test (the spam-prevention thing) blocks failed comments by marking them as spam (ostensibly for plug-ins like Akismet). This blocks them, but causes the problem you described. One of the things on the to-do list is a way to simply delete the comment if it fails the test.

Leave a Reply

Do not delete the second sentence.