c - 为什么编译器会假设这些看似相等的指针不同?

标签 c pointers gcc language-lawyer compiler-optimization

看起来经过一些优化的 GCC 认为来自不同翻译单元的两个指针永远不会相同,即使它们实际上相同。

代码:

ma​​in.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;
}

BC

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

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

TRUE
TRUE
TRUE

但是用-O1

FALSE
FALSE
TRUE

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

我不知道是哪个优化造成的。

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

这是记录在案的行为吗?或者这是一个错误?

最佳答案

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

  1. 指针到整数的转换是实现定义的。不能保证两个指针的转换使所有位都相同。

  2. uintptr_t 保证从指针转换为相同类型然后原封不动地返回(即比较等于原始指针)。但仅此而已。不保证整数值自身 比较相等。例如。可能存在具有任意值的未使用位。参见标准,7.20.1.4 .

  3. 并且(简而言之)两个指针可以只有当它们指向同一个数组或紧跟在它后面(最后一个条目加一)或至少一个是null时才比较相等指针。对于任何其他星座来说,它们都是不平等的。有关确切的详细信息,请参阅标准,6.5.9p6 .

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

对于您的示例,6.5.9p7 也适用。它基本上将指向非数组对象的指针用于比较,就像对大小为 1 的数组的第一个条目进行比较一样。这不会像 &a + 1 那样覆盖对象的递增指针。相关的是指针所基于的对象。即对象 a 用于指针 pb 用于指针 &b。其余的可以在第 6 段中找到。

您的变量都不是数组(第 6 段的最后一部分),因此指针不需要比较相等,即使对于 &a + 1 == &b 也是如此。最后一个“TRUE”可能来自 gcc,假设 uintptr_t 比较返回 true。

众所周知,gcc 在严格遵循标准的同时积极优化。其他编译器更为保守,但这会导致代码优化程度较低。 不要尝试通过禁用优化或其他 hack 来“解决”这个问题,而是使用定义明确的行为来修复它。这是代码中的错误。

关于c - 为什么编译器会假设这些看似相等的指针不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36035782/

相关文章:

c - 使用 C 程序验证 chrome 是否正在运行

在 C 中将字符指针转换为大写

c - 侵入式链表的指针算法

c - c中的 "short int"和 "short"有什么区别?

c - 逐行添加

android - GCC 6.3 警告常量 '0' 与 bool 表达式的比较始终为 false

linux - 安装 ns2 时如何在我的 makefile 中设置 gcc 编译器版本?

c - GCC 优化掉无法优化的 if 子句

c - 绘制带星号的三角形 int 时出现问题

c - 我刚刚设置的变量中 printf 期间的段错误