在 C11 中是否有定义的指针减法方法?

Is there a defined way to do pointer subtraction in C11?

有没有办法在 C11 中用一个指针减去另一个指针并始终定义结果?

标准表示,如果结果不能表示为 ptrdiff_t 类型,则行为未定义。

我对依赖静态断言的解决方案持开放态度,该解决方案有望在现代通用 32 位或 64 位环境中传递合理的实现。我想避免依赖任何类型的运行时检查的解决方案。

如果指向的类型的大小大于 1,我可以静态断言 size_t 和 ptrdiff_t 具有相同数量的非填充位。这个部分解决方案依赖于我不确定的两件事,因此对此的任何反馈都会提供部分答案:

  1. 在现代通用 32 位或 64 位环境中的合理实现中,可以预期 ptrdiff_t 最多比 size_t 少一个值位。

  2. 我对标准的理解是正确的,因为定义了两个指向大小大于 1 的对象的指针之间的差异,即使如果指针被强制转换,相同的差异将是未定义的到字符指针。这种理解似乎与委员会草案中的脚注106不一致,但我的理解是脚注不规范。

根据标准

只有当两个指针都指向同一个对象时才能减去指针,其中包括 "one-past-the-end" 指针。

减去 uintptr_tintptr_t 不一定有意义,因为,同样,根据标准,没有特定的方法来定义从指针到整数的转换。特别是,

  • 考虑分段内存模型中的远指针,其中可能有不止一种方式来表示给定地址(段 + 偏移量,例如,在 x86 上)。

  • 考虑带有被处理器忽略的位的指针。 (例如,Motorola 68000 处理器,它有 32 位指针,但高 8 位被忽略。)

因此,很遗憾,根据标准,没有办法以可移植的方式执行此操作。

记住: size_t 是对象的最大尺寸。它不是您地址的大小space。 size_t 的范围小于 uintptr_t 和朋友的范围是完全合法的。与 ptrdiff_t 相同:ptrdiff_t 的范围小于 uintptr_t 是完全合法的。想象一下,例如,一个分段内存模型,您不能分配任何大于段的东西,在这种情况下,size_tptrdiff_t 可能能够表示段的大小,但不能表示您的段的大小地址 space.

根据惯例

在您使用的计算机(现代 32 位和 64 位计算机)上,uintptr_t 将只包含指针地址。减去。这是实现定义的但不是未定义的行为。

不要在没有转换的情况下减去原始指针,除非它们指向同一个对象,或者指向该对象之后的地址。当您使用指针算法时,编译器可以并且将会做出别名假设。不仅您的程序 "technically" 错误,而且编译器在这里产生错误代码的历史由来已久。

关于指向同一个对象的指针到底意味着什么,现在有一些争论,但我上次检查时这个争论还没有解决。