ptrdiff_t太小了?

我一直想知道: ptrdiff_t是不是应该能够按照定义保持任意两个指针的差异? 当两个指针太远时怎么会失败呢? (我没有指出任何特定语言……我指的是所有具有此类型的语言。)

(例如,当你有32位指针时,用地址0xFFFFFFFF从字节指针中减去带地址1的指针,它会溢出符号位…)

不它不是。

$ 5.7 [expr.add] (来自n3225 – C ++ 0x FCD)
当减去指向同一数组对象的元素的两个指针时,结果是两个数组元素的下标的差异。 结果的类型是实现定义的有符号整数类型; 此类型应与头文件(18.2)中定义为std::ptrdiff_t类型相同。 与任何其他算术溢出一样,如果结果不适合所提供的空间,则行为未定义。 换句话说,如果表达式PQ分别指向数组对象的第i和第j个元素,则表达式(P)-(Q)具有值i − j条件是该值适合于std::ptrdiff_t类型的对象。 此外,如果表达式P指向数组对象的元素或者指向数组对象的最后一个元素,并且表达式Q指向同一数组对象的最后一个元素,则表达式((Q)+1)-(P)具有与((Q)-(P))+1-((P)-((Q)+1))的值,并且如果表达式P指向一个,则值为零即使表达式(Q)+1没有指向数组对象的元素,也是数组对象的最后一个元素。 除非两个指针指向同一个数组对象的元素,或者指向数组对象的最后一个元素,否则行为是未定义的。

请注意段落中出现undefined的次数。 另请注意,如果指针指向同一对象,则只能减去指针。

不,因为没有“任何两个指针”之间的区别。 您只能减去指向同一数组元素的指针(或指向刚好超过数组末尾的位置的指针)。

要添加更明确的标准引用, ISO 9899:1999 §J.2/1规定:

在以下情况下,行为未定义:

[…]

– 减去两个指针的结果在ptrdiff_t(6.5.6)类型的对象中无法表示。

ptrdiff_t与指针类型的大小完全相同,只要溢出语义由编译器定义,这样任何差异仍然可以表示。 无法保证负ptrdiff_t意味着第二个指针位于内存中比第一个更低的地址,或者ptrdiff_t完全是签名的。

对于固定大小的整数运算,上/下溢在数学上是明确定义的:

 (1 - 0xFFFFFFFF) % (1<<32) = (1 + -0xFFFFFFFF) % (1<<32) = 1 + (-0xFFFFFFFF % (1<<32)) = 2 

正确的结果!

具体来说,上溢/下溢后的结果是正确整数的别名。 事实上,每个不可表示的整数都有一个可表示的整数别名(无法区分) - 在固定大小的整数中计数到无穷大,你会像模拟时钟的表盘一样重复自己,圆形和圆形。

N位整数表示模2 ^ N的任何实数。 在C中,模2 ^ N写为%(1 << 32)。

我相信C保证上溢/下溢的数学正确性,但仅适用于无符号整数。 假定签名在/溢出下永远不会发生(为了优化)。

在实践中,有符号整数是两个补码,这在加法或减法上没有区别,因此对于有符号整数也保证正确的欠/溢出行为(尽管不是由C)。