启用优化时堆栈指针比较异常

时间:2018-02-07 23:43:01

标签: c++ optimization

这是我的头脑!我在函数调用期间对堆栈进行了一些实验,我遇到了这个小异常现象。编译器是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?)不同,因为堆栈传统上是作为连续数组实现的(显然有例外 - 例如),建议的副本给出了两个例子不同的数组。

2 个答案:

答案 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 ++中未定义的行为。)

因此,作为比较结果,您的代码可能会观察到truefalse;并且优化器可以随心所欲地插入结果,而不是在运行时实际进行任何比较。

历史记录:此更改来自CWG Defect 1652,针对C ++ 11提交并授予了缺陷状态,这意味着它会追溯应用。最初的C ++ 11文本说指针应该比较相等&#34;如果它们代表相同的地址&#34; ,但这一点都发生了变化,因为它阻碍了优化,并且因为constexpr中允许进行相等比较,这显然无法进行运行时地址检查。

我相信g ++总是执行这种优化,无视明显的C ++ 11要求而不执行它。