65

I have a question regarding using intptr_t vs. long int. I've observed that incrementing memory addresses (e.g. via manual pointer arithmetic) differs by data type. For instance incrementing a char pointer adds 1 to the memory address, whereas incrementing an int pointer adds 4, 8 for a double, 16 for a long double, etc...

At first I did something like this:

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:\n\n" );
printf( "pChar:  %d\n", (int)pChar );
printf( "pFloat:    %d\n", (int)pFloat );

which compiled and executed just fine, but XCode gave me warnings for my typecasting: "Cast from pointer to integer of different size."

After some googling and binging (is the latter a word yet?), I saw some people recommend using intptr_t:

#include <stdint.h>
...

printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

which indeed resolves the errors. So, I thought, from now on, I should use intptr_t for typecasting pointers... But then after some fidgeting, I found that I could solve the problem by just replacing int with long int:

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

So my question is, why is intptr_t useful, and when should it used? It seems superfluous in this instance. Clearly, the memory addresses for myChar and myFloat were just too big to fit in an int... so typecasting them to long ints solved the problem.

Is it that sometimes memory addresses are too big for long int as well? Now that I think about it, I guess that's possible if you have > 4GB of RAM, in which case memory addresses could exceed 232 - 1 (max value for unsigned long ints...) but C was created long before that was imaginable, right? Or were they that prescient?

Thanks!

3
  • 14
    Yes, binging is a word, which originally meant to indulge in an activity, especially eating, to excess.
    – ack
    Commented Jul 4, 2014 at 17:13
  • 2
    Using intptr_t with the printf functions is not portable, there is no format specifier for it. Cast the pointer to a void pointer instead and use the %p format specifier.
    – osvein
    Commented Nov 19, 2017 at 13:52
  • The minimal value for unsigned long int is 32 bits, it could also be 64 bits. You state, that the maximal value is 32.
    – Fabian
    Commented Jul 2 at 7:37

5 Answers 5

67

intptr_t is a new invention, created after 64-bit and even 128-bit memory addresses were imagined.

If you ever need to cast a pointer into an integer type, always use intptr_t. Doing anything else will cause unnecessary problems for people who need to port your code in the future.

It took a long time to iron out all of the bugs with this in programs like Mozilla/Firefox when people wanted to compile it on 64-bit Linux.

7
  • 26
    It would be better to use uintptr_t. Signed types are messy and surely make no sense with pointers... Commented Jun 13, 2011 at 5:10
  • Good call. Everything I see written about uintptr_t is in the context of c++ and the inttypes.h library. looks like it's in stdint.h, too, though. any difference? Commented Jun 13, 2011 at 5:30
  • 1
    Speaking of always using intptr_t this answer claims that it is not widely available stackoverflow.com/a/9492910/1073672 . So what to do then?
    – user10607
    Commented Dec 23, 2014 at 7:11
  • 2
    @user10607: It is available from C99 onwards. That's more than a decade. So unless you're using an ancient compiler like TurboC or a very exotic platform with no int to hold a pointer, it will be available ;)
    – MestreLion
    Commented Feb 17, 2015 at 6:13
  • 3
    @MestreLion that's not true, it's optional in C99
    – osvein
    Commented Nov 19, 2017 at 13:46
48

Here's the thing: on some platforms, int is the right size, but on others, long is the right size. How do you know which one is the one you should use? You don't. One might be right, but the standard makes no guarantees about which one it would be (if it is either). So the standard provides a type that is defined to be the correct size, regardless of what platform you're on. Where before you had to write:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

Now you just write:

#include <stdint.h>

And it covers so many more cases. Imagine specializing the snippet above for every single platform your code runs on.

