无关指针的相等比较可以评估为真吗? [英] Can an equality comparison of unrelated pointers evaluate to true?

查看:16
本文介绍了无关指针的相等比较可以评估为真吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

C 标准的第 6.5.9 节 关于 ==!= 运算符声明如下:

Section 6.5.9 of the C standard regarding the == and != operators states the following:

2 应满足以下条件之一:

2 One of the following shall hold:

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

...

6 两个指针比较相等当且仅当都是空指针,两者都是指向同一个对象的指针(包括指向一个对象的指针及其开头的子对象)或函数,两者都是指向同一个数组对象的最后一个元素之后的一个,或者 one 是一个指向一个数组对象末尾的指针,另一个是指向不同数组对象开始的指针紧跟地址空间中的第一个数组对象.109)

6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.109)

7 就这些运算符而言,指向对象的指针不是数组元素的行为与指向第一个元素的指针相同长度为 1 的数组的元素,以对象的类型为其元素类型.

脚注 109:

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

109) Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior.

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

This would seem to indicate you could do the following:

int a;
int b;
printf("a precedes b: %d
", (&a + 1) == &b);
printf("b precedes a: %d
", (&b + 1) == &a);

这应该是合法的,因为我们使用的地址是数组末尾的一个元素(在这种情况下,它是一个被视为大小为 1 的数组的单个对象)而没有取消引用它.更重要的是,如果一个变量在内存中紧跟另一个变量,则需要这两个语句之一输出 1.

This should be legal since we are using an address one element past the end of an array (which in this case is a single object treated as an array of size 1) without dereferencing it. More importantly, one of these two statements would be required to output 1 if one variable immediately followed the other in memory.

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

However, testing didn't seem to pan this out. Given the following test program:

#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
", sizeof(int));
    printf("&a=%p
", (void *)&a);
    printf("&b=%p
", (void *)&b);
    printf("x=%p
", (void *)x);
    printf("y=%p
", (void *)y);

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

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

    struct s s1;
    x=&s1.a;
    y=&s1.b;
    printf("addr: s.a precedes s.b: %d
", ((&s1.a)+1) == &s1.b);
    printf("pntr: s.a precedes s.b: %d
", (x+1) == y);
    return 0;
}

编译器为 gcc 4.8.5,系统为 CentOS 7.2 x64.

Compiler is gcc 4.8.5, system is CentOS 7.2 x64.

使用 -O0,我得到以下输出:

With -O0, I get the following output:

sizeof(int)=4
&a=0x7ffe9498183c
&b=0x7ffe94981838
x=0x7ffe9498183c
y=0x7ffe94981838
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 1
  x=0x7ffe9498183c,   &a=0x7ffe9498183c
y+1=0x7ffe9498183c, &b+1=0x7ffe9498183c
addr: s.a precedes s.b: 1

我们在这里可以看到 int 是 4 个字节,a 的地址比 b 的地址多 4 个字节,并且x 保存着 a 的地址,而 y 保存着 b 的地址.然而,比较 &a == ((&b)+1) 的计算结果为 false,而比较 (x+1) == y 的计算结果为 true.我希望两者都是真的,因为被比较的地址看起来相同.

We can see here that an int is 4 bytes and that the address of a is 4 bytes past the address of b, and that x holds the address of a while y holds the address of b. However the comparison &a == ((&b)+1) evaluates to false while the comparison (x+1) == y evaluates to true. I would expect both to be true as the addresses being compared appear identical.

使用 -O1,我明白了:

sizeof(int)=4
&a=0x7ffca96e30ec
&b=0x7ffca96e30e8
x=0x7ffca96e30ec
y=0x7ffca96e30e8
addr: a precedes b: 0
addr: b precedes a: 0
pntr: a precedes b: 0
pntr: b precedes a: 0
  x=0x7ffca96e30ec,   &a=0x7ffca96e30ec
y+1=0x7ffca96e30ec, &b+1=0x7ffca96e30ec
addr: s.a precedes s.b: 1
pntr: s.a precedes s.b: 1

现在,即使(和以前一样)所比较的地址看起来相同,但两个比较的计算结果都为假.

Now both comparisons evaluate to false even though (as before) the address being compared appear to be the same.

这似乎指向未定义的行为,但根据我阅读上述段落的方式似乎应该允许这样做.

This seems to point to undefined behavior, but based on how I read the above passage it seems this should be allowed.

另请注意,struct 中相同类型的相邻对象的地址的比较在所有情况下都会打印出预期的结果.

Note also that the comparison of the addresses of adjacent objects of the same type in a struct prints the expected result in all cases.

我是否误读了此处关于允许的内容(意味着这是 UB),或者在这种情况下此版本的 gcc 不符合标准?

Am I misreading something here regarding what is allowed (meaning this is UB), or is this version of gcc non-conforming in this case?

推荐答案

不相关指针的相等比较可以评估为真吗?

Can an equality comparison of unrelated pointers evaluate to true?

是的,但是......

int a;
int b;
printf("a precedes b: %d
", (&a + 1) == &b);
printf("b precedes a: %d
", (&b + 1) == &a);

根据我对 C 标准的解释,有三种可能:

There are, by my interpretation of the C standard, three possibilities:

  • a 紧跟在 b 之前
  • b 紧跟在 a 之前
  • a 和 b 都不在另一个之前(它们之间可能有间隙或另一个对象)

我前段时间玩过这个并得出结论认为 GCC 对 == 操作符进行了无效的指针优化,即使地址相同,它也会产生错误,所以我提交了错误报告:

I played around with this some time ago and concluded that GCC was performing an invalid optimization on the == operator for pointers, making it yield false even when the addresses are the same, so I submitted a bug report:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63611

该错误已作为另一份报告的副本关闭:

That bug was closed as a duplicate of another report:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61502

响应这些错误报告的 GCC 维护者似乎认为两个对象的邻接不需要一致,并且在程序的同一运行中,它们地址的比较可能会显示它们是否相邻.从我对第二张 Bugzilla 票的评论中可以看出,我非常不同意.在我看来,如果 == 操作符没有一致的行为,标准对相邻对象的要求是没有意义的,我认为我们必须假设这些词不仅仅是装饰性的.

The GCC maintainers who responded to these bug reports seem to be of the opinion that adjacency of two objects need not be consistent and that the comparison of their addresses might show them to be adjacent or not, within the same run of the program. As you can see from my comments on the second Bugzilla ticket, I strongly disagree. In my opinion, without consistent behavior of the == operator, the standard's requirements for adjacent objects is meaningless, and I think we have to assume that those words are not merely decorative.

这是一个简单的测试程序:

Here's a simple test program:

#include <stdio.h>
int main(void) {
    int x;
    int y;
    printf("&x = %p
&y = %p
", (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");
    }
}

当我用 GCC 6.2.0 编译它时,xy 的打印地址在所有优化级别都相差 4 个字节,但我得到 y 紧跟在 x 之后,仅在 -O0 处;在 -O1-O2-O3 我得到 x 和 y 不相邻.我相信这是不正确的行为,但显然,它不会被修复.

When I compile it with GCC 6.2.0, the printed addresses of x and y differ by exactly 4 bytes at all optimization levels, but I get y immediately follows x only at -O0; at -O1, -O2, and -O3 I get x and y are not adjacent. I believe this is incorrect behavior, but apparently, it's not going to be fixed.

clang 3.8.1 的行为是正确的,在所有优化级别都显示 x 紧跟 y.Clang 以前遇到过这个问题;我举报了:

clang 3.8.1, in my opinion, behaves correctly, showing x immediately follows y at all optimization levels. Clang previously had a problem with this; I reported it:

https://bugs.llvm.org/show_bug.cgi?id=21327

它被纠正了.

我建议不要依赖于行为一致的可能相邻对象的地址的比较.

I suggest not relying on comparisons of addresses of possibly adjacent objects behaving consistently.

(注意关系运算符(<<=>>=) 指向不相关对象的指针具有未定义的行为,但相等运算符 (==, !=) 通常要求行为一致.)

(Note that relational operators (<, <=, >, >=) on pointers to unrelated objects have undefined behavior, but equality operators (==, !=) are generally required to behave consistently.)

这篇关于无关指针的相等比较可以评估为真吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