c - C11 中是否有定义的方法来进行指针减法?

标签 c pointers undefined-behavior c11 pointer-arithmetic

是否有一种方法可以在 C11 中将一个指针与另一个指针相减,并始终定义结果?

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

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

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

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

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

最佳答案

根据标准

只有当两个指针都指向同一个对象(其中包括“最后一个”指针)时,才可以对指针进行减法。

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

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

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

因此,不幸的是,根据标准,没有办法可移植地做到这一点。

记住: size_t 是对象的最大大小。它不是您的地址空间的大小。 size_t 的范围小于 uintptr_t 及其 friend 是完全合法的。与 ptrdiff_t 相同:ptrdiff_t 的范围小于 uintptr_t 是完全合法的。例如,想象一下分段内存模型,您无法分配大于段的任何内容,在这种情况下,size_t 和 ptrdiff_t 可能能够表示段的大小但不是您的地址空间的大小。

根据实践

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

不要在不进行强制转换的情况下减去原始指针,除非它们指向同一个对象,或者指向该对象之后的地址。当您使用指针算术时,编译器可以并且将会做出别名假设。不仅您的程序在“技术上”是错误的,而且编译器在此处生成错误代码的历史也很长。

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

关于c - C11 中是否有定义的方法来进行指针减法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38442017/

相关文章:

c++ - 转换为不相关的引用类型是否违反了严格的别名规则?

c++ - 检测未定义行为的 C++ 实现?

c - 我可以打印出我读到的字符串值,但在转换为使用其 ASCII 等效项时遇到问题

ios - 如何将原始类型的 3D 数组包装在 NSArray 中?

c - "Assignment to expression with array type"错误

c++ - 为什么我的链表分配不能正常工作?

c++ - 定义未定义的行为

c - 使用异步套接字让服务器处理多个客户端

c - C中数组列表到链表节点

c++ - 如何用 C++ 中的引用替换指针?