看起来经过一些优化的 GCC 认为来自不同翻译单元的两个指针永远不会相同,即使它们实际上相同。
代码:
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;
}
BC
int b __attribute__((section("test")));
如果我用-O0编译它,它会打印
TRUE
TRUE
TRUE
但是用-O1
FALSE
FALSE
TRUE
所以 p
和 &b
实际上是相同的值,但是编译器假设它们永远不会相等,优化了它们的比较。
我不知道是哪个优化造成的。
它看起来不像严格的别名,因为指针是一种类型,-fstrict-aliasing
选项不会产生这种效果。
这是记录在案的行为吗?或者这是一个错误?
最佳答案
您的代码中有三个方面会导致一般问题:
指针到整数的转换是实现定义的。不能保证两个指针的转换使所有位都相同。
uintptr_t
保证从指针转换为相同类型然后原封不动地返回(即比较等于原始指针)。但仅此而已。不保证整数值自身 比较相等。例如。可能存在具有任意值的未使用位。参见标准,7.20.1.4 .并且(简而言之)两个指针可以只有当它们指向同一个数组或紧跟在它后面(最后一个条目加一)或至少一个是null时才比较相等指针。对于任何其他星座来说,它们都是不平等的。有关确切的详细信息,请参阅标准,6.5.9p6 .
最后,无法保证工具链(通常是静态变量的链接器,自动变量的编译器)如何将变量放置在内存中。只有数组或 struct
(即复合类型)才能保证其元素的顺序。
对于您的示例,6.5.9p7 也适用。它基本上将指向非数组对象的指针用于比较,就像对大小为 1
的数组的第一个条目进行比较一样。这不会像 &a + 1
那样不覆盖对象的递增指针。相关的是指针所基于的对象。即对象 a
用于指针 p
和 b
用于指针 &b
。其余的可以在第 6 段中找到。
您的变量都不是数组(第 6 段的最后一部分),因此指针不需要比较相等,即使对于 &a + 1 == &b
也是如此。最后一个“TRUE”可能来自 gcc,假设 uintptr_t
比较返回 true。
众所周知,gcc 在严格遵循标准的同时积极优化。其他编译器更为保守,但这会导致代码优化程度较低。 请不要尝试通过禁用优化或其他 hack 来“解决”这个问题,而是使用定义明确的行为来修复它。这是代码中的错误。
关于c - 为什么编译器会假设这些看似相等的指针不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36035782/