是否有一种方法可以在 C11 中将一个指针与另一个指针相减,并始终定义结果?
标准规定,如果结果不能表示为 ptrdiff_t 类型,则行为未定义。
我对依赖静态断言的解决方案持开放态度,该解决方案有望在现代通用 32 或 64 位环境中传递合理的实现。我想避免依赖任何类型的运行时检查的解决方案。
如果指向的类型的大小大于 1,我可以静态断言 size_t 和 ptrdiff_t 具有相同数量的非填充位。这个部分解决方案依赖于我不确定的两件事,因此任何对此的反馈都将提供部分答案:
在现代通用 32 或 64 位环境中的合理实现中,可以预期 ptrdiff_t 至多比 size_t 少一位值位。
我对标准的理解是正确的,因为两个指向大小大于 1 的对象的指针之间的差异是定义的,即使如果指针转换为字符指针,相同的差异将是未定义的。这种理解似乎与委员会草案中的脚注106不一致,但我的理解是脚注不规范。
最佳答案
根据标准
只有当两个指针都指向同一个对象(其中包括“最后一个”指针)时,才可以对指针进行减法。
减去uintptr_t
或intptr_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/