为什么使用优化选项 (-O2, -O3) 时,这段代码的行为会有所不同?

10

我需要编写一些检查程序,但它们似乎在使用-O0,-O1,-O2或-O3时表现不同。

下面我创建了一个最小的示例,对于-O0和-O1运行良好。但是使用-O2或-O3会改变行为。在-O0和-O1的情况下,for循环增加整数,第一次达到最大值时发生溢出,并触发检查程序。而在另一种情况下,虽然整数变成负数,但for循环永远不会停止。

代码

#include <iostream>

inline bool check(const int i) {
  if (i < 0)
    return false;
  else
    return true;
}

int main() {
  for (int i = 0;; i += 50000000) {
    std::cout << i << std::endl;
    const bool succ = check(i);
    if (succ == false) {
      std::cout << "Overflow: " << i << std::endl;
      break;
    }
  }
  return 0;
}

编译器为什么可以将这个优化掉?

在尝试使用gcc、clang和icc之后,只有icc在所有的优化变体中都正确地执行了它,而其他两个则没有。


2
你可以通过使用-fwrapv来关闭g++的愚蠢行为,我想是这样的。 - Cheers and hth. - Alf
@Cheersandhth.-Alf:那个有效。同时使用“-fno-inline”也可以达到同样的效果。 - Chris
一个更通用的解决方案可能是在执行加法时将 i 转换为无符号类型。 - phuclv
1个回答

19

7
特别是编译器可能会假定 i 永远不会出现负数,因此优化了检查过程。 - Oliver Charlesworth
1
@OliCharlesworth:是的,我怀疑这就是这里发生的事情。 - Joe Z
3
关于“编译器有自由支配权”的说法,这只适用于C++标准。此外,各平台有特定的规范和程序员的期望,如果编译器要在该平台上得到认真使用,那么最好遵循这些规范和期望。在这个编译器(g++)的情况下,进行这种“傻气”的主要原因是为了优化,但它产生的代码效率远不如主流Windows编译器,这并不令人惊讶。 - Cheers and hth. - Alf
7
未定义行为就是未定义行为。就各个编译器的设计决策进行评论并没有什么帮助。绝大部分编译器(甚至包括MSVC)都有很多地方依赖于这样的细节。当然,这个例子相当明显。通常更加微妙,比如将 x < y + 5 改成 x - y < 5 - Joe Z
1
@joez:不,正如我所写的那样,这只是与ISO标准相关的未定义行为。有很多形式上的UB情况在实践中可能不会是UB。最简单的可能是使用任何局部变量,因为标准没有限制这种变量的大小,并且没有指定最小堆栈大小。程序员可以自由选择实用、可预测的工具。GNU黑客可以自由地选择他们想要的不切实际和不可预测的东西:结果就像今天一样。就是这么简单。 - Cheers and hth. - Alf
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接