这是一个普遍的问题,但是由于我主要处理gcc/g++/VStudio,因此将其标记为c/c++。当我纠缠于优化选项时,我想到了这个问题。以最简单的形式,考虑算术运算,例如i / 6 * 8
。如果一个人面对这个表情,他很可能会将其简化为i / 3 * 4
。如果他更愿意乘以4,那么他会首先这样做,即(i * 4) / 3
。我必须再次强调,这只是一个简单的例子。
现在,编译器呢?他们是否有可能对此类操作做同样的事情?并且由于我们知道在上面的示例中,如果i
是整数,那么简化和更改操作顺序可能会导致完全不同的结果,因此可以将问题更改为:编译器是否完全避免了此类操作?
如果我们希望程序完全按照我们所说的进行某些算术运算,而又不改变运算顺序,那么我们应该担心编译器的行为吗?
最佳答案
正如其他答案所解释的那样,有效的编译器必须是保守的,并且不得采用任何会改变定义良好的程序的行为的优化。但是重要的是要记住,这种保守性仅适用于有效的,正确编写的,定义明确的程序。如果正在编译的代码依赖于未定义的行为,那么现代编译器在采用的优化中可能会显得过于彻底,而在现实世界中,这意味着上述问题的答案实际上是“是,在某些情况下,算术运算可能会受到编译器优化的影响。”
这是两个很棒的网页,描述了一些有意义的优化,编译器在遇到未定义的行为时有时会应用这些优化:
编程语言定义通常被描述为程序员和程序之间的“契约”,而一方是编译器及其实现者。只要您的代码遵循所有规则,编译器就有义务生成行为与语言定义和“抽象机”完全匹配的可执行文件。但是,如果您违反了任何规则,特别是如果您的代码陷入任何未定义的行为,则所有赌注都将消失,契约(Contract)无效,并且编译器基本上可以执行所需的任何操作。
例如,如果您写
int i = 1;
printf("%d\n", i++ + i++); /* WRONG */
您很可能会发现,更改优化级别时,表达式的值也会发生变化。
(不用说,这个故事的寓意不是,“如果编写未定义的代码,则必须小心使用哪种优化设置。”要学习的正确教训是:“不要编写依赖于以下内容的代码:未定义的行为。”)