为什么编译器会假设这些看似相等的指针不同?
Why does the compiler assume that these seemingly equal pointers differ?
看起来经过一些优化的 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;
}
b.c
int b __attribute__((section("test")));
如果我用-O0编译它,它会打印
TRUE
TRUE
TRUE
但是用-O1
FALSE
FALSE
TRUE
所以 p
和 &b
实际上是相同的值,但是编译器优化了它们的比较,假设它们永远不会相等。
我想不通,这是哪个优化造成的。
它看起来不像严格的别名,因为指针是一种类型,-fstrict-aliasing
选项不会产生这种效果。
这是记录在案的行为吗?或者这是一个错误?
p == &b
是一个 pointer 比较,并遵循 C 标准中的以下规则(6.5.9 等式运算符,第 4 点):
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.
(uintptr_t)p == (uintptr_t)&b
是一个 算术 比较,并遵循以下规则(6.5.9 等式运算符,第 6 点 ):
If both of the operands have arithmetic type, the usual arithmetic conversions are performed. Values of complex types are equal if and only if both their real parts are equal and also their imaginary parts are equal. Any two values of arithmetic types from different type domains are equal if and only if the results of their conversions to the (complex) result type determined by the usual arithmetic conversions are equal.
这两个摘录需要与实施截然不同的东西。很明显,C 规范没有要求实现在调用后一种比较的情况下模仿前一种比较的行为,反之亦然。 实现只需要遵循这个规则(7.18.1.4 Integer types capabled holding object pointers in C99 or 7.20.1.4 在 C11 中):
The [uintptr_t
] type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer.
(附录:上述引用不适用于这种情况,因为从 int*
到 uintptr_t
的转换不涉及 void*
作为中间步骤。参见 。尽管如此,所讨论的转换是实现定义的,并且您尝试的两个比较不需要表现出相同的行为,这是这里的主要内容。)
作为差异的示例,请考虑指向两个不同地址空间的同一地址的两个指针。将它们作为指针进行比较不应该 return 正确,但将它们作为无符号整数进行比较可能。
&a + 1
是一个整数加在一个指针上,遵守以下规则(6.5.6加法运算符,第8点):
When an expression that has integer type is added to or subtracted from a pointer, the
result has the type of the pointer operand. If the pointer operand points to an element of
an array object, and the array is large enough, the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression. In other words, if the expression P points to
the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and
(P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of
the array object, provided they exist. Moreover, if the expression P points to the last
element of an array object, the expression (P)+1 points one past the last element of the
array object, and if the expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object. If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
我相信这段摘录表明指针加法(和减法)仅针对同一数组对象内的指针或最后一个元素之后的指针定义。 并且因为 (1) a
不是数组并且 (2) a
和 b
不是同一个数组对象的成员,所以在我看来你的指针数学运算调用未定义的行为,您的编译器利用它来假设指针比较 return 为假。 再次如 中指出的那样(与我原来的对比此时假定的答案),指向非数组对象的指针可以被认为是指向长度为 1 的数组对象的指针,因此将 1 添加到指向标量的指针 does 有资格指向一个超过数组末尾。
因此,您的案例似乎属于此答案中提到的第一段摘录的最后一部分,当且仅当两个变量按顺序和升序链接时,您的比较才能明确评估为真。标准未指定这是否适用于您的程序,这取决于实现。
您的代码中有三个方面会导致一般问题:
指针到整数的转换是实现定义的。不能保证两个指针的转换使所有位都相同。
uintptr_t
保证从指针转换为相同类型然后返回不变(即比较等于原始指针)。但仅此而已。不能保证整数值 本身 比较相等。例如。可能存在具有任意值的未使用位。看标准,7.20.1.4.
并且(简而言之)两个指针 can 只有当它们指向同一个数组或紧跟在它后面(最后一个条目加一个)或至少一个是 空指针 。对于任何其他星座来说,它们都是不平等的。有关确切的详细信息,请参阅标准 6.5.9p6.
最后,无法保证工具链(通常是静态变量的链接器,自动变量的编译器)如何将变量放置在内存中。只有数组或 struct
(即复合类型)保证其元素的顺序。
对于您的示例,6.5.9p7 也适用。它基本上将指向非数组对象的指针用于比较,就像对大小为 1
的数组的第一个条目进行比较一样。这确实不 覆盖递增的指针过去 像&a + 1
的对象。相关的是指针所基于的对象。即指针 p
的对象 a
和指针 &b
的对象 b
。其余的可以在第6段中找到。
None 你的变量是一个数组(第 6 段的最后一部分),所以指针不需要比较相等,即使是 &a + 1 == &b
。最后一个 "TRUE" 可能来自 gcc 假设 uintptr_t
比较返回 true。
gcc 在严格遵循标准的同时积极优化。其他编译器更为保守,但这会导致代码优化程度较低。 请不要尝试 "solving" 通过禁用优化或其他 hack 来解决这个问题,而是使用定义明确的行为来修复它。这是代码中的错误。
重新阅读您的程序,我发现您(可以理解)对优化版本中的事实感到困惑
p == &b
为假,而
(uintptr_t)p == (uintptr_t)&b;
是真的。最后一行表明数值确实相同; p == &b
怎么会是假的??
我必须承认我不知道。我确信这是一个 gcc 错误。
在与 M.M 讨论后,我认为如果到 uintptr_t
的转换通过一个中间空指针(您应该将其包含在您的程序中并查看它是否改变),我可以做出以下情况任何东西):
因为转换链中的两个步骤 int*
-> void*
-> uintptr_t
都保证是可逆的,所以 不相等的 int
指针可以逻辑上不会导致相等的 uintptr_t
值。1(那些相等的 uintptr_t 值必须转换回相等的 int 指针,至少改变其中之一,因此违反了保值转换规则。)在代码中(我的目标不是这里的平等,只是演示转换和比较):
int a,b, *ap=&a, *bp = &b;
assert(ap != bp);
void *avp = ap, *bvp bp;
uintptr_t ua = (uintptr_t)avp, ub = (uintptr_t)bvp;
// Now the following holds:
// if ap != bp then *necessarily* ua != ub.
// This is violated by the OP's case (sans the void* step).
assert((int *)(void *)ua == (int*)(void*)ub);
1这假设 uintptr_t 不以填充位的形式携带隐藏信息,填充位不在算术比较中评估,但可能在类型中转换。可以通过 CHAR_BIT、UINTPTR_MAX、sizeof(uintptr_t) 和一些位摆弄来检查这一点。—
出于类似的原因,可以想象两个 uintptr_t 值比较 不同的 但转换回相同的指针(即如果 uintptr_t 中有位未用于存储指针值,并且转换不会将它们归零)。但这与 OP 的问题相反。
虽然其中一个答案已被接受,但接受的答案(以及与此相关的所有其他答案)是严重错误的,我将解释并回答问题。我将引用相同的 C 标准,即 n1570。
让我们从 &a + 1
开始。与@Theodoros 和@Peter 所说的相反,这个表达式定义了行为。要看到这一点,请考虑第 6.5.6 节第 7 "Additive operators" 段,其中指出:
For the purposes of these operators, a pointer to an object that is
not an element of an array behaves the same as a pointer to the first
element of an array of length one with the type of the object as its
element type.
第8段(特别强调的部分):
When an expression that has integer type is added to or subtracted
from a pointer, the result has the type of the pointer operand. If the
pointer operand points to an element of an array object, and the array
is large enough, the result points to an element offset from the
original element such that the difference of the subscripts of the
resulting and original array elements equals the integer expression.
In other words, if the expression P points to the i-th element of an
array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N
(where N has the value n) point to, respectively, the i+n-th and
i−n-th elements of the array object, provided they exist. Moreover, if
the expression P points to the last element of an array object, the
expression (P)+1 points one past the last element of the array object,
and if the expression Q points one past the last element of an array
object, the expression (Q)-1 points to the last element of the array
object. If both the pointer operand and the result point to elements
of the same array object, or one past the last element of the array
object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. If the result points one past the last element
of the array object, it shall not be used as the operand of a unary *
operator that is evaluated.
表达式 (uintptr_t)p == (uintptr_t)&b
有两部分。从指针到 uintptr_t
的转换是 NOT,由第 7.20.1.4 节定义(与@Olaf 和@Theodoros 所说的相反):
The following type designates an unsigned integer type with the
property that any valid pointer to void can be converted to this type,
then converted back to pointer to void, and the result will compare
equal to the original pointer:
uintptr_t
重要的是要认识到这条规则仅适用于指向 void
的有效指针。但是,在这种情况下,我们有一个指向 int
的有效指针。相关段落可见6.3.2.3节第1段:
A pointer to void may be converted to or from a pointer to any object
type. A pointer to any object type may be converted to a pointer to
void and back again; the result shall compare equal to the original
pointer.
这意味着根据本段和 7.20.1.4 允许 (uintptr_t)(void*)p
。但是 (uintptr_t)p
和 (uintptr_t)&b
由第 6.3.2.3 节第 6 段规定:
Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If the
result cannot be represented in the integer type, the behavior is
undefined. The result need not be in the range of values of any
integer type.
请注意,uintptr_t
是上述 7.20.1.4 节所述的整数类型,因此适用此规则。
(uintptr_t)p == (uintptr_t)&b
的第二部分是比较是否相等。如前所述,由于转换的结果是实现定义的,相等的结果也是实现定义的。无论指针本身是否相等,这都适用。
现在我将讨论 p == &b
。 @Olaf 的回答中的第三点是错误的,@Theodoros 的回答关于这个表达式是不完整的。第 6.5.9 节 "Equality operators" 第 7 段:
For the purposes of these operators, a pointer to an object that is
not an element of an array behaves the same as a pointer to the first
element of an array of length one with the type of the object as its
element type.
和第 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.)
与@Olaf 所说的相反,使用 ==
运算符比较指针永远不会导致未定义的行为(根据第 6.5.8 节,只有在使用 <=
等关系运算符时才会发生这种情况第 5 段,为简洁起见,我将在此处省略)。现在因为 p
指向相对于 a
的下一个 int
,只有当链接器将 b
放在二进制。否则,有不平等。所以这取决于实现(a
和 b
的相对顺序未由标准指定)。由于 a
和 b
的声明使用语言扩展,即 __attribute__((section("test")))
,相对位置确实取决于 J.5 和 3.4.2 的实现(为简洁起见省略)。
我们得出结论,check(p == &b)
和 check((uintptr_t)p == (uintptr_t)&b)
的结果是依赖于实现的。所以答案取决于您使用的编译器的版本。我正在使用 gcc 4.8,通过使用除优化级别之外的默认选项进行编译,我在 -O0 和 -O1 情况下得到的输出都是 TRUE。
根据 C11 6.5.9/6 和 C11 6.5.9/7,如果 a
和 b
相邻,测试 p == &b
必须给出 1
地址 space。
您的示例表明 GCC 似乎不满足标准的这一要求。
更新 26/Apr/2016: 我的原始答案包含有关修改代码以删除 UB 的其他潜在来源并隔离这种情况的建议。
但是,后来发现此线程提出的问题是 under review - N2012。
他们的一个建议是 p == &b
应该是未指定的,并且他们承认 GCC 实际上没有执行 ISO C11 要求。
所以我有我的答案中的剩余文本,因为不再需要证明 "compiler bug",因为不符合项(无论你是否想称之为错误)已经确定.
看起来经过一些优化的 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;
}
b.c
int b __attribute__((section("test")));
如果我用-O0编译它,它会打印
TRUE
TRUE
TRUE
但是用-O1
FALSE
FALSE
TRUE
所以 p
和 &b
实际上是相同的值,但是编译器优化了它们的比较,假设它们永远不会相等。
我想不通,这是哪个优化造成的。
它看起来不像严格的别名,因为指针是一种类型,-fstrict-aliasing
选项不会产生这种效果。
这是记录在案的行为吗?或者这是一个错误?
p == &b
是一个 pointer 比较,并遵循 C 标准中的以下规则(6.5.9 等式运算符,第 4 点):
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.
(uintptr_t)p == (uintptr_t)&b
是一个 算术 比较,并遵循以下规则(6.5.9 等式运算符,第 6 点 ):
If both of the operands have arithmetic type, the usual arithmetic conversions are performed. Values of complex types are equal if and only if both their real parts are equal and also their imaginary parts are equal. Any two values of arithmetic types from different type domains are equal if and only if the results of their conversions to the (complex) result type determined by the usual arithmetic conversions are equal.
这两个摘录需要与实施截然不同的东西。很明显,C 规范没有要求实现在调用后一种比较的情况下模仿前一种比较的行为,反之亦然。 实现只需要遵循这个规则(7.18.1.4 Integer types capabled holding object pointers in C99 or 7.20.1.4 在 C11 中):
The [
uintptr_t
] type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer.
(附录:上述引用不适用于这种情况,因为从 int*
到 uintptr_t
的转换不涉及 void*
作为中间步骤。参见
作为差异的示例,请考虑指向两个不同地址空间的同一地址的两个指针。将它们作为指针进行比较不应该 return 正确,但将它们作为无符号整数进行比较可能。
&a + 1
是一个整数加在一个指针上,遵守以下规则(6.5.6加法运算符,第8点):
When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.
我相信这段摘录表明指针加法(和减法)仅针对同一数组对象内的指针或最后一个元素之后的指针定义。 并且因为 (1) 再次如 a
不是数组并且 (2) a
和 b
不是同一个数组对象的成员,所以在我看来你的指针数学运算调用未定义的行为,您的编译器利用它来假设指针比较 return 为假。
因此,您的案例似乎属于此答案中提到的第一段摘录的最后一部分,当且仅当两个变量按顺序和升序链接时,您的比较才能明确评估为真。标准未指定这是否适用于您的程序,这取决于实现。
您的代码中有三个方面会导致一般问题:
指针到整数的转换是实现定义的。不能保证两个指针的转换使所有位都相同。
uintptr_t
保证从指针转换为相同类型然后返回不变(即比较等于原始指针)。但仅此而已。不能保证整数值 本身 比较相等。例如。可能存在具有任意值的未使用位。看标准,7.20.1.4.并且(简而言之)两个指针 can 只有当它们指向同一个数组或紧跟在它后面(最后一个条目加一个)或至少一个是 空指针 。对于任何其他星座来说,它们都是不平等的。有关确切的详细信息,请参阅标准 6.5.9p6.
最后,无法保证工具链(通常是静态变量的链接器,自动变量的编译器)如何将变量放置在内存中。只有数组或 struct
(即复合类型)保证其元素的顺序。
对于您的示例,6.5.9p7 也适用。它基本上将指向非数组对象的指针用于比较,就像对大小为 1
的数组的第一个条目进行比较一样。这确实不 覆盖递增的指针过去 像&a + 1
的对象。相关的是指针所基于的对象。即指针 p
的对象 a
和指针 &b
的对象 b
。其余的可以在第6段中找到。
None 你的变量是一个数组(第 6 段的最后一部分),所以指针不需要比较相等,即使是 &a + 1 == &b
。最后一个 "TRUE" 可能来自 gcc 假设 uintptr_t
比较返回 true。
gcc 在严格遵循标准的同时积极优化。其他编译器更为保守,但这会导致代码优化程度较低。 请不要尝试 "solving" 通过禁用优化或其他 hack 来解决这个问题,而是使用定义明确的行为来修复它。这是代码中的错误。
重新阅读您的程序,我发现您(可以理解)对优化版本中的事实感到困惑
p == &b
为假,而
(uintptr_t)p == (uintptr_t)&b;
是真的。最后一行表明数值确实相同; p == &b
怎么会是假的??
我必须承认我不知道。我确信这是一个 gcc 错误。
在与 M.M 讨论后,我认为如果到 uintptr_t
的转换通过一个中间空指针(您应该将其包含在您的程序中并查看它是否改变),我可以做出以下情况任何东西):
因为转换链中的两个步骤 int*
-> void*
-> uintptr_t
都保证是可逆的,所以 不相等的 int
指针可以逻辑上不会导致相等的 uintptr_t
值。1(那些相等的 uintptr_t 值必须转换回相等的 int 指针,至少改变其中之一,因此违反了保值转换规则。)在代码中(我的目标不是这里的平等,只是演示转换和比较):
int a,b, *ap=&a, *bp = &b;
assert(ap != bp);
void *avp = ap, *bvp bp;
uintptr_t ua = (uintptr_t)avp, ub = (uintptr_t)bvp;
// Now the following holds:
// if ap != bp then *necessarily* ua != ub.
// This is violated by the OP's case (sans the void* step).
assert((int *)(void *)ua == (int*)(void*)ub);
1这假设 uintptr_t 不以填充位的形式携带隐藏信息,填充位不在算术比较中评估,但可能在类型中转换。可以通过 CHAR_BIT、UINTPTR_MAX、sizeof(uintptr_t) 和一些位摆弄来检查这一点。—
出于类似的原因,可以想象两个 uintptr_t 值比较 不同的 但转换回相同的指针(即如果 uintptr_t 中有位未用于存储指针值,并且转换不会将它们归零)。但这与 OP 的问题相反。
虽然其中一个答案已被接受,但接受的答案(以及与此相关的所有其他答案)是严重错误的,我将解释并回答问题。我将引用相同的 C 标准,即 n1570。
让我们从 &a + 1
开始。与@Theodoros 和@Peter 所说的相反,这个表达式定义了行为。要看到这一点,请考虑第 6.5.6 节第 7 "Additive operators" 段,其中指出:
For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
第8段(特别强调的部分):
When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i−n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.
表达式 (uintptr_t)p == (uintptr_t)&b
有两部分。从指针到 uintptr_t
的转换是 NOT,由第 7.20.1.4 节定义(与@Olaf 和@Theodoros 所说的相反):
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
uintptr_t
重要的是要认识到这条规则仅适用于指向 void
的有效指针。但是,在这种情况下,我们有一个指向 int
的有效指针。相关段落可见6.3.2.3节第1段:
A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
这意味着根据本段和 7.20.1.4 允许 (uintptr_t)(void*)p
。但是 (uintptr_t)p
和 (uintptr_t)&b
由第 6.3.2.3 节第 6 段规定:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
请注意,uintptr_t
是上述 7.20.1.4 节所述的整数类型,因此适用此规则。
(uintptr_t)p == (uintptr_t)&b
的第二部分是比较是否相等。如前所述,由于转换的结果是实现定义的,相等的结果也是实现定义的。无论指针本身是否相等,这都适用。
现在我将讨论 p == &b
。 @Olaf 的回答中的第三点是错误的,@Theodoros 的回答关于这个表达式是不完整的。第 6.5.9 节 "Equality operators" 第 7 段:
For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
和第 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.)
与@Olaf 所说的相反,使用 ==
运算符比较指针永远不会导致未定义的行为(根据第 6.5.8 节,只有在使用 <=
等关系运算符时才会发生这种情况第 5 段,为简洁起见,我将在此处省略)。现在因为 p
指向相对于 a
的下一个 int
,只有当链接器将 b
放在二进制。否则,有不平等。所以这取决于实现(a
和 b
的相对顺序未由标准指定)。由于 a
和 b
的声明使用语言扩展,即 __attribute__((section("test")))
,相对位置确实取决于 J.5 和 3.4.2 的实现(为简洁起见省略)。
我们得出结论,check(p == &b)
和 check((uintptr_t)p == (uintptr_t)&b)
的结果是依赖于实现的。所以答案取决于您使用的编译器的版本。我正在使用 gcc 4.8,通过使用除优化级别之外的默认选项进行编译,我在 -O0 和 -O1 情况下得到的输出都是 TRUE。
根据 C11 6.5.9/6 和 C11 6.5.9/7,如果 a
和 b
相邻,测试 p == &b
必须给出 1
地址 space。
您的示例表明 GCC 似乎不满足标准的这一要求。
更新 26/Apr/2016: 我的原始答案包含有关修改代码以删除 UB 的其他潜在来源并隔离这种情况的建议。
但是,后来发现此线程提出的问题是 under review - N2012。
他们的一个建议是 p == &b
应该是未指定的,并且他们承认 GCC 实际上没有执行 ISO C11 要求。
所以我有我的答案中的剩余文本,因为不再需要证明 "compiler bug",因为不符合项(无论你是否想称之为错误)已经确定.