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

mik*_*ike 6 c c++ pointers c99 computer-architecture

我看到几个关于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));
Run Code Online (Sandbox Code Playgroud)

打印:"8,8,8"

这对我来说没有意义,因为我期望必须签名的diff类型需要比unsigned ptr本身更多的位.

考虑:

NULL - (2^64-1)  /*largest ptr, 64bits of 1's.*/
Run Code Online (Sandbox Code Playgroud)

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

[一个相关的问题是为什么intptr_t与uintptr_t的大小相同....虽然我很舒服这可能只是为了允许一个带符号的类型来包含表示的位(例如,在负ptr上使用带符号的算法将是(a)be undefined,和(b)实用性有限,因为ptrs根据定义是"正面的")]

谢谢!

AnT*_*AnT 16

首先,很明显uintptr_t这里没有做什么.语言(C和C++)不允许您相互减去任意指针值.如果指针指向同一个对象(进入同一个数组对象),则只能减去两个指针.否则,行为未定义.这意味着这两个指针不可能比SIZE_MAX字节分开更远.注意:距离受范围的限制size_t,而不受范围的限制uintptr_t.一般情况下uintptr_t可以是比较大的类型size_t.C/C++中没有人曾经答应过你应该能够减去两个指向UINTPTR_MAX字节的指针.

(是的,我知道在平面内存平台上uintptr_t并且size_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`
Run Code Online (Sandbox Code Playgroud)

然后减去完全有效的指向结尾和此数组开头的指针可能会导致未定义的行为,如果ptrdiff_t具有相同的大小size_t

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

ptrdiff_t distance = e - b; // Undefined behavior!
Run Code Online (Sandbox Code Playgroud)

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

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

也可以看看,

  • @Olaf:DOS,Win16,分段的IBM大型机......但它现在变成了什么完全无关紧要.我根本不关心它.只要语言规范保持差异化,它就会存在.这就是我们在语言中有单独的`size_t`和`uintptr_t`概念的原因.如果它不存在,就根本不需要`uintptr_t` - `size_t`可以很好地达到这个目的(并且已知很多人为了这个目的而滥用它). (4认同)
  • @Olaf:不,这没错.`unintptr_t`与平面内存平台上的`size_t`大小相同,这只是平板内存平台的一个巧合且无关紧要的特性.在分段内存平台上,`size_t`通常小于`uintptr_t`(分段内存平台通常支持不同的*内存模型*,这将决定`size_t`和`uintptr_t`的相对大小).一般情况下,从抽象语言的角度来看`sizeof(size_t)<= sizeof(uintptr_t)`. (3认同)