ptrdiff_t的动机是什么?

What is the motivation for ptrdiff_t?

为什么C要为两个指针的区别定义一个单独的类型?例如,为什么不能将其定义为 long long,甚至不能定义为 intmax_t?有没有 intmax_t 不等于 ptrdiff_t 的时候?

基本上,这个 typedef(具有特定的字符串格式等)有用的含义是什么?作为一个基本的使用示例,我正在做:

int a = 1, b = 2;
printf("%td XXX\n", (char *)(&b) - (char *)(&a)); // 't' is formatter for prtdiff_t

Is there ever a time when intmax_t does not equal ptrdiff_t ?

是,在指针为 32 位的 32 位平台上,intmax_t 为 >=64 位。

这是 C 标准中的相关段落:

6.5.6 Additive operators
[...]
Semantics
[...]
9. When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i - j provided the value fits in an object of type ptrdiff_t. Moreover, if the expression P points either to an element of an array object or one past the last element of an array object, and the expression Q points to the last element of the same array object, the expression ((Q)+1)-(P) has the same value as ((Q)-(P))+1 and as -((P)-((Q)+1)), and has the value zero if the expression P points one past the last element of the array object, even though the expression (Q)+1 does not point to an element of the array object.108)


108) 指针运算的另一种方法是首先将指针转换为字符指针:在该方案中,首先对转换后的指针添加或减去整数表达式乘以最初指向的对象的大小,并将结果指针转换回原始类型。对于指针减法,字符指针之间的差值的结果类似地除以最初指向的对象的大小。 这样看来,一个实现只需要在对象结束后提供一个额外的字节(它可能与程序中的另一个对象重叠),以满足“最后一个元素之后的一个”要求。

虽然 C 标准没有强制要求,但 ptrdiff_t 通常被定义为大小与 size_t 相同的有符号整数类型。在对象的最大大小限制为 32 位的体系结构上,例如 32 位 Windows 和 Unix 系统,size_t 是无符号 32 位整数类型,而 ptrdiff_t 是有符号的32位整数类型,而在地址更大的64位系统上space,它们都是64位整数类型,有符号为ptrdiff_t,无符号​​为size_t.

但是请注意,char 非常大的数组中的 2 个指针的差异可能会超出与 size_t 大小相同的带符号类型的范围。例如一个3GB的char数组在32位系统上可能是可用的(UINT32_MAX > 3GB)但是2个指针的绝对值相差可以达到3GB,超出了int32_t的范围,因此 fit 在此平台的 ptrdiff_t 类型中调用未定义的行为。

大多数实现通过减去地址并将结果除以元素大小来获得差异,如果大小是 2 的幂,则使用带符号的算术或带符号的右移。这可能会为大型元素数组产生不正确的结果如果该值适合 ptrdiff_t.

类型的对象,则大于 char even

使用实现定义类型 ptrdiff_t 而不是 long long 的基本原理是保持与现有实现的兼容性并避免在地址较小的系统上使用较大的类型 space,其中这会在 space 和时间上造成额外的开销。在早期的 C 实现中,类型 int 用于此目的,当 size_t 被引入的范围可能与 unsigned int 不同时,需要一个特定类型来区分 2 个指针,因为int 太小了。 ptrdiff_t 可能仍然太小,但仅适用于边界情况。