事实上,当今常见的编译器将执行您所询问的那种循环不变代码运动优化。有关这一点的演示,请参阅本文中题为“它会优化吗?”的第二个练习。,或使用gcc -S -O3
和/或clang -S -O3
组装下面的示例并检查组装中的main
入口点,就像我出于好奇所做的那样。如果你的 VS2010 编译器没有执行这个优化,没关系;llvm/clang“与 MSVC 2010、2012、2013 和 14 CTP 集成”。
从理论上讲,这两个引用解释了编译器在执行优化时所具有的范围或余量。它们来自 C11 标准。IIRC C++11 有类似的东西。
§5.1.2.3p4:
在抽象机中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。
§5.1.2.3p6:
对一致性实现的最低要求是:
— 对 volatile 对象的访问严格按照抽象机的规则进行评估。
— 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。
— 交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保在程序等待输入之前实际出现提示消息。
这是程序的可观察行为。
因此,如果可以的话,编译器可能会将您的整个程序提升到编译时评估中。例如,考虑以下程序:
#include <math.h>
#include <stdio.h>
double YSinX(double x,int y)
{
double total = 0.0;
for (int i = 0; i < y; i++)
total += sin(x);
return total;
}
int main(void) {
printf("%f\n", YSinX(M_PI, 4));
}
您的编译器可能会意识到该程序0.0\n
每次都会打印,并将您的程序优化为:
int main(void) { puts("0.0"); }
也就是说,提供您的编译器可以证明既sin
不会也不会YsinX
导致任何需要的副作用。请注意,它们可能(并且可能确实)仍然会导致副作用,但生成此程序的输出不需要它们。
为了演示在实践中应用的理论知识,我通过在我的 Windows 10 系统上组装(使用/ )上面的代码测试了llvm/clang
(3.8.0 from clang --version
)和 gcc(6.4.0 from ),这两个编译器都有效地应用了优化如上所述; 在实践中,您可以期望从上面的示例中转换为等效于.gcc --version
gcc -S -O3
clang -S -O3
main
int main(void) { printf("%f", 0.0); }
你问了一个关于“编译器”的问题。如果您指的是所有 C 或 C++ 实现,则没有保证的优化,C 实现甚至不需要是编译器。您需要告诉我们哪个特定的 C 或 C++ 实现;正如我上面解释的那样,LLVM/Clang“与 MSVC 2010、2012、2013 和 14 CTP 集成”,所以您可能正在使用它。如果您的 C 或 C++ 编译器无法生成最佳代码,请获取新的编译器(例如 LLVM/Clang)或自己生成优化,最好通过修改编译器,以便您可以将补丁发送给开发人员并让优化自动传播到其他的项目。