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

时间:2016-07-18 16:44:29

标签: 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不一致,但我的理解是脚注不是规范性的。

1 个答案:

答案 0 :(得分:5)

根据标准

如果两个指针都指向同一个对象,那么你只能减去指针,其中包括“一个接一个”的指针。

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

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

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

所以,不幸的是,根据标准,没有办法做到这一点。

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

根据实践

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

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

现在有一个争论正在进行,关于指向同一个对象的指针究竟是什么意思,但是这个参数在我上次检查时没有得到解决。