Florian
Florian

Reputation:

What's the proper use of printf to display pointers padded with 0s

In C, I'd like to use printf to display pointers, and so that they line up properly, I'd like to pad them with 0s.

My guess was that the proper way to do this was:

printf("%016p", ptr);

This works, but this gcc complains with the following message:

warning: '0' flag used with ‘%p’ gnu_printf format

I've googled a bit for it, and the following thread is on the same topic, but doesn't really give a solution.

http://gcc.gnu.org/ml/gcc-bugs/2003-05/msg00484.html

Reading it, it seems that the reason why gcc complains is that the syntax I suggested is not defined in C99. But I can't seem to find any other way to do the same thing in a standard approved way.

So here is the double question:

Upvotes: 47

Views: 42962

Answers (8)

Keith Thompson
Keith Thompson

Reputation: 263347

The only portable way to print pointer values is to use the "%p" specifier, which produces implementation-defined output. (Converting to uintptr_t and using PRIxPTR is likely to work, but (a) uintptr_t was introduced in C99, so some compilers may not support it, and (b) it's not guaranteed to exist even in C99 (there might not be an integer type big enough to hold all possible pointer values.

So use "%p" with sprintf() (or snprintf(), if you have it) to print to a string, and then print the string padding with leading blanks.

One problem is that there's no really good way to determine the maximum length of the string produced by "%p".

This is admittedly inconvenient (though of course you can wrap it in a function). If you don't need 100% portability, I've never used a system were casting the pointer to unsigned long and printing the result using "0x%16lx" wouldn't work. [UPDATE: Yes, I have. 64-bit Windows has 64-bit pointers and 32-bit unsigned long.] (Or you can use "0x%*lx" and compute the width, probably something like sizeof (void*) * 2. If you're really paranoid, you can account for systems where CHAR_BIT > 8, so you'll get more than 2 hex digits per byte.)

Upvotes: 5

Jerry Miller
Jerry Miller

Reputation: 981

Note that if the pointer prints with the "0x" prefix, you would need to substitute "%018p" to compensate.

Upvotes: 0

AProgrammer
AProgrammer

Reputation: 52294

#include <inttypes.h>

#include <stdint.h>

printf("%016" PRIxPTR "\n", (uintptr_t)ptr);

but it won't print the pointer in the implementation defined way (says DEAD:BEEF for 8086 segmented mode).

Upvotes: 42

stefanct
stefanct

Reputation: 2944

This answer is similar to the one given earlier in https://stackoverflow.com/a/1255185/1905491 but also takes the possible widths into account (as outlined by https://stackoverflow.com/a/6904396/1905491 which I did not recognize until my answer was rendered below it ;). The following snipped will print pointers as 8 0-passed hex characters on sane* machines where they can be represented by 32 bits, 16 on 64b and 4 on 16b systems.

#include <inttypes.h>
#define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))

printf("0x%0*" PRIxPTR, PRIxPTR_WIDTH, (uintptr_t)pointer);

Note the usage of the asterisk character to fetch the width by the next argument, which is in C99 (probably before?), but which is quite seldom seen "in the wild". This is way better than using the p conversion because the latter is implementation-defined.

* The standard allows uintptr_t to be larger than the minimum, but I assume there is no implementation that does not use the minimum.

Upvotes: 5

Jonathan Leffler
Jonathan Leffler

Reputation: 754110

Use:

#include <inttypes.h>

printf("0x%016" PRIXPTR "\n", (uintptr_t) pointer);

Or use another variant of the macros from that header.

Also note that some implementations of printf() print a '0x' in front of the pointer; others do not (and both are correct according to the C standard).

Upvotes: 9

Rodrigo Queiro
Rodrigo Queiro

Reputation: 1336

Maybe this will be interesting (from a 32-bit windows machine, using mingw):

rjeq@RJEQXPD /u
$ cat test.c
#include <stdio.h>

int main()
{
    char c;

    printf("p: %016p\n", &c);
    printf("x: %016llx\n", (unsigned long long) (unsigned long) &c);

    return 0;
}

rjeq@RJEQXPD /u
$ gcc -Wall -o test test.c
test.c: In function `main':
test.c:7: warning: `0' flag used with `%p' printf format

rjeq@RJEQXPD /u
$ ./test.exe
p:         0022FF77
x: 000000000022ff77

As you can see, the %p version pads with zeros to the size of a pointer, and then spaces to the specified size, whereas using %x and casts (the casts and format specifier are highly unportable) uses only zeros.

Upvotes: 0

groovingandi
groovingandi

Reputation: 2006

As your link suggests already, the behaviour is undefined. I don't think there's a portable way of doing this as %p itself depends on the platform you're working on. You could of course just cast the pointer to an int and display it in hex:

printf("0x%016lx", (unsigned long)ptr);

Upvotes: -1

T.E.D.
T.E.D.

Reputation: 44804

I usually use %x to display pointers. I suppose that isn't portable to 64-bit systems, but it works fine for 32-bitters.

I'll be interested in seeing what answers there are for portable solutions, since pointer representation isn't exactly portable.

Upvotes: -1

Related Questions