c++ intptr_t有什么用?

t8e9dugd  于 2023-01-28  发布在  其他
关注(0)|答案(5)|浏览(164)

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

    • 编辑**

标记为"already been asking"的问题没有回答这个问题,这里的问题是使用intptr_t作为void*的通用替代是否是个好主意,而这里的答案似乎是"don't use intptr_t",所以我的问题仍然有效:什么是intptr_t的好用例?

2j4z5cfb

2j4z5cfb1#

主要原因是,您不能在void *上执行按位操作,但可以在intptr_t上执行相同的操作。
在很多情况下,当你需要对一个地址执行位操作时,你可以使用intptr_t
但是,对于按位运算,最好的方法是使用unsigned的对应项uintptr_t
正如@chux在other answer中提到的,指针比较是另一个重要方面。
此外,根据C11标准§7.20.1.4,FWIW

  • 这些类型是可选的。*
jobtbby3

jobtbby32#

还有一个语义上的考虑。
一个void*被认为是“指向某个东西”。尽管现代实用性很强,但指针并不是内存地址。好吧,它通常/可能/总是(!)包含一个地址,但它不是一个数字。它是一个指针。它指向一个东西。
intptr_t则不然,它是一个整数值,可以安全地转换为指针,这样你就可以把它用于老式的API,把它打包成一个pthread函数参数,诸如此类。
这就是为什么在intptr_t上比在void*上可以做更多琐碎的事情,也是为什么应该通过使用适当的类型来实现自文档化。
最终,几乎所有的东西都可能是整数(记住,你的计算机是处理数字的!);指针也可能是整数,但它们不是;它们之所以是指针,是因为它们有不同的用途;而且,理论上,它们可以是数字以外的东西。

jgwigjjp

jgwigjjp3#

uintptr_t类型在编写内存管理代码时非常有用,这类代码希望用通用指针(void *)与客户端通信,但在内部对地址进行各种运算。
通过对char *进行操作,您可以做一些相同的事情,但不是所有事情,结果看起来像AnsiC之前的版本。
不是所有的内存管理代码都使用uintptr_t-例如,BSD内核代码定义了一个具有类似属性的vm_offset_t.但是,如果你正在编写调试malloc包,为什么要发明自己的类型呢?
当您的printf中有%p可用,并且正在编写需要在各种架构上以十六进制打印指针大小的整型变量的代码时,这也很有帮助。
我发现intptr_t用处不大,除了可能作为强制转换时的中转站,以避免在同一强制转换中更改有符号性和整数大小的可怕警告(编写在所有相关体系结构上传递-Wall -Werror的可移植代码可能有点困难)。

sc4hvdpw

sc4hvdpw4#

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。-只是不可移植的功能。我希望用它来解决this problem

7kqas0il

7kqas0il5#

(u)intptr_t用于对指针进行算术运算,特别是位运算。但正如其他人所说,您几乎总是希望使用uintptr_t,因为位运算在无符号中更好地完成。然而,如果您需要执行 * 算术右移 *,则必须使用intptr_t**1。它通常用于在指针中存储数据,通常称为tagged pointer

  • 在x86-64中,您可以use the high 16/7 bits of the address for data,但必须手动进行符号扩展以使指针规范化,因为它没有用于忽略高位like in ARM 2的标志。因此,例如,如果您有char* tagged_address,则需要在解引用它之前进行符号扩展
char* pointer = (char*)((intptr_t)tagged_address << 16 >> 16);
|----- 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;

有关详细信息,请参阅

另一种用法是将有符号整数作为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 support

相关问题