intptr_t有什么用?

What is the use of intptr_t?

我知道它是一个整数类型,可以在不丢失数据的情况下转换为 to/from 指针,但我为什么要这样做?使用整数类型比 void* 用于保存指针和 THE_REAL_TYPE* 用于指针运算有什么优势?

编辑
标记为“已被问及”的问题没有回答这个问题。问题是使用 intptr_t 作为 void* 的一般替代品是否是个好主意,那里的答案似乎是“不要使用 intptr_t”,所以我的问题仍然是有效:intptr_t 的一个好的用例是什么?

主要原因是,您不能对 void * 进行按位运算,但可以对 intptr_t 进行同样的操作。

很多时候,需要对地址进行按位运算,可以使用intptr_t.

但是,对于按位运算,最好的方法是使用 unsigned 对应的 uintptr_t

by @chux中所述,指针比较是另一个重要方面。

此外,FWIW,根据 C11 标准,§7.20.1.4,

These types are optional.

What is the use of intptr_t?

使用示例:顺序比较。
比较指针是否相等不是问题。
其他比较操作如 >, <= 可能是 UB。 C11dr §6.5.8/5 关系运算符。
所以先转换成intptr_t

[编辑]新示例:按指针值对指针数组进行排序。

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);

[前例] 使用示例:测试指针是否属于指针数组。

#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);
}

正如 , 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 评论的那样。

uintptr_t类型在编写内存管理代码时非常有用。那种代码想根据通用指针 (void *) 与其客户端通信,但在内部对地址进行各种算术运算。

您可以通过根据 char * 进行操作来做一些相同的事情,但不是所有事情,结果看起来像 Ansi C 之前的版本。

并非所有内存管理代码都使用 uintptr_t - 例如,BSD 内核代码定义了具有类似属性的 vm_offset_t。但是如果你正在写,例如调试 malloc 包,为什么要发明自己的类型?

当您的 printf 中有 %p 可用并且正在编写需要在各种体系结构上以十六进制打印指针大小的整数变量的代码时,它也很有帮助。

我发现 intptr_t 的用处不大,除了可能作为投射时的一个中转站,以避免在同一投射中更改符号和整数大小的可怕警告。 (在所有相关架构上编写通过 -Wall -Werror 的可移植代码可能有点困难。)

还有语义方面的考虑。

一个 void* 应该 指向某个东西 。尽管具有现代实用性,但指针不是内存地址。好的,它 usually/probably/always(!) 包含一个,但它不是数字。这是一个指针。指的是一个东西。

A intptr_t 没有。它是一个整数值,可以安全地转换 to/from 指针,因此您可以将它用于古董 API,将其打包到 pthread 函数参数中,诸如此类。

这就是为什么您可以在 intptr_t 上比在 void* 上做更多繁琐的事情,以及为什么您应该通过使用正确的工作类型来自我记录。

最终,几乎所有 都可以 为整数(请记住,您的计算机处理数字!)。指针可以是整数。但他们不是。它们是指针,因为它们有不同的用途。而且,理论上,它们可能不是数字。

(u)intptr_t 用于对指针进行算术运算,特别是按位运算。但正如其他人所说,您几乎总是想使用 uintptr_t 因为按位运算最好在无符号的情况下完成。但是,如果您需要进行 算术右移 ,那么您 必须使用 intptr_t。它通常用于指针中存储数据,通常称为tagged pointer

  • 在x86-64中你可以使用高16/7位作为数据,但是你必须手动对make the pointer canonical because it currently doesn't has a flag for ignoring the high bits like in ARM进行符号扩展。因此,例如,如果您有 char* address,那么您需要在取消引用之前执行此操作

    char* pointer = (char*)((intptr_t)address << 16 >> 16);
    
  • 32位Chrome V8引擎使用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
    

    所以当指针的最低有效位为0时,将右移得到原始的31位有符号整数

    int v = (intptr_t)address >> 1;
    

阅读更多信息


另一种用法是将有符号整数作为 void* 传递,这通常在回调函数或线程中完成

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);
}