为什么编译器会假设这些看起来相同的指针有所不同? [英] Why does the compiler assume that these seemingly equal pointers differ?

查看:235
本文介绍了为什么编译器会假设这些看起来相同的指针有所不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看起来像GCC的一些优化,认为来自不同翻译单元的两个指针,即使它们实际上是相同的,也不可能是相同的。



代码:



main.c

  #include  #include< stdio.h> 

int a __attribute __((section(test)));
extern int b;

void check(int cond){puts(cond?TRUE:FALSE); }

int main(){
int * p =& a + 1;
check(
(p ==& b)
==
((uintptr_t)p ==(uintptr_t)& b)
);
check(p ==& b);
check((uintptr_t)p ==(uintptr_t)& b);
返回0;
}



bc $ b

  int b __attribute __((section(test))); 

如果我使用-O0进行编译,它会打印

  TRUE 
TRUE
TRUE



< - >但与-O1

  FALSE 
FALSE
TRUE

因此 p & b 实际上是相同的值,但编译器优化了它们的比较,假设它们永远不会相等。



我不明白,哪种优化做到了这一点。

它看起来不像严格的别名,因为指针是一种类型,并且 -fstrict-aliasing 选项不会产生这种效果。



这是记录的行为吗?或者这是一个bug?

解决方案

代码中有三个方面会导致一般问题:
$ b


  1. 将指针转换为整数是实现定义。我们不保证将两个指针转换为所有位相同。

  2. uintptr_t 保证转换从一个指针指向相同的类型,然后返回不变(即等于原始指针)。但仅此而已。整数值本身并不保证相等。例如。可能会有任意值的未使用位。请参阅标准 7.20.1.4

  3. 至少有一个是空指针。对于任何其他星座,他们比较不平等。有关确切的详细信息,请参阅标准 6.5.9p6

最后,不能保证工具链如何将变量放入内存中(通常是静态变量的链接器,自动变量的编译器)。只有一个数组或 struct (即复合类型)才能保证其元素的排序。



6.5.9p7也适用。它基本上将一个指向非数组对象的指针视为比较大小 1 的数组的第一个条目。 不是覆盖像& a + 1 这样的对象的递增指针过去。相关的是指针所基于的对象。对于指针 p b 来说,它是指针 a 和b 。其余部分可以在第6段找到。

没有一个变量是数组(第6段的最后部分),所以指针不需要相等,即使是& a + 1 ==& b 。假设 uintptr_t 比较返回true,gcc可能会产生最后一个TRUE。

gcc已知可以极力优化同时严格遵守标准。其他编译器更保守,但这会导致代码不太好。 不要通过禁用优化或其他黑客手段来解决此问题,而是使用定义良好的行为来解决此问题。这是代码中的一个错误。


Looks like GCC with some optimization thinks two pointers from different translation units can never be same even if they are actually the same.

Code:

main.c

#include <stdint.h>
#include <stdio.h>

int a __attribute__((section("test")));
extern int b;

void check(int cond) { puts(cond ? "TRUE" : "FALSE"); }

int main() {
    int * p = &a + 1;
    check(
        (p == &b)
        ==
        ((uintptr_t)p == (uintptr_t)&b)
    );
    check(p == &b);
    check((uintptr_t)p == (uintptr_t)&b);
    return 0;
}

b.c

int b __attribute__((section("test")));

If I compile it with -O0, it prints

TRUE
TRUE
TRUE

But with -O1

FALSE
FALSE
TRUE

So p and &b are actually the same value, but the compiler optimized out their comparison assuming they can never be equal.

I can't figure out, which optimization made this.

It doesn't look like strict aliasing, because pointers are of one type, and -fstrict-aliasing option doesn't make this effect.

Is this the documented behavour? Or is this a bug?

解决方案

There are three aspects in your code which result in general problems:

  1. Conversion of a pointer to an integer is implementation defined. There is no guarantee conversion of two pointers to have all bits identical.

  2. uintptr_t is guaranteed to convert from a pointer to the same type then back unchanged (i.e. compare equal to the original pointer). But nothing more. The integer values themselves are not guaranteed to compare equal. E.g. there could be unused bits with arbitrary value. See the standard, 7.20.1.4.

  3. And (briefly) two pointers can only compare equal if they point into the same array or right behind it (last entry plus one) or at least one is a null pointer. For any other constellation, they compare unequal. For the exact details, see the standard, 6.5.9p6.

Finally, there is no guarantee how variables are placed in memory by the toolchain (typically the linker for static variables, the compiler for automatic variables). Only an array or a struct (i.e. composite types) guarantee the ordering of its elements.

For your example, 6.5.9p7 also applies. It basically treats a pointer to a non-array object for comparision like on to the first entry of an array of size 1. This does not cover an incremented pointer past the object like &a + 1. Relevant is the object the pointer is based on. That is object a for pointer p and b for pointer &b. The rest can be found in paragraph 6.

None of your variables is an array (last part of paragraph 6), so the pointers need not compare equal, even for &a + 1 == &b. The last "TRUE" might arise from gcc assuming the uintptr_t comparison returning true.

gcc is known to agressively optimise while strictly following the standard. Other compilers are more conservative, but that results in less optimised code. Please don't try "solving" this by disabling optimisation or other hacks, but fix it using well-defined behaviour. It is a bug in the code.

这篇关于为什么编译器会假设这些看起来相同的指针有所不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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