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

时间:2015-06-05 23:51:06

标签: c++ c pointers c99 computer-architecture

我看到几篇关于size_t与uintptr_t / ptrdiff_t的帖子(如size_t vs. 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类型需要比unsigned ptr本身更多的位。

考虑:

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

这是2的补码负值不适合64位;因此我希望ptrdiff_t大于ptr_t。

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

谢谢!

2 个答案:

答案 0 :(得分:16)

首先,很清楚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足够宽以容纳所有指针减法结果,即使两个指针指向同一对象的部分(即它们不比{{1更远)除了字节之外)。法律允许SIZE_MAX具有与ptrdiff_t相同的位数。

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

SIZE_MAX / 3 * 2
如果char array[SIZE_MAX / 3 * 2]; // This is smaller than `SIZE_MAX` ptrdiff_t具有相同的大小,

然后减去指向该数组的结尾和开头的完美有效指针可能会导致未定义的行为

size_t

这些语言的作者决定选择这种更简单的解决方案,而不是要求编译器实现对[可能非本地]超宽签名整数类型char *b = array; char *e = array + sizeof array; ptrdiff_t distance = e - b; // Undefined behavior! 的支持。

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

另见,

答案 1 :(得分:-1)

接受的答案并没有错,但并没有提供很多关于为什么 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