Alana Storm
Alana Storm

Reputation: 166086

Printing a C Pointer as %i and %u

Context: Established programmer, revisiting C, not for any particular purpose other than better understanding what's really going on behind the scenes.

Consider this program.

#include <stdio.h>

int main()
{
    //https://stackoverflow.com/questions/2581769/dereference-a-pointer-inside-a-structure-pointer

    int concreteInteger = 42;
    int* pointerInteger;
    pointerInteger = &concreteInteger;

    printf("concreteInteger as %%i = %i\n",concreteInteger);
    printf("pointerInteger  as %%p = %p\n" ,(void*)pointerInteger);        
    printf("pointerInteger  as %%u = %u\n" ,(unsigned)pointerInteger);                
    printf("*pointerInteger as %%i = %i\n",*pointerInteger);
    printf("Done\n");
    return 0;
}

When I compile and run this program on OS X 10.11, I get the following output.

$ cc main.c;./a.out 
concreteInteger as %i = 42
pointerInteger  as %p = 0x7fff5d614878
pointerInteger  as %u = 1566656632
*pointerInteger as %i = 42
Done 

Where does 1566656632 come from? If I convert 1566656632 to hex I get 0x5D614878, not 0x7fff5d614878.

So where does 1566656632 come from? What incorrect assumption am I making above? Is casting a pointer as an unsigned number something that's undefined in C? If so, for bonus points, if I wanted to represent the hex address of a pointer as a number in base 10, what's the most straight forward way to do that?

Upvotes: 1

Views: 622

Answers (2)

Alana Storm
Alana Storm

Reputation: 166086

I marked the answer above as best because it got me where I needed to be, but the following program gave me what I wanted/needed.

#include <stdio.h>
#include <stdint.h>

//needed for the PRI*PTR Macros
#include <inttypes.h>

int main()
{
    //#include <inttypes.h>    
    int concreteInteger = 42;
    int* pointerInteger;
    pointerInteger = &concreteInteger;

    printf("concreteInteger as %%i = %i\n",concreteInteger);
    printf("pointerInteger  as %%p = %p\n" ,(void*)pointerInteger);        
    printf("pointerInteger  as %%u = %u\n" ,(unsigned)pointerInteger);                
    printf("*pointerInteger as %%i = %i\n",*pointerInteger);

    //as base 10 printf
    printf("pointerInteger  as %%lu = %lu\n" ,(uintptr_t)pointerInteger);                    
    printf("pointerInteger  as %%llu = %llu\n" ,(unsigned long long)(uintptr_t)pointerInteger);                    

    //as hex printf
    printf("pointerInteger  as %%lx = %lx\n" ,(uintptr_t)pointerInteger);           
    printf("pointerInteger  as %%llx = %llx\n" ,(unsigned long long)pointerInteger);           

    //using macros from inttypes.h
    printf("pointerInteger  as %%PRIxPTR = %" PRIxPTR "\n", (uintptr_t) pointerInteger);         
    printf("pointerInteger  as %%PRIxPTR = %" PRIdPTR "\n", (uintptr_t) pointerInteger);         

    // 140,734,665,721,976

    printf("Done\n");
    return 0;
}

A few key things to take away.

  1. I needed to the stdint.h library to get the (uintptr_t) type.
  2. The compiler warnings let me know that %lu and %lx were the format strings for displaying a (uintptr_t)
  3. Per @chux above, casting the pointer as a (unsigned long long) required %llu and %llx
  4. The converters over on binaryhexconverter.com are super useful

Upvotes: 0

chux
chux

Reputation: 153592

Conversion of a int* to void* does not lose needed information to reference the data as a conversion back to int* can reference concreteInteger.

Conversion of OP's int* to unsigned (32-bit) retained only 32-bits of the pointer's address.

Try %x and convert to uintptr_t first.
uintptr_t is in C99 and is an optional type, yet very commonly available.

 #include <stdint.h>
 ...
 printf("pointerInteger  as %%x = %x\n" ,(unsigned)(uintptr_t) pointerInteger); 

I'd expect the following, the lower 32-bits of the pointer. @Chris Turner

pointerInteger  as %x = 51af8878

Try %llx and convert to uintptr_t first to see more.

 printf("pointerInteger  as %%llx = %llx\n", 
    (unsigned long long)(uintptr_t) pointerInteger); 

With macros in <inttypes.h>, a matching print specifier can be use to print uintptr_t types directly.

 printf("pointerInteger  %" PRIxPTR "\n", (uintptr_t) pointerInteger); 

... to represent the hex address of a pointer as a number in base 10, what's the most straight forward way to do that?

The address of a pointer is not "hex", it is what is is. To display a pointer in decimal/hex/octal, convert to a wide enough integer and print using the matching print specifier. To print most portably, convert to void* and use "%p", which may print in hex.


Upvotes: 1

Related Questions