C 为什么两个相同的指针不与-O1进行相等比较?

C 为什么两个相同的指针不与-O1进行相等比较?,c,pointers,gcc,clang,language-lawyer,C,Pointers,Gcc,Clang,Language Lawyer,在这种情况下,正确的行为是什么 当且仅当两个指针都是空指针时,两个指针比较相等 它们不是空的 两者都是指向同一对象(包括指向对象的指针及其开头的子对象)或函数的指针 它们不指向同一对象、子对象或函数 两者都是指向同一数组对象的最后一个元素的指针 它们不是指向数组元素的指针 或者一个指针指向一个数组对象的末尾,另一个指针指向另一个数组对象的开头,该数组对象恰好紧跟在地址空间中的第一个数组对象之后 它们不是指向数组元素的指针 因此,根据标准,您的指针不符合作为相等进行比较的要求,并且不应该作为相等

在这种情况下,正确的行为是什么

当且仅当两个指针都是空指针时,两个指针比较相等

它们不是空的

两者都是指向同一对象(包括指向对象的指针及其开头的子对象)或函数的指针

它们不指向同一对象、子对象或函数

两者都是指向同一数组对象的最后一个元素的指针

它们不是指向数组元素的指针

或者一个指针指向一个数组对象的末尾,另一个指针指向另一个数组对象的开头,该数组对象恰好紧跟在地址空间中的第一个数组对象之后

它们不是指向数组元素的指针


因此,根据标准,您的指针不符合作为相等进行比较的要求,并且不应该作为相等进行比较

现在,在您的测试中,在前三种情况下,指针实际上是相等的。可以说编译器没有严格遵守该标准,因为该标准说“如果且仅当”,但正如您所看到的,没有-O1的clang和gcc的行为就像该标准说“如果”而没有“且仅当”部分一样。编译器只是不试图采取额外的措施来确保“且仅当”部分得到尊重,因此它们允许指针进行相等的比较,这纯粹是巧合,尽管根据标准,它们不应该这样做

由于这纯粹是巧合,在最后一种情况下,巧合不再成立,因为有许多未知的原因与编译器的优化实现有关。编译器可能已经决定颠倒堆栈上变量的顺序,或者使它们彼此远离,或者谁知道是什么

在这种情况下,正确的行为是什么

没有。将指针与两个完全不相关的对象的末尾进行比较或将指针与的末尾进行比较是未定义的行为

Per(粗体是我的):

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


只是为了在其他答案中澄清一件事。假设您有两个对象,
a
b
,一个指针
p
,并使指针指向其中一个具有偏移量的对象,例如
p=&a+1
。现在,
p
恰好可以与
&b
具有相同的值

但是-这很重要-这并不意味着
p
指向
b


我在这里做一个比较。也许这有点奇怪,但请容忍我。想象一下,你看到一个指向城市的路标。现在想象一下,你决定站在标志和城市之间。这是否意味着标志正指向你?在这种情况下,答案可能是相当哲学的,但它表明了这一点(哈哈)。即使有人可以说是和否,但很明显,这个标志并不是要指向你。在C标准中,他们选择将“指向”解释为“故意指向”

没有正确的行为<代码>a和
b
是不相关的。对堆栈布局进行假设会使您进入未定义的区域
a
b
在语言语义方面并没有很好的定义关系,所以你们可以得到你们所得到的。这种情况下的“正确行为”是避免编写这样的代码=D@BenZotto:直觉上这很清楚,但它是如何从标准中的语言中得出的?@NateEldredge:我扩展成了一个答案。另请参见Andrewenle的答案,该答案来自标准的另一部分。
&b-1
不是由C标准定义的,因为它定义了对象内部或之后的指针算术发生的情况,而不是减去对象之前的点时发生的情况。然而,如果将代码更改为将
&a+1
&b
进行比较,则GCC可能返回false,即使它显示的
a
b
的地址显示
b
实际上刚刚超出
a
…如果我正确理解标准段落,这只是因为指针而未定义的行为减法。如果将
q
(一个过去的-
b
)与例如
&a
@walnut进行比较,则这只是未指定的情况,如果语言规范省略了某些结构或表达式的行为的任何定义,那么该行为在一般英语用法和特定C语言域意义上都是未定义的。该标准明确规定了这一点。因此,在C语言域的意义上,未指定的行为只有在标准明确表示它会发生时才会发生。@walnut,否,在标准语言中,内存布局的相关细节是未定义的,不是未指定的。在OP这样的情况下,等式表达式的值取决于两个不相关对象的(未定义的)相对布局,该表达式的值是未定义的。“将指针与两个完全不相关对象的端点进行比较或超过一个指针是未定义的行为”这显然是200%的错误。字母错误,C的精神错误。句子“将指针与两个完全不相关的对象的端点进行比较或一个指针超过两个完全不相关的对象的端点是未定义的行为”显然是错误的。根据C 2018 6.5.8 5,对于关系运算符,此类指针的比较未定义。但定义了与相等运算符的比较。6.5.9给出了定义每种情况下结果的“如果且仅当”
#include <stdio.h>

int main(void)
{
    int a, b;
    int *p = &a;

#ifdef __clang__
    int *q = &b + 1;
#elif __GNUC__
    int *q = &b - 1;
#endif

    printf("%p %p %d\n", (void *)p, (void *)q, p == q);
}
$ ./prog_clang
0x7ffebf0a65d4 0x7ffebf0a65d4 1
$ ./prog_clang_01
0x7ffd9931b9bc 0x7ffd9931b9bc 1
$ ./prog_gcc
0x7ffea055a980 0x7ffea055a980 1
$ ./prog_gcc_01
0x7fffd5fa5490 0x7fffd5fa5490 0