【问题标题】:What is the motivation for ptrdiff_t?ptrdiff_t 的动机是什么?
【发布时间】:2021-04-21 04:39:58
【问题描述】:

为什么 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

【问题讨论】:

  • 出自标准:当两个指针相减时,都指向同一个数组对象的元素,或者指向数组对象最后一个元素的元素;结果是两个数组元素的下标之差。 结果的大小是实现定义的,其类型(有符号整数类型)是在 头文件中定义的 ptrdiff_t
  • @DavidRanieri 所以ptrdiff_t 的重点是基本上表示数组索引偏移量?例如“1 个元素”、“2 个元素”等?
  • 是的,换句话说:sizeof(a - b) == sizeof(ptrdiff_t)
  • 在世界上几乎所有 PC 计算机(64 位操作系统)上,intmax_t 都小于 ptrdiff_t
  • @0___________ 谁说的?在几乎所有 64 位平台中,intmax_tptrdiff_t 的类型相同

标签: c language-lawyer pointer-arithmetic ptrdiff-t


【解决方案1】:

是否有过intmax_t 不等于ptrdiff_t 的时候?

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

【讨论】:

  • 这样的平台有什么例子?
  • @samuelbrody1249 一些示例是答案中提到的 32 位平台。无论如何,ptrdiff_t 早在 C99 引入 intmax_t 之前就已经存在了
  • @samuelbrody1249 任何 32 位 Windows/Linux/...?
  • @DavidRanieri 刚刚从 MSYS2 下载了 x32 MinGW 来测试它。 ptrdiff_t 是 32 位,intmax_t 是 64 位。这是有道理的,因为long long 至少需要 64 位。
  • @HolyBlackCat 是的,抱歉,我看错了答案,我总是忘记long longint 类型;) 你下载MinGW 只是为了测试它吗?我很抱歉
【解决方案2】:

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

6.5.6 加法运算符
[...]
语义
[...]
9. 两个指针相减时,都指向同一个数组对象的元素,或者指向数组对象的最后一个元素;结果是两个数组元素的下标之差。结果的大小是实现定义的,其类型(有符号整数类型)是ptrdiff_t 定义在<stddef.h> 标头中。如果结果在该类型的对象中不可表示,则行为未定义。换句话说,如果表达式PQ 分别指向数组对象的i-th 和j-th 元素,则表达式(P)-(Q) 的值是i - j,前提是value 适合 ptrdiff_t 类型的对象。此外,如果表达式P 指向数组对象的一个​​元素或数组对象的最后一个元素,并且表达式Q 指向同一数组对象的最后一个元素,则表达式((Q)+1)-(P)具有与((Q)-(P))+1-((P)-((Q)+1)) 相同的值,并且如果表达式P 指向数组对象的最后一个元素后一个,则值为零,即使表达式(Q)+1 不指向数组对象。108)


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

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

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

大多数实现通过减去地址并将结果除以元素大小来获得差异,如果大小是 2 的幂,则使用有符号算术或有符号右移。对于大于char even 如果值适合 ptrdiff_t 类型的对象。

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

【讨论】:

    猜你喜欢
    • 2012-05-11
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 2010-10-10
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    相关资源
    最近更新 更多