以下for循环产生相同的结果,即使一个使用后增量和另一个预增量。
以下是代码:
for(i=0; i<5; i++) {
printf("%d", i);
}
for(i=0; i<5; ++i) {
printf("%d", i);
}
我为两个'for'循环得到相同的输出。我错过了什么吗?
答案 0 :(得分:302)
评估i++
或++i
后,i
的新值在两种情况下均相同。增量前和增量之间的差异在于评估表达式本身的结果。
++i
递增i
并评估为i
的新值。
i++
评估i
的旧值,并递增i
。
这在for循环中无关紧要的原因是控制流大致如下:
因为(1)和(4)是去耦的,所以可以使用前增量或后增量。
答案 1 :(得分:110)
嗯,这很简单。上述for
循环在语义上等同于
int i = 0;
while(i < 5) {
printf("%d", i);
i++;
}
和
int i = 0;
while(i < 5) {
printf("%d", i);
++i;
}
请注意,i++;
和++i;
行与此代码块的视角具有相同的语义。它们对i
的值具有相同的影响(将其递增1),因此对这些循环的行为具有相同的影响。
请注意,如果将循环重写为
,则会有所不同int i = 0;
int j = i;
while(j < 5) {
printf("%d", i);
j = ++i;
}
int i = 0;
int j = i;
while(j < 5) {
printf("%d", i);
j = i++;
}
这是因为在第一个代码块j
看到增量后i
的值(i
首先递增,或者预先递增,因此名称)并且在第二个代码块j
在增量之前看到i
的值。
答案 2 :(得分:88)
您的代码的结果将是相同的。原因是两个增量操作可以看作是两个不同的函数调用。这两个函数都会导致变量递增,只有它们的返回值不同。在这种情况下,返回值就会被丢弃,这意味着输出中没有可区别的差异。
然而,引擎存在差异:后增量i++
需要创建临时变量以存储原始值{{1然后执行增量并返回临时变量。预增量i
不会创建临时变量。当然,任何体面的优化设置都应该能够在对象像++i
这样的简单对象时进行优化,但请记住++ - 运算符在迭代器等更复杂的类中被重载。由于两个重载方法可能具有不同的操作(例如,可能希望输出“嘿,我是预先递增的!”到stdout),当不使用返回值时,编译器无法判断方法是否等效(基本上因为这样的编译器会解决无法解析的halting problem),如果你写int
,它需要使用更昂贵的增量后版本。
你应该预先增加的三个原因:
答案 3 :(得分:24)
这是我最喜欢的面试问题之一。我先解释答案,然后告诉你为什么我喜欢这个问题。
<强>解决方案:强>
答案是两个片段都会打印0到4之间的数字,包括0和4。这是因为for()
循环通常等同于while()
循环:
for (INITIALIZER; CONDITION; OPERATION) {
do_stuff();
}
可写:
INITIALIZER;
while(CONDITION) {
do_stuff();
OPERATION;
}
您可以在循环底部看到OPERATION 始终完成。在这种形式中,应该清楚i++
和++i
具有相同的效果:它们都会增加i
并忽略结果。 i
的新值在下一次迭代开始之前不会在循环的顶部进行测试。
编辑:感谢Jason指出,如果循环包含控制语句(例如,for()
到while()
等效,则不成立作为continue
)阻止OPERATION
在while()
循环中执行。 OPERATION
总是在for()
循环的下一次迭代之前执行
为什么这是一个很好的面试问题
首先,如果候选人立即告诉正确答案,则只需一两分钟,因此我们可以直接进入下一个问题。
但令人惊讶的是(对我来说),许多候选人告诉我,后增量的循环将打印从0到4的数字,而预增量循环将打印0到5,或1到5.他们通常会解释前后增量之间的差异正确,但他们误解了for()
循环的机制。
在这种情况下,我要求他们使用while()
重写循环,这确实让我对他们的思维过程有了一个很好的了解。这就是为什么我首先提出这个问题的原因:我想知道他们如何解决问题,以及当我对他们的世界运作方式产生怀疑时他们如何处理。
此时,大多数候选人意识到他们的错误并找到了正确的答案。但我有一个人坚持他的原始答案是正确的,然后改变了他将for()
翻译成while()
的方式。这是一次精彩的采访,但我们没有提出要约!
希望有所帮助!
答案 4 :(得分:6)
因为在任何一种情况下,增量都是在循环体之后完成的,因此不会影响循环的任何计算。如果编译器是愚蠢的,使用后增量可能效率稍低(因为通常它需要保留 pre 值的副本供以后使用),但我希望有任何差异在这种情况下优化了。
考虑如何实现for循环可能很方便,基本上可以转换为一组赋值,测试和分支指令。在伪代码中,预增量看起来像:
set i = 0
test: if i >= 5 goto done
call printf,"%d",i
set i = i + 1
goto test
done: nop
后增量至少会有另一个步骤,但优化远离
将是微不足道的 set i = 0
test: if i >= 5 goto done
call printf,"%d",i
set j = i // store value of i for later increment
set i = j + 1 // oops, we're incrementing right-away
goto test
done: nop
答案 5 :(得分:4)
如果你这样写,那就很重要了:
for(i=0; i<5; i=j++) {
printf("%d",i);
}
迭代次数比写下这样:
for(i=0; i<5; i=++j) {
printf("%d",i);
}
答案 6 :(得分:2)
您可以在此处阅读Google的答案: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preincrement_and_Predecrement
所以,重点是,对于简单对象没什么区别,但是对于迭代器和其他模板对象,你应该使用preincrement。
编辑:
没有区别,因为你使用简单类型,所以没有副作用,并且在循环体后执行post或preincrements,因此对循环体中的值没有影响。
你可以用这样的循环检查它:
for (int i = 0; i < 5; cout << "we still not incremented here: " << i << endl, i++)
{
cout << "inside loop body: " << i << endl;
}
答案 7 :(得分:1)
是的,你们两个都会获得完全相同的输出。为什么你认为他们应该给你不同的产出?
在这种情况下的增量后或预增量事项:
int j = ++i;
int k = i++;
f(i++);
g(++i);
通过分配或传递参数来提供某些值。你在for
循环中都没有。它只会增加。后期和之前没有意义!
答案 8 :(得分:1)
i ++和++ i都是在每次执行printf(“%d”,i)后执行的,所以没有区别。
答案 9 :(得分:1)
for构造中的第三个语句仅被执行,但其评估值被丢弃而不被处理 当评估值被丢弃时,前后增量相等 只有采取它们的价值才会有所不同。
答案 10 :(得分:-1)
如果出现以下情况有区别:
int main()
{
for(int i(0); i<2; printf("i = post increment in loop %d\n", i++))
{
cout << "inside post incement = " << i << endl;
}
for(int i(0); i<2; printf("i = pre increment in loop %d\n",++i))
{
cout << "inside pre incement = " << i << endl;
}
return 0;
}
结果:
在post post incement = 0
i =循环0中的后增量
内部帖子incement = 1
i =循环1中的后增量
第二个for循环:
在pre incement里面= 0
i =循环1中的预增量
内部预制= 1
i =循环2中的预增量
答案 11 :(得分:-3)
编译器翻译
for (a; b; c)
{
...
}
到
a;
while(b)
{
...
end:
c;
}
所以在你的情况下(后期/预增量)并不重要。
编辑:继续简单地由goto end;