在C或C ++中,如果编译器遇到for
循环,其中计数器从0
计数到n
,而n
是变量(不是函数调用,以及 NOT A CONSTANT ,编译器是否会优化循环,检查变量n
(绑定变量)是否会在循环期间被更改(访问以进行写入,例如:n
可以是在循环之前计算的字符串长度,通过优化这里我的意思是将其值复制到寄存器以避免内存访问?
以下是一个例子:
for (int c = 0; c < n; c++) {
// do something not related to n
}
编译器会注意到并优化它吗?
答案 0 :(得分:8)
c / c ++编译器会通过检查变量n(绑定变量)是否会在循环期间更改来优化循环
&#34; as-if&#34;规则允许编译器重新排序或重写代码,只要可观察的效果是&#34;好像&#34;它执行了你写的代码。
此处的关键字是&#34;可观察&#34;。除非可以证明n
在执行循环体期间不能改变,否则循环必须为每次迭代重新计算c < n
。
如果编译器可以访问循环中的所有代码(例如,它在同一编译单元中定义,或者全局优化在链接期间具有另一种外观,则n
永远不会被别名一个引用或指针)然后它很可能证明n
没有改变。
在这种情况下,你不应该对以某种方式优化循环感到惊讶。
答案 1 :(得分:2)
结果取决于正在使用的编译器。
编译器可以使用n的处理器寄存器,您仍然可以在循环内修改n。无论如何,最小化的优化是可能的。
将变量的值放在处理器寄存器中可能会导致&#39;别名&#39;问题如果你有一个指向n的指针,你使用指针间接改变n的值。
例如:
int n = 4;
int *nptr = &n;
for(int i = 0; i < n; ++i)
--*nptr;
编译器必须知道nptr是n的别名,因此必须在每次访问时从内存中读取n,但在许多情况下编译器根本无法知道n和nptr之间的关系。
您可以使用volatile关键字来阻止编译器优化变量(即volatile int n = 4;
)
答案 2 :(得分:1)
您应该尝试编译并自己查看。编译器中的优化取决于几个方面。
无论如何,为了回答你的问题,我唯一能做的就是为你提供一个与你相似的具体案例。
代码很简单:
#include <string>
int main(int argc, char *argv[]) {
std::string str = "this_is_a_string";
int size = str.size();
for (int i = 0; i < size; ++i) {
str += std::to_string(i);
}
return 0;
}
结果汇编代码是(针对不同的优化级别):
movl $0x0,-0x14(%rbp) // int i = 0;
mov -0x14(%rbp),%eax // load i into the register
cmp -0x18(%rbp),%eax // load size and compare with i in the register
jge 401317 <main+0x91> // jump if >=
// initialization up
add $0x1,%ebx // ++i (now i is stored in register)
cmp %ebx,-0x5c(%ebp) // compare i and size (which is load from memory)
je 0x80488a3 <main(int, char**)+136> // jump if = (and not >=)
相同-O1
。
Here使用过的代码和程序集。
答案 3 :(得分:0)
是的,至少GCC和大多数其他编译器都会这样做。如果您尝试查看for循环变量是什么,那么当您使用-O1或更高版本进行编译时,调试器会说该变量已经过优化。当然,编译器不需要这样做。这就是标准所说的。
for语句
for ( for-init-statement conditionopt; expressionopt) statement
相当于
{
for-init-statement
while ( condition ) {
statement
expression ;
}
}