什么是uintptr_t数据类型?

342

uintptr_t是什么,它可以用来做什么?


6
这种类型以及其他相关类型的详细信息可以在这里找到:http://www.opengroup.org/onlinepubs/000095399/basedefs/stdint.h.html - Void
2
http://en.cppreference.com/w/cpp/types/integer 列出了C++11的可选类型 std::uintptr_tstd::intptr_t - alfC
5个回答

303

首先,当问题被提出时,C++中没有 uintptr_t 这个类型,它是在 C99 标准的 <stdint.h> 中作为一个可选类型定义的。许多 C++03 编译器提供了这个文件。它也出现在 C++11 中,在 <cstdint> 中也是作为一个可选类型,而且它的定义参考了 C99。

在 C99 中,它被定义为“一种无符号整数类型,其特性是任何指向 void 的有效指针都可以转换为此类型,然后再转换回 void 指针,并且结果将与原始指针相等”。

只要理解字面含义就好,它并没有说什么关于大小的事情。

uintptr_t 可能与 void* 大小相同。它可能更大。它也可能更小,尽管这样的 C++ 实现有些奇怪。例如,在某些假设的平台上,void* 是32位,但只使用了24位虚拟地址空间,你可以有一个24位的 uintptr_t 来满足这个要求。我不知道为什么会有这样的实现,但标准允许这样做。


17
感谢您提供了"<stdint.h>"库。由于缺少uintptr_t声明,我的应用程序无法编译。但是当我看到您的评论后,我添加了"#include <stdint.h>",现在可以正常工作了。谢谢! - JavaRunner
4
比如,为了分配对齐的内存(例如在stackoverflow.com/q/227897/183120中讨论的),还有其他用途? - legends2k
5
将指针转换为整数的另一个常见用途是创建一个隐藏指针的不透明句柄。这对于从API返回对象的引用非常有用,其中您想将对象保持私有,并防止应用程序访问它。然后,应用程序被迫使用API来执行任何与对象相关的操作。 - Joel Cunningham
5
那行得通,但并没有与使用"void*"有任何区别。然而,它可能会影响未来的发展方向,特别是如果你想改用真正只是整数句柄而不是转换后的指针。 - Steve Jessop
2
常见的用例是将int传递给期望void*的API以进行通用数据处理。这样可以节省您的击键量:typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;。相反,使用callback.dataPtr = (void*)val。在另一方面,您当然会得到void*并且必须将其转换回int - Francesco Dondi
显示剩余2条评论

262

uintptr_t 是一种无符号整型,能够存储一个数据指针 (是否可以保存函数指针未指定)。通常来说,这意味着它的大小与指针相同。

它是在C++11及以后的标准中进行可选定义。

希望拥有一个整型类型能够保存计算机架构的指针类型,通常是为了在指针上执行整数特定操作,或者通过将其提供为整数“句柄”来模糊指针的类型。


66
请注意,size_t 只需要足以容纳最大对象的大小,并且可以比指针更小。在类似于 8086 的分段体系结构中,这是可以预期的(size_t 是 16 位,但 void* 是32位)。 - MSalters
4
为了表示两个指针之间的差异,可以使用 ptrdiff_t。而 uintptr_t 不适用于此。 - jalf
3
针对“距离”,你需要使用无符号类型,但对于“差异”则不需要。 - Drew Dormann
1
uintptr_t的定义显然在C++11中甚至不是强制性的(因此不是标准!)http://www.cplusplus.com/reference/cstdint/(我从Steve Jessop的回答中得到了提示) - Antonio
2
@MarcusJ unsigned int通常不够大。但它可能足够大。这种类型的存在是为了消除所有的“假设”。 - Drew Dormann
显示剩余3条评论

31

这是一个无符号整数类型,大小正好与指针相同。每当需要对指针进行不寻常的操作时 - 比如颠倒所有位(不要问为什么),您将其转换为 uintptr_t 并像处理普通的整数一样进行操作,然后再转换回去。


