【问题标题】:Is comparing equality on two pointers that are not in the same array defined behaviour?是否比较不在同一数组定义的行为中的两个指针的相等性?
【发布时间】:2018-03-06 16:45:37
【问题描述】:

请考虑

int main(void)
{
    int a;
    int b;
    int* pa = &a;
    int* pb = &b;

    int e = pa == pb;
}

这是明确定义的吗? papb 没有指向同一个数组中的元素。

【问题讨论】:

  • 是的,已经定义好了。不确定它是否有用。
  • @DyZ: 恕我直言,只有一点点绒毛才能说服我 ;-)
  • 定义明确。永远不要怀疑它。对指针的关系操作必须在指向同一个对象的指针上。
  • 在这种情况下,a,b 都没有初始化也没有分配。它与您的调查问题有关吗? (IMO,它不会改变“是”的答案。)
  • @chux 不,不是。我想以尽可能少的方式呈现它,也许是为了让我看起来不像真正的江湖骗子;-)

标签: c language-lawyer


【解决方案1】:

这种行为是明确定义的。

C standard 第 6.5.9 节关于等式运算符(==!=)陈述如下:

2 应满足以下条件之一:

  • 两个操作数都有算术类型;
  • 两个操作数都是指向兼容类型的合格或不合格版本的指针
  • 一个操作数是指向对象类型的指针,另一个是指向限定或非限定版本的 void 的指针;或
  • 一个操作数是一个指针,另一个是一个空指针常量。

...

5 否则,至少有一个操作数是指针。如果一个操作数是指针,另一个是空指针常量,则空指针 常量被转换为指针的类型。如果一个操作数是 一个指向对象类型的指针,另一个是指向对象类型的指针 void 的合格或不合格版本,前者被转换 为后者的类型。

6 两个指针比较相等当且仅当两个指针都是空指针,都是指向同一个对象的指针(包括指向一个对象的指针) 和开头的子对象)或函数,两者都是指向的指针 一个超过同一数组对象的最后一个元素,或者一个是 指向一个数组对象末尾的指针,另一个是 指向恰好发生在的不同数组对象的开头的指针 紧跟地址中的第一个数组对象 空间。

根据第 2 段,它不需要两个指针指向同一个对象,只要求类型兼容或至少其中一个是 void *NULL。第 6 段随后说明了两个指针比较相等的含义。

这与第 6.5.8 节不同,后者涉及关系运算符(<><=>=),即使类型相同,也不允许在不相关的对象之间进行比较:

5 比较两个指针时,结果取决于所指向对象在地址空间中的相对位置。 如果两个指向对象类型的指针都指向同一个对象,或者 都指向同一数组的最后一个元素 对象,它们比较相等。如果指向的对象是 同一聚合对象的成员,指向结构的指针 稍后声明的成员比较大于指向的指针 在结构前面声明的成员,以及指向 具有较大下标值的数组元素比较大于 指向具有较低下标值的同一数组的元素的指针。
指向同一联合对象成员的所有指针比较 平等的。如果表达式 P 指向数组的元素 对象和表达式 Q 指向的最后一个元素 同一个数组对象,指针表达式Q+1比较大于P . 在所有其他情况下,行为未定义

在这里,它说明了如何比较指向同一数组对象内的两个指针,并且在所有其他情况下行为是未定义的。第 6.5.9 节没有有这样的措辞。

附带说明:

char a[5], b[5];

如果a 在内存中紧跟b,则6.5.9p6 可以允许a + 5 == b 评估为真。

【讨论】:

  • ..and that 我怀疑(你的最后陈述)。 所有 指针可以比较是否相等,与它们指向的类型无关。在引用的第三个项目符号中,两者都可以是空指针。
  • @PaulOgilvie 好点。添加注释,void *NULL 也允许比较。
【解决方案2】:

如果我可以引用 标准:§6.5.9¶2 关于== 的约束:

应满足以下条件之一:

  • 两个操作数都有算术类型;
  • 两个操作数都是指向兼容类型的合格或不合格版本的指针;
  • 一个操作数是指向对象类型的指针,另一个是指向限定或非限定版本的 void 的指针;或
  • 一个操作数是一个指针,另一个是一个空指针常量。

所以是的,按照这个 - 你的代码格式正确。这是定义明确的(为什么?因为 standard doesn't mention 以任何形式比较两个不相关的指针以获得 平等是未定义的行为(所有访问元素都不是。脚注109))。1

这是§6.5.9¶6中所说的

两个指针比较相等当且仅当两个指针都是空指针,都是指向同一个对象(包括一个指向一个对象和一个在其开头的子对象)或函数的指针,两者都是指向最后一个元素的指针相同的数组对象,或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象的开头的指针,该数组对象恰好紧跟在地址空间中的第一个数组对象之后

1) 链接部分( §6.5.9¶6 ) 是描述使用== 比较的两个指针何时相等的部分。根据法律,现在我们可以说所有其他情况都不是平等的。 没有对未明确定义的其他声明提出任何声明。


澄清一点 - 关系运算符的规则与相等运算符的规则完全不同。 §6.5.8¶5部分(关系运算符的语义)

当比较两个指针时,结果取决于所指向对象在地址空间中的相对位置。 .. 在所有其他情况下,行为未定义

清楚地提到了这一点-甚至可以比较-他们必须遵循上述标准。所以在两个不相关的指针上使用关系运算符会产生未定义的行为。


扩展讨论:

还有一件事需要澄清 - 约束语义 是完全不同的东西。在运营商标准的情况下 - 它首先提供必须保持的约束,但这只是硬币的一方面。语义呢?之后提到的标准。现在,当最初回答时,这个答案集中在过多的约束而不是语义上。为什么这很重要?当我们研究关系运算符时,我们就会知道这一点。检查它的约束 -

应满足以下条件之一:

  • 两个操作数都有实型;或
  • 两个操作数都是指向兼容对象类型的合格或不合格版本的指针。

仅通过查看此约束 - 人们可能会说ptr < NULL 定义明确。好吧,事实并非如此——从语义上看,特别是从§6.5.8¶5这一点开始就很清楚了。但是ptr < NULL 的格式肯定是正确的,因为它符合强加于它的约束条件。

【讨论】:

    猜你喜欢
    • 2011-06-22
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多