【发布时间】: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 ?? */
应该打印零,因为ap 和bp 既不是指向同一个对象或函数的指针,也不是任何其他列出的位。
在 §6.5.8.5(关系运算符)中,行为更加清晰(我的重点):
当比较两个指针时,结果取决于所指向对象在地址空间中的相对位置。如果两个指向对象或不完整类型的指针都指向同一个对象,或者都指向同一个数组对象的最后一个元素,它们比较相等。如果指向的对象是同一个聚合对象的成员,则指向稍后声明的结构成员的指针比较大于指向结构中较早声明的成员的指针,并且指向具有较大下标值的数组元素的指针比较大于指向同一数组的元素的指针具有较低的下标值。所有指向同一个联合对象成员的指针比较相等。如果表达式
P指向一个数组对象的元素,而表达式Q指向同一个数组对象的最后一个元素,则指针表达式Q+1比较大于P。 在所有其他情况下,行为未定义。
问题:
关于何时允许使用带指针的相等运算符存在一些歧义,我是正确的(比较脚注和正文)?
如果没有歧义,指针与相等运算符的比较究竟何时可以是UB?例如,如果至少一个指针是人为创建的(如上所述),它总是 UB 吗?如果一个指针指向的是
free()d的内存怎么办?鉴于脚注是非规范性的,是否可以得出结论永远不存在 UB,因为所有“其他”比较都必须产生false?第 6.5.9.6 节真的意味着无意义但按位相等的指针的相等比较应该始终为假吗?
注意这个问题被标记为language-lawyer;我不是在问编译器在实践中做什么,因为我相信已经知道答案(使用与比较整数相同的技术来比较它们)。
【问题讨论】:
-
您可能是正确的,存在歧义。您引用的第一件事没有说明 valid 指针比较,尽管这可能是意图。好问题。
-
请记住,ISO 标准中的脚注不是规范性的——它们提供信息,但不定义语言。如有疑问,非脚注材料规则。这对你来说是纯粹的信息;在任何意义上都不是您问题的答案。
-
@PaulGriffiths 但没有什么规范可以说它是 UB!它说'两个指针比较相等当且仅当[条件]'(这意味着它们相等或不相等),然后§6.5.9.3描述了相等性如何反映在运算符的行为中.使 §6.5.8.5 UB 落在 '[conditions]' 之外的东西就像两个不相等的有效指针落在 '[conditions]' 之外一样,因此,除非比较任何两个不相等的指针是 UB(不可能是真的!),否则所有行为都已定义。
-
@Oktalist 和标准的哪个部分进行比较 UB?我在争论 is 定义的行为。
-
@PaulGriffiths 这不可能。您当然可以将两个指针(例如)从
malloc()或mmap()返回与==进行比较,即使将它们与<进行比较是UB。
标签: language-lawyer c language-lawyer