why is sizeof(ptrdiff_t) == sizeof(uintptr_t)

why is sizeof(ptrdiff_t) == sizeof(uintptr_t)

本文关键字:sizeof uintptr ptrdiff is why      更新时间:2023-10-16

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

示例机:香草ubuntu 14lts x64, GCC 4.8

printf("%zu, %zu, %zun", 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的大小与intptr_t相同....虽然我很舒服,但这可能只是为了允许有符号类型包含表示的位(例如,在负PTR上使用有符号算术将(a)未定义,并且(b)有限的效用,因为PTRS根据定义是"正")]

谢谢!

首先,很明显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_tsize_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!

这些语言的作者决定选择这个更简单的解决方案,而不是要求编译器实现对[可能是非本地的]extra - wide signed integer类型ptrdiff_t的支持。

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

也看到,

  • 为什么数组的最大大小"太大"?

公认的答案是正确的,但是并没有深入了解为什么intptr_t、size_t和ptrdiff_t实际上是有用的,以及如何使用它们。这里是:

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

  • 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