简体   繁体   中英

What is the use of intptr_t?

I know it is an integer type that can be cast to/from pointer without loss of data, but why would I ever want to do this? What advantage does having an integer type have over void* for holding the pointer and THE_REAL_TYPE* for pointer arithmetic?

EDIT
The question marked as "already been asked" doesn't answer this. The question there is if using intptr_t as a general replacement for void* is a good idea, and the answers there seem to be "don't use intptr_t", so my question is still valid: What would be a good use case for intptr_t ?

The primary reason, you cannot do bitwise operation on a void * , but you can do the same on a intptr_t .

On many occassion, where you need to perform bitwise operation on an address, you can use intptr_t .

However, for bitwise operations, best approach is to use the unsigned counterpart, uintptr_t .

As mentioned in the other answer by @chux , pointer comparison is another important aspect.

Also, FWIW, as per C11 standard, §7.20.1.4,

These types are optional.

There's also a semantic consideration.

A void* is supposed to point to something . Despite modern practicality, a pointer is not a memory address. Okay, it usually/probably/always(!) holds one, but it's not a number. It's a pointer. It refers to a thing.

A intptr_t does not. It's an integer value, that is safe to convert to/from a pointer so you can use it for antique APIs, packing it into a pthread function argument, things like that.

That's why you can do more numbery and bitty things on an intptr_t than you can on a void* , and why you should be self-documenting by using the proper type for the job.

Ultimately, almost everything could be an integer (remember, your computer works on numbers!). Pointers could have been integers. But they're not. They're pointers, because they are meant for different use. And, theoretically, they could be something other than numbers.

The uintptr_t type is very useful when writing memory management code. That kind of code wants to talk to its clients in terms of generic pointers ( void * ), but internally do all kinds of arithmetic on addresses.

You can do some of the same things by operating in terms of char * , but not everything, and the result looks like pre-Ansi C.

Not all memory management code uses uintptr_t - as an example, the BSD kernel code defines a vm_offset_t with similar properties. But if you are writing eg a debug malloc package, why invent your own type?

It's also helpful when you have %p available in your printf , and are writing code that needs to print pointer sized integral variables in hex on a variety of architectures.

I find intptr_t rather less useful, except possibly as a way station when casting, to avoid the dread warning about changing signedness and integer size in the same cast. (Writing portable code that passes -Wall -Werror on all relevant architectures can be a bit of a struggle.)

What is the use of intptr_t?

Example use: order comparing.
Comparing pointers for equality is not a problem.
Other compare operations like >, <= may be UB. C11dr §6.5.8/5 Relational operators.
So convert to intptr_t first.

[Edit] New example: Sort an array of pointers by pointer value.

int ptr_cmp(const void *a, const void *b) {
  intptr_t ia = (intptr) (*((void **) a));
  intptr_t ib = (intptr) (*((void **) b));
  return (ia > ib) - (ia < ib);
}

void *a[N];
...
qsort(a, sizeof a/sizeof a[0], sizeof a[0], ptr_cmp);

[Former example] Example use: Test if a pointer is of an array of pointers.

#define N  10
char special[N][1];

// UB as testing order of pointer, not of the same array, is UB.
int test_special1(char *candidate) {
  return (candidate >= special[0]) && (candidate <= special[N-1]);
}

// OK - integer compare
int test_special2(char *candidate) {
  intptr_t ca = (intptr_t) candidate;
  intptr_t mn = (intptr_t) special[0];
  intptr_t mx = (intptr_t) special[N-1];
  return (ca >= mn) && (ca <= mx);
}

As commented by @MM , the above code may not work as intended. But at least it is not UB. - just non-portably functionality. I was hoping to use this to solve this problem .

(u)intptr_t is used when you want to do arithmetic on pointers, specifically bitwise operations. But as others said, you'll almost always want to use uintptr_t because bitwise operations are better done in unsigned. However if you need to do an arithmetic right shift then you must use intptr_t . It's usually used for storing data in the pointer, usually called tagged pointer

  • In x86-64 you can use the high 16/7 bits for data, but you must do sign extension manually to make the pointer canonical because it currently doesn't has a flag for ignoring the high bits like in ARM . So for example if you have char* address then you'll need to do this before dereferencing it

    char* pointer = (char*)((intptr_t)address << 16 >> 16);
  • The 32-bit Chrome V8 engine uses smi (small integer) optimization where the low bit denotes the type

     |----- 32 bits -----| Pointer: |_____address_____w1| # Address to object, w = weak pointer Smi: |___int31_value____0| # Small integer

    So when the pointer's least significant bit is 0 then it'll be right shifted to retrieve the original 31-bit signed int

    int v = (intptr_t)address >> 1;

For more information read


Another usage is when you pass a signed integer as void* which is usually done in callback functions or threads

void* my_thread(void *arg)
{
     intptr_t val = (intptr_t)arg;
     // Do something
}

int main()
{
    pthread_t thread1;
    intptr_t some_val = -2;
    int r = pthread_create(&thread1, NULL, my_thread, (void*)some_val);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM