什么时候uintptr_t优于intptr_t?

Joh*_*ell 17 c pointers void-pointers

鉴于我需要在结构中存储"泛型"指针的值并且对指向的内存本身没有兴趣,我发现将它存储为intptr_t比a 更为语义正确void*.问题是a uintptr_t是否更适合,并且当一个人更喜欢另一个人时?

unw*_*ind 10

这听起来很奇怪,因为它需要演员阵容.void *C中的A 具有巨大的优势,它可以在没有强制转换的情况下转换为其他对象指针类型/从其他对象指针类型进行转换

uintptr_t如果你想对指针的某些部分做一些事情,那么这可能是有意义的,你无法通过有符号整数(例如将它们向右移动)来做.

  • @JohannGerell当然,很难确定.但是因为'void*'的全部意义在于它不能被解除引用,因为没有关于指针的类型信息,我会说它正是它已经传达的内容. (2认同)

Bas*_*tch 9

它主要是一种风格论证(优化编译器可能生成相同或非常相似的代码).但是,指针比较可能是一个棘手的问题.

记住,比纯粹的标准C指针比较仅对指向相同聚合数据的指针大致有意义.您可能不允许比较两个结果malloc,例如保留已排序的指针数组.

我会保留它们void*,或者作为uintptr_t.签名intptr_t有分隔负数和正数的不便,以及它们来自重要的应用程序指针,这可能不受欢迎.

请注意,a void*不能被解除引用:作为一个uintptr_t,您必须将其强制转换为对地址指向的数据执行有用的操作; 但是void*指针可以传递给例程memset

PS.我假设一个普通的处理器(例如某些x86,PowerPC,ARM,......)具有平坦的虚拟地址空间.您可能会发现异国情调的处理器 - 可能是某些DSP - 具有非常显着的差异(并且可能intptr_t并不总是有意义的;请记住在20世纪90年代的Cray Y-MP超级计算机sizeof(long*) != sizeof(char*);当时C99不存在,我不确定它<stdint.h>是否可以在这样的机器上有意义)

  • 在T90上,整数和指针转换只是复制了这些位.如果`char*`指针具有非零偏移量,将其转换为`long*`将产生无效指针,因为3个高位位将成为地址的一部分(并且实际地址空间不是那么大).我曾经看到一些代码通过首先将它们转换为整数来计算两个指针之间的差异; 我指出直接指针减法更简单,具有实际工作的优点. (2认同)

Lun*_*din 5

您应该选择适合给定系统和程序的类型。大多数时候,指针是正地址值,在这种情况下uintptr_t是正确的类型。但是有些系统使用负地址作为表达内核空间的一种方式,正如这里所解释的:指针(地址)可以为负吗?这就是为什么有两种不同类型的原因。


对于通用指针类型的(u)intptr_tvs void*,前者在坚固、专业的程序中更受欢迎。有许多与指针类型相关的问题/错误来源:

  • 各种不同的指针类型通常彼此不兼容,并且不能使用别名。这是对象指针和函数指针的问题。
  • 你经常有像 那样的类型限定符const,这使得与该类型之间的指针转换有问题或定义不明确。
  • void*与其他指针类型之间的转换是隐式发生的,这使得与使用错误指针类型相关的错误很容易被忽视。这在 C++ 中已修复,但在 C 中仍然存在危险。以旧但经典的“我在 C90 中使用 malloc 时忘记包含 stdlib.h”错误为例。
  • 对指针执行算术运算会带来许多陷阱,因为您只能安全地对指向已分配数组的指针执行算术运算。然而,正如任何使用嵌入式系统的人都知道的那样,除了指向数组之外,通常还有许多其他原因可以拥有内存地址。
  • 您甚至无法void*. 这样做依赖于非标准编译器扩展。

话虽如此,很多遗留代码都依赖于void指针,在受限上下文中使用它们是完全没问题的。一些示例是依赖通用回调函数的规范代码:bsearchqsort、 pthreads 等。

然而,我不建议void在设计新的 C 程序时使用指针——在我看来,它们最好被视为过去的一个危险特性。现在存在更好和更安全的通用 C 编程方法,例如 C11 _Generic,使用指定初始值设定项的技巧,将参数作为数组指针传递(到 VLA),在编译时检查边界static_assert等。一些例子可以在我的回答中找到:如何创建类型安全的枚举?.