【问题标题】:Equality comparison of pointers to different objects指向不同对象的指针的相等比较
【发布时间】:2017-04-10 20:01:31
【问题描述】:

answering this question 的启发,我对指针上使用相等运算符的 C11 和 C99 标准进行了一些研究(最初的问题涉及关系运算符)。以下是 C11 在 §6.5.9.6 中所说的(C99 类似):

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

脚注 94 说(并注意脚注是非规范性的):

两个对象在内存中可能是相邻的,因为它们是较大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择将它们如此放置,即使它们不相关。如果先前的无效指针操作(例如访问数组边界外)产生未定义的行为,则后续比较也会产生未定义的行为。

正文和非规范性注释似乎有冲突。如果一个人认真对待正文中的'当且仅当',那么除了所列出的情况之外,没有其他情况应该返回相等,并且没有UB的空间。所以,例如这段代码:

uintptr_t a = 1;
uintptr_t b = 1;
void *ap = (void *)a;
void *bp = (void *)b;
printf ("%d\n", ap <= bp); /* UB by §6.5.8.5 */
printf ("%d\n", ap < bp);  /* UB by §6.5.8.5 */
printf ("%d\n", ap == bp); /* false by §6.5.9.6 ?? */

应该打印零,因为apbp 既不是指向同一个对象或函数的指针,也不是任何其他列出的位。

在 §6.5.8.5(关系运算符)中,行为更加清晰(我的重点):

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

问题:

  • 关于何时允许使用带指针的相等运算符存在一些歧义,我是正确的(比较脚注和正文)?

  • 如果没有歧义,指针与相等运算符的比较究竟何时可以是UB?例如,如果至少一个指针是人为创建的(如上所述),它总是 UB 吗?如果一个指针指向的是free()d 的内存怎么办?鉴于脚注是非规范性的,是否可以得出结论永远不存在 UB,因为所有“其他”比较都必须产生 false

  • 第 6.5.9.6 节真的意味着无意义但按位相等的指针的相等比较应该始终为假吗?

注意这个问题被标记为;我不是在问编译器在实践中做什么,因为我相信已经知道答案(使用与比较整数相同的技术来比较它们)。

【问题讨论】:

  • 您可能是正确的,存在歧义。您引用的第一件事没有说明 valid 指针比较,尽管这可能是意图。好问题。
  • 请记住,ISO 标准中的脚注不是规范性的——它们提供信息,但不定义语言。如有疑问,非脚注材料规则。这对你来说是纯粹的信息;在任何意义上都不是您问题的答案。
  • @PaulGriffiths 但没有什么规范可以说它是 UB!它说'两个指针比较相等当且仅当[条件]'(这意味着它们相等或不相等),然后§6.5.9.3描述了相等性如何反映在运算符的行为中.使 §6.5.8.5 UB 落在 '[conditions]' 之外的东西就像两个不相等的有效指针落在 '[conditions]' 之外一样,因此,除非比较任何两个不相等的指针是 UB(不可能是真的!),否则所有行为都已定义。
  • @Oktalist 和标准的哪个部分进行比较 UB?我在争论 is 定义的行为。
  • @PaulGriffiths 这不可能。您当然可以将两个指针(例如)从malloc()mmap() 返回与== 进行比较,即使将它们与&lt; 进行比较是UB。

标签: language-lawyer c language-lawyer


【解决方案1】:

关于带指针的相等运算符何时为 UB 存在一些歧义,我是否正确?

不,因为 §6.5.9(3) 中的这段话:

==!= 运算符类似于关系运算符,只是它们的优先级较低。

暗示 §6.5.9(6) 中的以下内容也适用于相等运算符:

当比较两个指针时 [...] 在所有其他情况下,行为是未定义的。

如果没有歧义,指针与相等运算符的比较究竟什么时候可以是UB?

在标准未明确定义行为的所有情况下都存在未定义的行为。

如果至少有一个指针是人工创建的从任意整数转换而来,是否总是UB?

§6.3.2.3(5):

整数可以转换为任何指针类型。除非前面指定,否则结果是实现定义的,可能未正确对齐,可能不指向引用类型的实体,并且可能是陷阱表示。

如果一个指针指向freed 的内存怎么办?

§6.2.4(2):

当指针指向的对象到达其生命周期结束时,指针的值变得不确定。

是否可以断定永远不存在 UB,即所有“其他”比较都必须产生错误?

没有。该标准定义了在什么条件下两个指针必须比较相等,以及在什么条件下两个指针必须比较不相等。在这两组条件之外的两个指针之间的任何相等比较都会调用未定义的行为。

第 6.5.9(6) 条是否真的意味着无意义但按位相等的指针的相等比较应该始终为假?

不,它是未定义的。

【讨论】:

  • 我不是反对者,但您的回答未能解决 6.5.9/6 的问题。该问题的关键点之一是 6.5.9/6 是否应该将相等比较的适用性扩展到关系比较所允许的范围之外。您似乎声称 6.5.9/3 胜过 6.5.9/6。这没有说服力。
  • 没有投反对票,但 ==!= 属于第 6.5.9(6) 条中的比较运算符最不令人信服。 §6.5.8(5)(当比较两个指针时......)在关系运算符下(不包括 == 和!=),与我类似并不意味着应该适用 §6.5.8(5),如果不相关对象的相等比较是UB,那么比较fn指针之类的东西就是UB,这似乎是有问题的。
  • 标准的作者认识到实现的可能性是符合标准的,但质量如此低以至于基本上没有用处。我个人不会将标准解读为给予一致但无用的实现的全面许可,以便在不相关的指针之间进行相等比较,而不是执行 yield 0 或 1,但可以接受尊敬的人可能会以不同的方式阅读它。另一方面,我不明白为什么那些不想破坏语言的人应该尝试以此为借口进行“优化”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-15
  • 2011-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-18
相关资源
最近更新 更多