12
  • 4
    @caravaggisto - Nope. long is only guaranteed to be 32 bits, and may not work on a 64-bit platform. You could always use long long, but that doesn't exist before C99. I doubt the allocation matters much. It's more a problem of correctness.
    – Chris Lutz
    Commented Jun 13, 2011 at 4:27
  • 2
    I would be very skeptical of any C book that teaches you to cast between pointers and integers. Actually almost all casts are indicative of bugs; they're at least a code smell. Pointer/integer casts are much worse. Commented Jun 13, 2011 at 5:12
  • 1
    @R..: Often an API which provides a callback function, or a messaging API like the Windows message pump, will have an integer data field or two that can be passed around. Your program may need to stuff a pointer into a DWORD or an int because that is all that is available in the API.
    – Zan Lynx
    Commented Sep 5, 2012 at 15:41
  • 3
    @ZanLynx WinAPI is a terrible API designed for horribly broken pre-standard C compilers in the mid-80s. It hasn't changed much since the Win16 era. There's a reason why MS is trying to kill it. Commented Mar 10, 2014 at 22:28
  • 2
    @william_grisaitis even today Microsoft's compiler uses 32 bits for long and 64 bits for a pointer. They didn't increase the size of long because they wanted to maintain backward compatibility. Commented Jul 9, 2023 at 14:36
14

First, intptr_t is only for data pointers (not functions) and is not guaranteed to exist.

Then, no, you shouldn't use it for the purpose of printing. The %p is for that. You just have to cast your pointer to (void*) and there you go.

It is also no good for arithmetic / accessing individual bytes. Cast to (unsigned char*) instead.

intptr_t is really for the rare occasions that you have to interpret pointers as integers (which they really aren't). Don't that if you mustn't.

3
  • thanks. What are pointers, then, if not (binary) integers? Thank you for your time. Commented Jun 14, 2011 at 16:57
  • 3
    @caravaggisto, this depends a lot on the architecture. Some architectures have segmented memory, so an address is composed of two parts. Just simple integer addition might for example not always give you a valid address. Arithmetic on unsigned char will do, provided you allocated a chunk that is large enough. Commented Jun 14, 2011 at 18:17
  • intptr_t or uintptr_t could be useful for certain scenarios. For example you are building your own version of malloc(), a specialized dynamic memory allocator, perhaps for an embedded system or another computer system where a specialized memory allocator is needed. Say you keep track of deallocated blocks in an explicit free linked list. You could use an intptr_t to store pointers to the next and previous blocks in the linked list. This would not be part of the payload itself, but rather the previous word before it, in a "header" or "footer" of the block.
    – Galaxy
    Commented Jul 4, 2018 at 22:42
13

You could make your life easier by using the p conversion specifier:

printf("%p\n", (void *)foo);

Also, the portable way to print a variable of type (u)intptr_t is to use the PRI*PTR macros from inttypes.h; the following is equivalent to using p on my platform (32-bit):

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

The casts to void * are necessary for full portability, but can be omitted on platforms with uniform pointer representations.

1
  • 1
    Although this probably should have been a comment to the question. Commented Jun 18, 2015 at 19:48
1
printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

This is wrong. And this is even worse

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

Is it that sometimes memory addresses are too big for long int as well?

Yes, nothing requires intptr_t to have the same size as long, for example on 64-bit Windows long is 32-bit but pointers are 64-bit, or some 128-bit architectures in the future may have 128-bit pointers but shorter long type. Therefore this is incorrect

Clearly, the memory addresses for myChar and myFloat were just too big to fit in an int... so typecasting them to long ints solved the problem

It only solves the problem on some platforms. You must use PRIdPTR or PRIiPTR for intptr_t. The correct way is like this

printf("pChar:  %" PRIiPTR "\n", (intptr_t)pChar);
printf("pFloat: %" PRIdPTR "\n", (intptr_t)pFloat);

But why do you need to print the decimal values of the pointers? Usually they're supposed to be printed as hex. Besides uintptr_t is preferred in most cases, especially when doing bitwise arithmetic on pointers (for example for tagged pointers or XOR linked list) so this is better

printf("pChar:  %" PRIxPTR "\n", (uintptr_t)pChar);
printf("pFloat: %" PRIxPTR "\n", (uintptr_t)pFloat);

why is intptr_t useful, and when should it used

There are very few cases where you need intptr_t rather than uintptr_t. See What is the use of intptr_t? for some examples

Not the answer you're looking for? Browse other questions tagged or ask your own question.