鉴于要求我需要在结构中存储“泛型”指针的值并且对指向的内存本身没有兴趣,我发现将它存储为intptr_t
在语义上更正确而不是void*
。问题是uintptr_t
是否更适合,一般情况下是否优先于另一种?
答案 0 :(得分:8)
它主要是一种风格论证(优化编译器可能生成相同或非常相似的代码)。但是,指针比较可能是一个棘手的问题。
请记住,与纯粹的标准C指针相比,仅对指向相同聚合数据的指针大致有意义。您可能不允许比较malloc
的两个结果,例如保留一个排序的指针数组。
我会将它们保留为void*
,或者保留为uintptr_t
。签名intptr_t
有不方便分隔负数和正数,以及它们来自重要的应用程序指针,这可能不受欢迎。
请注意,void*
无法取消引用:作为uintptr_t
,您拥有来投射它以对地址指向的数据执行有用的操作;但void*
指针可以传递给memset
PS。我假设一个普通的处理器(例如某些x86,PowerPC,ARM,......)具有平坦的虚拟地址空间。您可能会发现异乎寻常的处理器 - 可能是某些DSP - 具有非常显着的差异(并且intptr_t
可能并不总是有意义的;请记住在20世纪90年代Cray Y-MP超级计算机sizeof(long*) != sizeof(char*)
;当时{ {3}}不存在,我不确定<stdint.h>
在这些机器上是否有意义。
答案 1 :(得分:3)
您应该选择适合给定系统和程序的类型。在大多数情况下,指针是正地址值,在这种情况下,uintptr_t
是正确的类型。但是某些系统使用负地址来表示内核空间,如此处所述:Can a pointer (address) ever be negative?这就是为什么存在两种不同类型的原因。
对于(u)intptr_t
与void*
(对于通用指针类型)而言,前者在坚固耐用的专业程序中是首选。与指针类型相关的问题/错误来源很多:
const
,这会使往/从该类型的指针转换产生疑问或定义不明确。void*
和其他指针类型之间的转换是隐式发生的,这使得与使用错误的指针类型有关的错误很容易被忽视。该问题在C ++中已修复,但在C语言中仍然存在危险。例如,古老而经典的“我忘记了在C90中使用malloc时包含stdlib.h”错误。void*
上执行指针算术运算。这样做依赖于非标准的编译器扩展。话虽这么说,很多遗留代码都依赖于void
指针,并且在受限的上下文中使用它们是完全可以的。一些示例是依赖于通用回调函数的规范代码:bsearch
,qsort
,pthread和类似的东西。
但是,在设计新的C程序时,我不建议使用void
指针-在我看来,最好将它们视为过去的危险特性。如今,存在更好,更安全的通用C编程方法,例如C11 _Generic
,使用指定的初始化程序的技巧,将参数作为数组指针传递给VLA,在编译时使用static_assert
进行边界检查等。可以在我的答案中找到一些示例:How to create type safe enums?。
答案 2 :(得分:2)
如果你想算术地操纵值(例如,加密它),你可以更灵活地使用无符号类型(算术环绕)而不是带符号类型(算术溢出会给出未定义的行为)。