指向不同对象的指针的相等比较

受此回答这个问题的启发,我对C11和C99标准进行了一些挖掘,以便在指针上使用相等运算符(原始问题涉及关系运算符)。 这是C11在§6.5.9.6中所说的(C99类似):

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

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

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

案文正文和非规范性说明似乎存在冲突。 如果一个人认真对待文本正文中的“if if only only” ,那么在任何其他情况下都不应该返回平等,并且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(关系运算符)中,行为更加明确(我的重点):

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

问题:

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

  • 如果没有歧义,何时可以精确地将指针与等式运算符进行比较? 例如,如果至少有一个指针被人为地创建(如上所述),它总是UB吗? 如果一个指针指的是已经free()d内存free()d怎么办? 鉴于脚注是非规范性的,可以得出结论,从来没有UB,因为所有“其他”比较都必须产生false吗?

  • §6.5.9.6是否真的意味着无意义但按位相等的指针的相等比较总是应该是假的?

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

我是否纠正了有关指针的相等运算符是否为UB时存在一些歧义?

不,因为这段来自§6.5.9(3):

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

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

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

如果没有歧义,何时可以精确地将指针与等式运算符进行比较?

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

如果至少有一个指针,它总是UB 人为创造 从任意整数转换?

§6.3.2.3(5):

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

如果一个指针指的是已经free d的内存怎么办?

第6.2.4节(2):

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

可以得出一个结论,从来没有UB,因为所有“其他”比较都必须产生错误吗?

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

§6.5.9(6)是否真的意味着无意义但按位相等的指针的相等比较应该总是假的?

不,它是未定义的。