Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why / when to use `intptr_t` for type-casting in C?

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 2^32 - 1 (max value for unsigned long ints...) but C was created long before that was imaginable, right? Or were they that prescient?

Thanks!

like image 749
grisaitis Avatar asked Jun 13 '11 03:06

grisaitis


People also ask

Why use intptr_ t?

The intptr_t and uintptr_t types are extremely useful for casting pointers when you want to do address arithmetic. They should be used instead of long or unsigned long for this purpose.

What is intptr_t?

intptr_t is a signed integer memsize-type that can safely store a pointer regardless of the platform capacity. The type intptr_t is similar to the types ptrdiff_t and INT_PTR. The size of the type depends upon the data model.


2 Answers

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.

like image 59
Zan Lynx Avatar answered Oct 09 '22 11:10

Zan Lynx


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.

like image 26
Chris Lutz Avatar answered Oct 09 '22 11:10

Chris Lutz