As Mark Ransom noted in the comments, you can't use the %d
format specifier to print a pointer, you must use %p
in order for your code to be portable. It's technically Undefined Behavior to use anything besides %p
for pointers, but in all likelihood you'll see that it will seem to work ok on 32-bit systems.
However, on 64-bit systems, which I'd wager you're using, it blows up. Pointers are 8 bytes, but printf
only tries to read 4 bytes off of the stack when it sees %d
. For the second %d
specifier, it reads the next 4 bytes, which are the other 4 bytes of the pointer—if you're seeing 0 for that, it probably means that your pointer was allocated within the first 4 GB of memory on a little-endian system (i.e. its value was something like 0x00000000'xxxxxxxx
). The 8 bytes on the stack from the second pointer passed to printf
never get read.
The %p
specifier portably prints a pointer on all platforms, regardless of whether they're 32 bits, 64 bits, or some other size. One unfortunate consequence of this is that the exact output format is implementation-defined, meaning it can and does change between systems. Some systems might use a leading 0x
or 0X
, while others might have no prefix at all and just print the raw hex value. Some might pad to the left with 0's, and others might not.
If you want to control the exact output, then you can use a format of your choice (e.g. %08x
or %016llx
) as long as you cast the pointer appropriately for that specifier. For example, here's how you would do that on 32- and 64-bit systems:
printf("0x%08x\n", (unsigned int)(uintptr_t)myPointer); // 32-bit
printf("0x%016llx\n", (unsigned long long)(uintptr_t)myPointer); // 64-bit
The reason for casting twice is to avoid spurious compiler warnings, since some compilers will complain if you cast from a pointer type to an integer type which isn't guaranteed to be large enough to hold a pointer.