intptr_t有什么用?

Bar*_*uch 25 c c++ pointers

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

编辑
标记为"已被询问"的问题没有回答这个问题.问题是如果使用intptr_t作为一般的替换void*是一个好主意,那里的答案似乎是"不要使用intptr_t",所以我的问题仍然有效:什么是一个很好的用例intptr_t

Sou*_*osh 15

主要原因是,你不能对a进行按位操作void *,但你可以做同样的事情intptr_t.

在许多场合,您需要对地址执行按位操作,您可以使用intptr_t.

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

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

另外,FWIW,按照C11标准,§7.20.1.4,

这些类型是可选的.

  • 对于按位操作,我更喜欢无符号类型,即`std :: uintptr_t`.我必须承认,我仍然需要遇到签名版本的用例. (4认同)
  • @5gon12eder:我完全同意,但我知道至少有一个平台使用了实际签名的内存空间。诚然,Transputers 现在不是很先进,但 C 也针对更奇特的平台。仅供参考:这与指令集以及文字和指令的组成方式有关。非常有趣的主题表明,除了当前非常无聊(来自指令集)的架构之外,曾经有一些非常有趣的架构。 (3认同)
  • @baruch一个例子:你可能想生成一个正确对齐`int`的指针,所以你可以拿一些其他指针并将它增加到`sizeof(int)`的下一个倍数.(或`alignof(int)`) (3认同)
  • 指针类型不保证+1实际上将值增加1.`void*`没有`operator +`,尽管gcc支持它作为扩展.即使`sizeof(char)`(标准)和`sizeof(void)`(通过gcc扩展名)保证等于1,在`char*`或`void*`中加1也不能保证增加它们的数值1:示例是非字节可寻址机器(指针的低位始终为0).所以`intptr_t`也使'+/-更安全. (2认同)
  • @ user3528438 为什么要将指针增加任何数字,而不是它指向的对象大小的倍数? (2认同)
  • @baruch:普通指针上不可用的按位运算符的一个示例是使用异或来实现异或链表。 (2认同)

chu*_*ica 7

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);
Run Code Online (Sandbox Code Playgroud)

[原实例]使用示例:测试指针是否为指针数组.

#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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • @MM除非你正在编写核导弹发射控制系统 (3认同)

phu*_*clv 7

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

欲了解更多信息,请阅读


另一种用法是当您传递一个有符号整数时,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);
}
Run Code Online (Sandbox Code Playgroud)

1当然,当实现对有符号类型进行算术移位时

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

  • @ZanLynx 64 位地址空间是如此之大,以至于您几乎无法使用全部它(至少在个人电脑中)。只要从上位到下位使用,在可预见的将来都是安全的。当然总是更喜欢使用较低的位,它们总是安全的 (2认同)
  • @sklott [这是一个神话](https://www.computerworld.com/article/2534312/the--640k--quote-won-t-go-away----but-did-gates-really-say -it-.html),他从来没有说过。不,这是完全不同的,64 位的地址空间是 32 位地址空间的 40 亿倍以上,而从 x86 的 20 位地址空间(640KB 所属)切换到 32 位仅增加了 4096 倍。人的一生不可能从1数到2⁶⁴。而且宇宙中只有 ~10⁸⁰ 粒子,因此也不可能为每个人提供完整的 2⁶⁴ 字节地址空间 (2认同)

Arl*_*ens 6

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

你可以通过char*而不是所有的东西来做同样的事情,结果看起来像前Ansi C.

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

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

我发现intptr_t相当不太有用,除了可能作为转换时的方式站,以避免在同一个强制转换中改变签名和整数大小的恐惧警告.(编写在所有相关体系结构上传递-Wall -Werror的可移植代码可能有点困难.)


Lig*_*ica 6

还有一个语义考虑因素.

A void*应该指向某种东西.尽管具有现代实用性,但指针不是存储器地址.好吧,它通常/可能/总是(!)拥有一个,但它不是一个数字.这是一个指针.它指的是一件事.

A intptr_t没有.它是一个整数值,可以安全地转换为指针/从指针转换,因此您可以将它用于古老的API,将其打包成pthread函数参数,就像这样.

这就是为什么你可以做一些intptr_t比你更多的数字和小事void*,为什么你应该通过使用适当的工作类型自我记录.

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