C 不相关指针的相等比较是否可以计算为true?

C 不相关指针的相等比较是否可以计算为true?,c,pointers,language-lawyer,C,Pointers,Language Lawyer,关于==和的第6.5.9节=运算符声明如下: int a; int b; printf("a precedes b: %d\n", (&a + 1) == &b); printf("b precedes a: %d\n", (&b + 1) == &a); 2下列其中一项应适用: 两个操作数都具有算术类型 两个操作数都是指向兼容类型的合格或不合格版本的指针 一个操作数是指向对象类型的指针,另一个是指向void的限定或非限定版本的指针;或 一个操作数是指针,另一

关于
==
的第6.5.9节=运算符声明如下:

int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
2下列其中一项应适用:

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

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

7在这些运算符中,指向以下对象的指针 数组中的元素的行为与指向第一个元素的指针的行为不同 长度为1的数组的元素,其对象类型为 元素类型。

int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
脚注109:

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

这似乎表明您可以执行以下操作:

int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
这应该是合法的,因为我们使用的地址元素超过了数组的末尾(在本例中,它是一个被视为大小为1的数组的单个对象),而不去引用它。更重要的是,如果一个变量紧跟在内存中的另一个变量之后,则需要这两个语句中的一个来输出
1

然而,测试似乎并没有解决这个问题。给定以下测试程序:

#include <stdio.h>

struct s {
    int a;
    int b;
};

int main()
{
    int a;
    int b;
    int *x = &a;
    int *y = &b;

    printf("sizeof(int)=%zu\n", sizeof(int));
    printf("&a=%p\n", (void *)&a);
    printf("&b=%p\n", (void *)&b);
    printf("x=%p\n", (void *)x);
    printf("y=%p\n", (void *)y);

    printf("addr: a precedes b: %d\n", ((&a)+1) == &b);
    printf("addr: b precedes a: %d\n", &a == ((&b)+1));
    printf("pntr: a precedes b: %d\n", (x+1) == y);
    printf("pntr: b precedes a: %d\n", x == (y+1));

    printf("  x=%p,   &a=%p\n", (void *)(x), (void *)(&a));
    printf("y+1=%p, &b+1=%p\n", (void *)(y+1), (void *)(&b+1));

    struct s s1;
    x=&s1.a;
    y=&s1.b;
    printf("addr: s.a precedes s.b: %d\n", ((&s1.a)+1) == &s1.b);
    printf("pntr: s.a precedes s.b: %d\n", (x+1) == y);
    return 0;
}
#include <stdio.h>
int main(void) {
    int x;
    int y;
    printf("&x = %p\n&y = %p\n", (void*)&x, (void*)&y);
    if (&y == &x + 1) {
        puts("y immediately follows x");
    }
    else if (&x == &y + 1) {
        puts("x immediately follows y");
    }
    else {
        puts("x and y are not adjacent");
    }
}
我们可以在这里看到,
int
是4个字节,
a
的地址比
b
的地址多4个字节,
x
保存
a
的地址,
y
保存
b
的地址。但是,比较
&a==(&b)+1)
的计算结果为false,而比较
(x+1)==y
的计算结果为true。我希望两者都是正确的,因为被比较的地址看起来是相同的

使用
-O1
,我得到以下信息:

sizeof(int)=4
&a=0x7ffca96e30ec
&b=0x7ffca96e30e8
x=0x7ffca96e30ec
y=0x7ffca96e30e8
地址:a在b之前:0
地址:b位于a:0之前
pntr:a先于b:0
pntr:b先于a:0
x=0x7ffca96e30ec,&a=0x7ffca96e30ec
y+1=0x7ffca96e30ec和b+1=0x7ffca96e30ec
地址:s.a先于s.b:1
pntr:s.a先于s.b:1
现在,两个比较的结果都为false,即使(与以前一样)被比较的地址看起来是相同的

这似乎指向,但根据我如何阅读上述文章,这似乎应该是允许的

还要注意的是,对
结构中相同类型的相邻对象的地址进行比较会打印出所有情况下的预期结果

我是否在这里误读了关于什么是允许的(意思是这是UB),或者在这种情况下,这个版本的gcc不符合要求

int a;
int b;
printf("a precedes b: %d\n", (&a + 1) == &b);
printf("b precedes a: %d\n", (&b + 1) == &a);
是定义完美的代码,但可能更多的是运气而不是判断

您可以获取标量的地址,并将指针设置为超过该地址一个。因此
&a+1
有效,但
&a+2
无效。您还可以使用
=
将同一类型指针的值与任何其他有效指针的值进行比较=,但指针算法仅在数组中有效

你断言
a
b
的地址告诉了你关于如何将它们放在内存中的任何事情,这是胡说八道。明确地说,您无法通过指针算术在
a
的地址上“到达”
b

至于

struct s {
    int a;
    int b;
};
该标准保证
结构
的地址与
a
的地址相同,但允许在
a
b
之间插入任意数量的填充。同样,您无法通过
a
地址上的任何指针算法到达
b
地址

不相关指针的相等比较是否可以计算为true

对。C指定何时为true

两个指针比较相等当且仅当。。。或者一个指针指向一个数组对象的末尾,另一个指针指向另一个数组对象的开头,该数组对象恰好紧跟在地址空间中的第一个数组对象之后。C11dr§6.5.9 6

需要明确的是:代码中的相邻变量不需要在内存中相邻,但可以是


下面的代码说明了这是可能的。除了传统的
“%p”
(void*)
之外,它还使用
int*
的内存转储

然而OP的代码和输出并没有反映这一点。鉴于上述规范的“比较相等当且仅当”部分,IMO,OP的编译不符合要求。相同类型的相邻内存中变量
p,q
&p+1==&q
&p==&q+1
必须为真

如果对象类型不同,则无意见-OP不会询问IAC


void print\u int\u ptr(常量字符*前缀,int*p){
printf(“%s%p”,前缀,(void*)p);
联合{
int*ip;
#include <stdio.h>
int main(void) {
    int x;
    int y;
    printf("&x = %p\n&y = %p\n", (void*)&x, (void*)&y);
    if (&y == &x + 1) {
        puts("y immediately follows x");
    }
    else if (&x == &y + 1) {
        puts("x immediately follows y");
    }
    else {
        puts("x and y are not adjacent");
    }
}