6
当然,你可以这样做,但那将是未定义行为。我认为对于一个uintptr_t类型的强制类型转换的结果,唯一能够做的就是传递它而不做任何修改,并再次将其强制类型转换回来 - 其他任何操作都将导致未定义行为。 - sleske
有时候你需要操作位,但这通常会产生编译器错误。一个常见的例子是对于某些视频和性能关键应用程序强制使用16字节对齐内存。https://dev59.com/pHVC5IYBdhLWcg3wpSzf - Cloud
4
@sleske那不是真的。在具有自对齐类型的机器上,指针的最低有效位将为零(因为地址是4或8的倍数)。我见过利用此特性压缩数据的程序。 - mrk
3
@saadtaame:我只是指出这是基于C标准的未定义行为。这并不意味着它不能在某些系统中被定义 - 编译器和运行时可以为标准C中的某些东西定义特定的行为。因此,这里没有矛盾 :-)。 - sleske
8
这并不一定完全是指针的大小。标准只保证将 void* 指针值转换为 uintptr_t,再转回 void* 值后与原始指针相等。uintptr_t 的大小通常与 void* 相同,但不能保证,也没有任何保证转换后的值的位具有特定含义。并且不能保证它可以保存转换后的函数指针值而不会丢失信息。最后,也不能保证其存在。 - Keith Thompson
一个用途是异或链表,例如。 - WENDYN

20

已经有许多很好的回答了 "uintptr_t是什么数据类型?",在这篇文章中,我将尝试回答它的用途。

主要用于指针的按位操作。请记住,在C++中,不能对指针执行按位操作。原因请参见为什么不能在C语言中对指针执行按位操作,是否有解决方法?

因此,为了对指针执行按位操作,需要将指针强制转换为uintptr_t类型,然后执行按位操作。

以下是一个我刚刚编写的函数示例,用于对两个指针进行按位异或运算,以存储在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
除了位运算之外,如果您想基于地址而不是对象计数拥有语义,这也很好。 - Alexander Oh
哈哈,Xor 链表也是我能找到这个原因的原因。 - Bingoabs

11
冒着再获得另一个死灵法师徽章的风险,我想补充一个非常好的用途,即编写可测试的嵌入式代码。 我主要编写针对各种 ARM 和目前的 Tensilica 处理器的嵌入式代码。 这些具有各种本地总线宽度,而 Tensilica 实际上是一种哈佛结构,具有单独的代码和数据总线,可以具有不同的宽度。 我对我的大部分代码使用面向测试驱动开发的样式,这意味着我为我编写的所有代码单元进行单元测试。 在实际目标硬件上进行单元测试很麻烦,因此我通常在基于 Intel 的 PC 上使用 Ceedling 和 GCC 在 Windows 或 Linux 中编写所有内容。 话虽如此,大量嵌入式代码涉及位扭曲和地址操作。 我的大多数 Intel 机器都是 64 位的。因此,如果您要测试地址操作代码,则需要一个通用对象来执行数学运算。因此,uintptr_t 为您提供了一种在尝试部署到目标硬件之前调试代码的机器无关方式。 另一个问题是对于某些机器甚至某些编译器上的内存模型,函数指针和数据指针具有不同的宽度。在这些机器上,编译器甚至可能不允许在两个类之间进行强制转换,但是 uintptr_t 应该能够容纳任何一个。

不确定是否相关...您尝试过“不透明的typedef”吗? 视频链接:https://www.youtube.com/watch?v=jLdSjh8oqmE - shycha
@sleske 我希望 C 也有这个功能。但是有 stdint.h 比没有好。(同时我也希望枚举类型更强类型化,但大多数调试器都能很好地解决它们) - bd2357
不完全正确。uintptr_t适用于将void *转换为对象指针,以及将对象指针转换为void *。函数指针可能无法与void *往返(例如,函数指针比void *大),因此也可能无法适应uintptr_t - chux - Reinstate Monica
@chux,是的,在某些有限的情况下,这是不可移植的。我会添加一个注释。自从我看过intel-16bit的东西以来已经过了很多年,但是段会让事情变得混乱。 - bd2357

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接