intptr_t有什么用

What is the use of intptr_t?

本文关键字:什么 intptr      更新时间:2023-10-16

我知道它是一个整数类型,可以在不丢失数据的情况下强制转换为指针/从指针转换,但我为什么要这样做呢?与void*相比,拥有整数类型在保存指针和THE_REAL_TYPE*指针算术方面有什么优势?

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

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

在很多情况下,需要对地址执行按位运算时,可以使用intptr_t .

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

正如@chux在另一个答案中提到的,指针比较是另一个重要方面。

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

这些类型是可选的。

还有一个语义上的考虑。

void*应该指向某事。尽管具有现代实用性,但指针不是内存地址。好的,它通常/可能/总是(!)包含一个,但它不是一个数字。这是一个指针。它指的是一件事。

intptr_t则不然。它是一个整数值,可以安全地转换为指针,因此您可以将其用于古董 API,将其打包到pthread函数参数中,诸如此类。

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

最终,几乎所有东西都可以是整数(记住,你的电脑处理数字!指针可以是整数。但事实并非如此。它们是指针,因为它们用于不同的用途。而且,从理论上讲,它们可能是数字以外的其他东西。

uintptr_t类型在编写内存管理代码时非常有用。这种代码希望用通用指针(void *)与客户端对话,但在内部对地址进行各种算术。

你可以通过char *来做一些同样的事情,但不是全部,结果看起来像 Ansi C 之前。

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

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

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

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

正如 @M.M 所评论的那样,上述代码可能无法按预期工作。 但至少它不是 UB。- 只是非便携式功能。 我希望用它来解决这个问题。

(u)intptr_t 用于对

指针进行算术运算,特别是按位运算。但正如其他人所说,您几乎总是希望使用uintptr_t因为按位运算最好在无符号中完成。但是,如果您需要进行算术右移,则必须使用intptr_t 1。它通常用于在指针中存储数据,通常称为标记指针

  • 在 x86-64 中,您可以将地址的高 16/7 位用于数据,但您必须手动执行符号扩展以使指针成为规范指针,因为它没有像 ARM2 那样忽略高位的标志。因此,例如,如果您有char* tagged_address,则需要在取消引用之前执行此操作

    char* pointer = (char*)((intptr_t)tagged_address << 16 >> 16);
    
  • 32 位 Chrome V8 引擎使用 smi(小整数)优化,其中低位表示类型

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

    因此,当指针的最低有效位为 0 时,它将向右移动以检索原始的 31 位有符号 int

    int v = (intptr_t)address >> 1;
    

有关更多信息,请阅读

  • 在 64 位指针中使用额外的 16 位
  • 用于高效动态值表示的指针魔术
<小时 />

另一种用法是当您将有符号整数作为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);
}
<小时 />

1 当实现对有符号类型的课程进行算术转换时

2 非常新的 x86-64 CPU 可能具有 UAI/LAM 支持