46

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:

  • Is my understanding correct that this behavior is not defined by the C99 standard?
  • If so, is there a standard approved, portable way of doing this?

8 Answers8

41

#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).

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • 3
    Interesting. This is the first time I see this PRIxPTR notation. Is this standard, or gcc specific? If standard, which standard? C89 or C99? –  Aug 10 '09 at 14:50
  • 2
    Standard in C99. The macro PRIxPTR is defined in . The uintptr_t type is defined in . – AProgrammer Aug 10 '09 at 15:00
  • 1
    Let me make sure I got this straight. The reason what I tried to do is not portable is because not all platforms display pointers as 0x..... Because of this padding with 0s is not portable. On the other hand, printf("%016"PRIxPTR", (uintptr_t)ptr); will on all platforms print my pointer with the right lenght, formated as a 0x... number, padded with 0s, without unsafe casting. –  Aug 10 '09 at 15:16
  • Right. But you depend on C99 -- Microsoft compilers and runtimes support it badly from what I read here and elsewhere (I'm a Unix guy), and some gcc packaging for Windows are reusing the Microsoft run-time. BTW, you won't get the 0x prefix (put it yourself or use a # flag, but note that the flag won't put it for pointers which are converted to 0 -- usually NULL pointers). – AProgrammer Aug 10 '09 at 15:26
  • Makes sense. And as Of C89, there is no portable way of doing this? –  Aug 10 '09 at 15:30
  • It was quite a common practice in C89 to assume that `unsigned long` was able to contains a pointer without loss. Microsoft broke that practice by the choice of the LLP64 Windows 64 bits (while all other systems I know of had chosen LP64 or even ILP64 years before). So I know of no sure way which works on common systems. Defining your own inttypes.h is probably the easiest thing to do (after all, that possibility is one probable reason for which there are macros and not new format for the stdint.h types). – AProgrammer Aug 10 '09 at 15:48
  • 1
    @Artelius, one of the words writable using only letters which are hexadecimal digit (see http://nedbatchelder.com/text/hexwords.html). – AProgrammer Sep 04 '09 at 07:46
  • Suppose I wanted it padded to 8 digits on a system with 32-bit pointers, and to 16 digit on a system with 64-bit pointers. Is there any easy way to do that? – jchl Jan 17 '12 at 12:42
  • @jchl `printf("%0"PTRDISPLAYWIDTH PRIxPTR"\n", (uintptr_t)ptr);` with PTDISPLAYWIDTH a macro expanding to "8" or "16" depending on the kind of systems you are on. – AProgrammer Jan 17 '12 at 12:55
  • 2
    Shouldn't have be `DEAD:BEEF`? – Mike Mar 18 '13 at 18:15
  • 1
    @Mike, you made me understand Artelius comment. – AProgrammer Mar 18 '13 at 18:25
9

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).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Note that formally you need to cast to uintptr_t. – AProgrammer Aug 10 '09 at 15:55
  • Or you could use `"%#016" PRIxPTR` instead, the `#` says to display the base for octal and hexadecimal. – Joe D Jul 23 '10 at 15:57
  • @Joe: you are right - you could use the '#', but that prints 0x01ab or 0X01AB and I don't like either of those notations: I like 0x01AB. What I wrote gets me what I like and not what I dislike. – Jonathan Leffler Jul 23 '10 at 19:28
5

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.

Community
  • 1
  • 1
stefanct
  • 2,503
  • 1
  • 28
  • 32
5

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.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • The problem I find with this suggestion: `casting the pointer to unsigned long and print... using "0x%16x"` is that you're trading one warning for another. At least for me this gets rid of the warning of the 0 flag used with `%p` but then I get a `warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]` instead. – Mike Mar 18 '13 at 19:16
  • @Mike: I suggested that only for the case where neither `"%p"` nor `uintptr_t` is available. – Keith Thompson Mar 18 '13 at 19:20
  • 2
    There IS a standards compliant way, which is to use PRIxPTR. If you choose to use a compiler that doesn't conform to C99, like MSVC 2011, it's up to you to find out how to do it, probably with an #ifdef. I don't even think MSVC conforms to C89 fully. – cmccabe Oct 28 '13 at 02:09
  • 2
    Note that on Windows 64-bit, the type `unsigned long` is still a 32-bit quantity, though pointers are 64-bits. Using `0x%16lx` would fail on Win64, therefore; you'd always get 8 leading zeros. In principle, using `unsigned long long` and `0x%16llx` would work — MS VS 2015 runtime does support the `ll` modifier; it also uses `I64` to indicate 64-bit numbers ([`printf()` format specifiers](https://msdn.microsoft.com/en-us/library/56e442dc.aspx)). Check whether earlier versions supported `ll` before using it. – Jonathan Leffler Jan 17 '18 at 16:17
  • I would think another portable approach would be to write a function which stores the pointer into a `void*` and then writes out `sizeof (void*)` hex bytes starting at the address of (not in) that pointer. – supercat Jan 25 '18 at 21:12
  • 1
    @PaulStelian: Whoever/whatever is going to be reading the output will need to know how it is formatted. Having consecutive addresses represented as FF5F3412 and 00603412 might not be as convenient for human parsing as 12345FFF and 12346000 would be, but it would nonetheless be a usable representation for a human who understands it, or for a program that would need to re-read the address while the object is still live. Having an option to output bytes in reverse order could be handy, but one would then risk confusion if routines to output and read back pointers are configured differently. – supercat Jul 23 '19 at 15:27
  • @supercat Well maybe establish that the pointers are only to be read on the same platform or a standard endianness (networking picked big endian) – Paul Stelian Jul 23 '19 at 22:37
  • @PaulStelian: If the address of some useful object happens to have bit pattern represented by hex bytes 12,34,56,78, then within that particular execution of the program, unless or until the lifetime of the object ends, a pointer formed from hex bytes 12,34,56,78 will identify that object. That particular pointer's bit pattern would have no significance to portable code in any other context. – supercat Jul 24 '19 at 19:38
  • Well, it would certainly be confusing to whoever is trying to debug the program to see addresses like that, especially since sometimes we need to compare addresses (I needed to do that yesterday in fact) – Paul Stelian Jul 25 '19 at 06:06
0

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.

Rodrigo Queiro
  • 1,324
  • 8
  • 15
  • Interesting. It confirms that %016p" isn't portable, since on my machine (64-bit linux) it pads purely with 0s, not with spaces. –  Aug 10 '09 at 14:39
  • That's not a bug, just an unexpected feature. ;-) He's casting it to a 64-bit value on a 32-bit architecture. – mpontillo Dec 11 '12 at 02:26
  • The cast to `unsigned long` before recasting to `unsigned long long` breaks if `sizeof(unsigned long) != sizeof(unsigned long long)`, as on Windows 64-bit. – Jonathan Leffler Jan 17 '18 at 16:24
0

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

Jerry Miller
  • 921
  • 1
  • 8
  • 11
-1

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.

T.E.D.
  • 44,016
  • 10
  • 73
  • 134
  • 1
    My main system is actually a 64-bit one, so %x doesn't really cut it. Avoiding this kind of problem is why I want to use %p. –  Aug 10 '09 at 14:18
  • `%#016lx` should work on whatever system your on, a pointer is guaranteed to fit inside a long. (I think) BTW that is the letter 'l' not 1 the number before the 'x'. – Joe D Jul 23 '10 at 15:59
  • @Joe D - VS2005 disagrees. It gives me warnings about possible lost data unless I use something like ULONG_PTR instead. Also see the comment left by AProgrammer on the accepted answer. Of course VS isn't exactly known as a paragon of compliance... – T.E.D. Jul 23 '10 at 17:15
  • 1
    Oh, I forgot to mention the fact that it doesn't work on 64-bit MSVC. I think the both the 32-bit and 64-bit version warn about it. – Joe D Jul 23 '10 at 18:41
-1

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);
groovingandi
  • 1,986
  • 14
  • 16
  • 1
    Note my [comment](https://stackoverflow.com/questions/1255099/whats-the-proper-use-of-printf-to-display-pointers-padded-with-0s/1255341#comment83596093_6904396) about Windows 64-bit; this will not work there. – Jonathan Leffler Jan 17 '18 at 16:20