这是我的头脑!我在函数调用期间对堆栈进行了一些实验,我遇到了这个小异常现象。编译器是GCC 4.9.2,以及-O1,-O2或-O3优化标志。禁用优化时(-O0)不存在该问题。无论是在C ++ 98,C ++ 11还是C ++ 14下进行编译,结果都是一样的。
在下面的示例中,我假设CDECL调用约定用于调用result(),因此args应该从右向左推送到堆栈。
#include <iostream>
void result(int a, int b) {
int* pA = &a;
int* pB = &b;
// pA should be equal to pB + sizeof(int) bytes, why does this report "false"
// when optimizations are on?
std::cout << (pA == pB + 1 ? "true" : "false") << '\n';
// this appears to be true if we dump the pointer addresses.
std::cout << pA << ", " << pB + 1 << '\n';
// and still holds water when getting values through indirection.
std::cout << "a: " << *(pB + 1) << " b: " << *pB << '\n';
}
int main(int argc, char** argv)
{
result(10, 20);
return 0;
}
启用了优化的示例输出:
false // why?
0x704eca3c088c, 0x704eca3c088c // clearly they're equal
a: 10 b: 20 // and point to the correct arguments on the stack
禁用优化的示例输出:
true
0x7e5bd5f34fdc, 0x7e5bd5f34fdc
a: 10 b: 20
编译器优化会导致堆栈指针比较失败,当它们的相等性可以直观地比较并发现是真的,优化还是没有?
编辑:我认为这个问题与建议的重复(Is it unspecified behavior to compare pointers to different arrays for equality?)不同,因为堆栈传统上是作为连续数组实现的(显然有例外 - 例如),建议的副本给出了两个例子不同的数组。
答案 0 :(得分:1)
这个结果与堆栈的布局无关,而且与无关指针之间的比较结果未指定的事实有关。
让我们看一下GCC为result
函数的第一个位置生成的程序集:
.LC0:
.string "false"
.LC1:
.string ", "
.LC2:
.string "a: "
.LC3:
.string " b: "
result(int, int):
push rbx
mov edx, 5
sub rsp, 32
mov DWORD PTR [rsp+12], edi
mov DWORD PTR [rsp+8], esi
mov edi, OFFSET FLAT:std::cout
mov esi, OFFSET FLAT:.LC0
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
如你所见,根本没有分支。指向字符串"false"
的指针被加载到esi
中,并无条件地用作std::__ostream_insert
的参数。由于指针比较的行为没有由语言指定,编译器只是假设true
情况永远不会发生并完全删除它。
答案 1 :(得分:1)
来自C ++ 17(N4659)[expr.eq] / 2(等式运算符):
比较指针的定义如下:
- 如果一个指针表示一个完整对象的地址,另一个指针表示一个超过另一个完整对象的最后一个元素的地址,则未指定比较结果。
- [...]
这意味着&a
和&b+1
的比较未指定。 (它没有未定义,正如其他答案和评论中所声称的那样;事实上,指针的相等比较从来没有在C ++中未定义的行为。)
因此,作为比较结果,您的代码可能会观察到true
或false
;并且优化器可以随心所欲地插入结果,而不是在运行时实际进行任何比较。
历史记录:此更改来自CWG Defect 1652,针对C ++ 11提交并授予了缺陷状态,这意味着它会追溯应用。最初的C ++ 11文本说指针应该比较相等&#34;如果它们代表相同的地址&#34; ,但这一点都发生了变化,因为它阻碍了优化,并且因为constexpr
中允许进行相等比较,这显然无法进行运行时地址检查。
我相信g ++总是执行这种优化,无视明显的C ++ 11要求而不执行它。