繁体   English   中英

为什么是 sizeof(ptrdiff_t) == sizeof(uintptr_t)

[英]why is sizeof(ptrdiff_t) == sizeof(uintptr_t)

我看到几篇关于 size_t 与 uintptr_t/ptrdiff_t 的帖子(例如size_t 与 uintptr_t ),但没有关于这些新的 c99 ptr 大小类型的相对大小。

示例机器:vanilla ubuntu 14lts x64,gcc 4.8:

printf("%zu, %zu, %zu\n", sizeof(uintptr_t), sizeof(intptr_t), sizeof(ptrdiff_t));

打印:“8, 8, 8”

这对我来说没有意义,因为我希望必须有符号的 diff 类型需要比无符号 ptr 本身更多的位。

考虑:

NULL - (2^64-1)  /*largest ptr, 64bits of 1's.*/

2 的负补数不适合 64 位; 因此我希望 ptrdiff_t 比 ptr_t 大。

[一个相关的问题是为什么 intptr_t 与 uintptr_t 的大小相同......虽然我很舒服这可能只是为了允许有符号类型包含表示的位(例如,在负 ptr 上使用有符号算术将(a)未定义,并且 (b) 效用有限,因为 ptr 根据定义为“正”)]

谢谢!

首先,很明显uintptr_t在这里做什么。 语言(C 和 C++)不允许您从彼此中减去任何任意指针值。 两个指针只有在指向同一个对象(同一个数组对象)时才能相减。 否则,行为是未定义的。 这意味着这两个指针的距离不可能超过SIZE_MAX字节。 注意:距离受size_t的范围限制,而不是受uintptr_t的范围限制。 通常情况下uintptr_t可以是比size_t更大的类型。 C/C++ 中没有人向你保证你应该能够减去位于UINTPTR_MAX字节的两个指针。

(是的,我知道在平面内存平台上uintptr_tsize_t通常是相同的类型,至少在范围和表示上是这样。但从语言的角度来看,假设它们总是是不正确的。)

您的NULL - (2^64-1) (如果解释为地址减法)就是这种可疑减法的一个明显例子。 是什么让您认为您应该能够做到这一点?

其次,在从不相关的uintptr_t切换到更相关的size_t ,可以说您的逻辑是完全有效的。 sizeof(ptrdiff_t)应该大于sizeof(size_t)因为需要额外的位来表示有符号的结果。 然而,无论听起来多么奇怪,语言规范并不要求ptrdiff_t足够宽以容纳所有指针减法结果,即使两个指针指向同一对象的部分(即它们相隔不超过SIZE_MAX字节)。 ptrdiff_t在法律上被允许与size_t具有相同的位数。

这意味着“看似有效”的指针减法实际上可能仅仅因为结果太大而导致未定义的行为。 如果您的实现允许您声明大小的char数组,例如SIZE_MAX / 3 * 2

char array[SIZE_MAX / 3 * 2]; // This is smaller than `SIZE_MAX`

如果ptrdiff_tsize_t具有相同的大小,则减去指向该数组末尾和开头的完全有效的指针可能会导致未定义的行为

char *b = array;
char *e = array + sizeof array;

ptrdiff_t distance = e - b; // Undefined behavior!

这些语言的作者决定选择这种更简单的解决方案,而不是要求编译器实现对 [可能是非本地] 超宽有符号整数类型ptrdiff_t

现实生活中的实现意识到了这个潜在的问题,并且通常会采取措施来避免它。 他们人为地限制了支持的最大对象的大小,以确保指针减法永远不会溢出。 在典型的实现中,您将无法声明大于PTRDIFF_MAX字节(大约SIZE_MAX / 2 )的数组。 例如,即使您平台上的SIZE_MAX是 2 64 -1,该实现也不会让您声明任何大于 2 63 -1 字节的内容(并且来自其他因素的实际限制可能比这更严格)。 有了这个限制,任何合法的指针减法都会产生一个适合ptrdiff_t范围的ptrdiff_t

也可以看看,

接受的答案并没有错,但并没有提供很多关于为什么 intptr_t、size_t 和 ptrdiff_t 实际上有用以及如何使用它们的见解。 所以这里是:

  • size_t基本上是size_of表达式的类型。 只需要能够容纳您可以创建的最大对象的大小,包括数组。 因此,如果您只能使用 64k 连续内存,那么即使您有 64 位指针,size_t 也可以小至 16 位。

  • ptrdiff_t是指针差异的类型,例如&a - &b 虽然0 - &a确实是未定义的行为(就像在 C/C++ 中做几乎所有事情一样),但无论它是什么,都必须适合ptrdiff_t 它通常与指针的大小相同,因为这是最有意义的。 如果ptrdiff_t是一个奇怪的大小,指针算术本身就会中断。

  • intptr_t / uintptr_t具有与指针相同的大小。 它们适合相同的int*_t模式,其中 * 是 int 的大小。 与所有int*_t / uint*_t类型一样,标准出于某种原因允许它们比所需的更大,但这种情况非常罕见。

根据经验,您可以将size_t用于大小和数组索引,并将intptr_t / uintptr_t用于与指针相关的所有内容。 不要使用ptrdiff_t

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM