什么是uintptr_t数据类型


Answers:


198

uintptr_t是能够存储数据指针的无符号整数类型。通常,这意味着它与指针的大小相同。

它可以在C ++ 11和更高版本的标准中定义。

想要一个可以容纳体系结构的指针类型的整数类型的常见原因是对指针执行特定于整数的操作,或者通过将其提供为整数“ handle”来掩盖指针的类型。

编辑:请注意,Steve Jessop 在这里为您的学究类型提供了另一个非常有趣的附加细节(我不会偷走):)


53
注意,size_t只需要足以容纳最大对象的大小即可,并且可以小于指针。这将预期对分段架构像8086(16位size_t,但32位void*
MSalters

3
为了表示两个指针之间的差异,您有ptrdiff_tuintptr_t不是为了那个。
jalf

3
@jalf:是的,是的,但是对于distance,您需要一个无符号类型。
德鲁·多曼

即使在C ++ 11中,uintptr_t定义显然也不是必需的(因此不是标准的)!cplusplus.com/reference/cstdint(我从Steve Jessop的回答中得到了提示)
安东尼奥

2
@MarcusJ unsigned int通常不够大。但是它可能足够大。该类型专门用于删除所有“假定”
德鲁·多曼

238

在提出问题时,第一件事uintptr_t不是C ++。在C99中的中<stdint.h>,作为可选类型。许多C ++ 03编译器确实提供了该文件。它也在C ++ 11中的中<cstdint>,该处也是可选的,它的定义参考C99。

在C99中,它被定义为“一个无符号整数类型,具有以下属性:可以将任何有效的指向void的指针转换为该类型,然后再转换回指向void的指针,结果将与原始指针进行比较”。

以此来表达它的意思。它没有说明尺寸。

uintptr_t的大小可能与相同void*。可能更大。可以想象,它可能会更小,尽管这样的C ++实现方式是错误的。例如,在一个假设的平台上,其中void*有32位,但仅使用了24位虚拟地址空间,您可以拥有一个uintptr_t满足要求的24位。我不知道为什么实现会这样做,但是标准允许这样做。


8
感谢您的“ <stdint.h>”。由于uintptr_t声明,我的应用程序未编译。但是,当我阅读您的评论时,我添加了“ #include <stdint.h>”,是的,现在可以使用了。谢谢!
JavaRunner 2014年

2
例如,要分配对齐的内存,还有其他用途?
legends2k

4
将指针强制转换为int的另一个常见用例是创建一个隐藏指针的不透明句柄。这对于从API返回对对象的引用很有用,在API中,您希望使对象对库保持私有,并防止应用程序对其进行访问。然后应用程序被强制使用API的对象上进行任何操作
乔尔·坎宁安

3
@JoelCunningham:可以,但是与使用没有什么不同void*。但是,它会影响将来的可能方向,尤其是如果您想更改为使用实际上只是整数句柄而不是根本没有转换指针的东西。
史蒂夫·杰索普

2
@CiroSantilli乌坎事件2016六四事件法轮功一个常见的用例是将一个int传递给一个API,该API期望对普通数据使用void *。节省您的按键typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;。相反:callback.dataPtr = (void*)val。另一方面,您当然void*必须将其强制转换回int
Francesco Dondi

19

这是一个无符号整数类型,恰好等于指针的大小。每当您需要对指针执行一些异常操作时(例如将所有位都求反(不要问为什么)),都将其uintptr_t强制转换为普通整数,然后将其转换为整数。


6
您当然可以这样做,但这当然是未定义的行为。我相信,对uintptr_t进行强制转换后,您所能做的就是将其传递给原样,然后再强制返回-其他所有内容都是UB。
sleske 2011年

有时您需要使用这些位,并且通常会产生编译器错误。一个常见的示例是为某些视频和性能关键型应用程序强制使用16字节对齐的内存。stackoverflow.com/questions/227897/…–

3
@sleske那不是真的。在具有自对齐类型的机器上,指针的两个最低有效位将为零(因为地址是4或8的倍数)。我已经看到了利用这种压缩数据的程序..
saadtaame

3
@saadtaame:我刚刚指出这是根据C标准的 UB 。这并不意味着它不能在某些系统上定义-编译器和运行时可以自由地为标准C中的UB定义特定的行为。因此,没有矛盾:-)。
sleske 2015年

2
它并不一定就是指针的大小。所有标准保证是,将void*指针值uintptr_t再次来回转换会产生一个void*与原始指针相等的值。uintptr_t通常与大小相同void*,但不能保证,也不能保证转换后的值的位具有任何特殊含义。并且不能保证它可以保存转换后的指向函数的指针值而不会丢失信息。最后,它不能保证存在。
基思·汤普森

15

对于“什么是uintptr_t数据类型”部分,已经有许多好的答案。我将尝试解决“它的用途是什么?” 在这篇文章中的一部分。

主要用于指针的按位操作。请记住,在C ++中,不能对指针执行按位运算。出于原因请参阅为什么不能在C中对指针进行按位运算,并且有办法解决吗?

因此,为了对指针进行按位运算,需要将指针转换为类型为unitpr_t的指针,然后执行按位运算。

这是我刚刚编写的一个函数示例,它执行按位互斥或将2个指针存储在XOR链表中,这样我们就可以像双向链表一样在两个方向上遍历,而不必在每个节点中存储2个指针。

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }

1
除了位算术之外,如果您希望基于地址而不是对象计数的语义也很好。
亚历克斯

4

冒着获得另一个死灵法师徽章的风险,我想为uintptr_t(甚至是intptr_t)添加一个很好的用法,那就是编写可测试的嵌入式代码。我主要针对各种手臂和当前的tensilica处理器编写嵌入式代码。它们具有各种本机总线宽度,而tensilica实际上是哈佛架构,具有可以不同宽度的单独的代码和数据总线。我对大多数代码使用测试驱动的开发风格,这意味着我对所编写的所有代码单元都进行了单元测试。在实际目标硬件上进行单元测试很麻烦,因此我通常使用Ceedling和GCC在Windows或Linux的基于Intel的PC上编写所有内容。话虽这么说,许多嵌入式代码都涉及到纠结和地址操纵。我的大多数Intel机器都是64位的。因此,如果要测试地址处理代码,则需要一个通用对象来进行数学运算。因此,在尝试部署到目标硬件之前,uintptr_t为您提供了一种与机器无关的调试代码的方式。另一个问题是对于某些机器,甚至某些编译器上的内存模型,函数指针和数据指针的宽度不同。在这些机器上,编译器甚至可能不允许在这两个类之间进行强制转换,但是uintptr_t应该能够保留其中任何一个。


不知道是否相关...您是否尝试过“ opaque typedefs”?视频:youtube.com/watch?
v = jLdSjh8oqmE

@sleske我希望C中可用。但是拥有stdint.h总比没有好。(也希望枚举更强类型的位置,但大多数调试器会尽一切努力找出它们)
bd2357
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.