1

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?

Alana Storm
  • 164,128
  • 91
  • 395
  • 599

2 Answers2

1

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.


chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • "Conversion of a int* to void* does not lose needed information to reference the data." - That's not exactly correct. There might be less information in the `void *`. Only conversion to and back **to the same type** is guaranteeed to compare equal. That does not imply there is no information lost in the `void *` (at least the original type information is always lost)..And conversion of a pointr to an inteer is implementation defined. There is no guarantee it is truncated. – too honest for this site Jun 14 '17 at 16:22
  • @chux Thank you for the attention and the help. Re: the `uintptr_t` type -- is that something that's part of standard C systems? In some library? I ended up with a `use of undeclared identifier 'uintptr_t'` warning when I tried to use it. – Alana Storm Jun 14 '17 at 16:50
  • @AlanStorm Look for `uintptr_t` in ``. C11 and maybe C99 includes it. It is an _optional_ integer type, yet very likely defined. It is part of the C standard. If not found, cast to `unsigned long long` and `"%llx"` – chux - Reinstate Monica Jun 14 '17 at 16:54
  • Thanks @chux, `stdint.h` did it (although so did `inttypes.h` so, ¯\_(ツ)_/¯) Also, for anyone else coming along, I'm going to tack on the formats I ended up using to @chux's answers for completeness. – Alana Storm Jun 14 '17 at 17:10
  • @AlanStorm Better to post your own answer or append to your post than change this answer. Suggest rolling back the edit. BTW: `%lx\n" ,(uintptr_t)` is not necessarily matching. – chux - Reinstate Monica Jun 14 '17 at 17:14
  • @chux Sure thing. – Alana Storm Jun 14 '17 at 17:16
  • Why not use the correct conversion type specifier for `uintptr_t` at least? Not that it really makes sense not to use `%p`. – too honest for this site Jun 14 '17 at 17:17
  • @Olaf in _C11 7.21.6.1 The fprintf function_, I did not not see a specified matching print specifier for `uintptr_t` as so did not include [that idea](https://stackoverflow.com/questions/44549669/printing-a-c-pointer-as-i-and-u/44549984?noredirect=1#comment76092398_44549984) here. – chux - Reinstate Monica Jun 14 '17 at 17:21
  • @chux: There is also none for `uint32_t` in the function itself. You are aware about `inttypes.h`? (yes, `%zu` is superfluous actually, too). As with all your code above: the conversion of the pointer is implementation defined and printing the integers is even less defined. – too honest for this site Jun 14 '17 at 17:24
0

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
Alana Storm
  • 164,128
  • 91
  • 395
  • 599
  • 1
    `uintptr_t` might not be the same type as `unsigned long`, so `printf("pointerInteger as %%lu = %lu\n" ,(uintptr_t)pointerInteger);` may need work on other platforms. – chux - Reinstate Monica Jun 14 '17 at 17:24
  • 1
    You need `"%" PRIuPTR` to print a `uintptr_t` portably, not `"%lu"`. You need `#include ` to get all the `PRI`/`SCN` macros. – Chris Dodd Jun 14 '17 at 18:07