在“for”循环中进行后增量和预增量产生相同的输出
即使使用后增量和其他预增量,以下for循环也会产生相同的结果。
这里是代码:
for(i=0; i<5; i++) { printf("%d", i); } for(i=0; i<5; ++i) { printf("%d", i); }
我为'for'循环获得相同的输出。 我错过了什么吗?
在评估i++
或++i
, ++i
的新值在两种情况下都是相同的。 增量之前和之后的差异是评估expression式本身的结果。
++i
增加i
和评估到i
的新值。
i++
评估为i++
的旧值,并增加i
。
这在for循环中无关紧要的原因是控制stream程大致如下所示:
- testing条件
- 如果是错误的,终止
- 如果这是真的,执行正文
- 执行增量步骤
因为(1)和(4)是分开的,所以可以使用前或后增量。
那么,这很简单。 以上for
循环在语义上等同于
int i = 0; while(i < 5) { printf("%d", i); i++; }
和
int i = 0; while(i < 5) { printf("%d", i); ++i; }
请注意,行i++;
和++i;
从这个代码块的angular度来看具有相同的语义。 它们都对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
的值。
你的代码的结果是一样的。 原因是这两个增量操作可以看作是两个截然不同的函数调用。 这两个函数都会导致variables增加,只有返回值不同。 在这种情况下,返回值只是被丢弃,这意味着在输出中没有可区分的差异。
然而, 在底层有一个不同之处:后增量i++
需要创build一个临时variables来存储我的原始值,然后执行增量并返回临时variables。 预增量++i
不创build一个临时variables。 当然,任何体面的优化设置应该能够优化这个对象是简单的像一个int
,但要记住,++运算符重载在更复杂的类,如迭代器。 由于两个重载的方法可能有不同的操作(例如,可能希望输出“嘿,我是预先递增的!”),编译器无法分辨在不使用返回值时方法是否相等(主要是因为这样的编译器会解决无法解决的暂停问题 ),如果你编写myiterator++
,它需要使用更昂贵的后增量版本。
你应该预先增加的三个原因:
- 你不必考虑variables/对象是否可能有一个重载的增量后方法(例如在一个模板函数中),并以不同的方式处理(或忘记对待它)。
- 一致的代码看起来更好。
- 当有人问你“你为什么预增?” 您将有机会向他们介绍编译器优化的暂停问题和理论极限 。 🙂
这是我最喜欢的面试问题之一。 我会先解释答案,然后告诉你为什么我喜欢这个问题。
解:
答案是这两个片段都打印从0到4的数字。 这是因为for()
循环通常等价于while()
循环:
for (INITIALIZER; CONDITION; OPERATION) { do_stuff(); }
可以写成:
INITIALIZER; while(CONDITION) { do_stuff(); OPERATION; }
您可以看到,操作始终在循环的底部完成。 在这种forms下,应该很清楚的是i++
和++i
会有相同的效果:它们都会增加i
而忽略结果。 i
的新值在下一次迭代开始之前不会被testing,在循环的顶部。
编辑 :感谢杰森指出,如果循环包含控制语句(如continue
),这将防止while()
循环中执行OPERATION
for()
到while()
等价不成立。 OPERATION
总是在for()
循环的下一次迭代之前执行。
为什么这是一个好的面试问题
首先,如果候选人立即说出正确的答案,只需要一两分钟,所以我们可以直接转到下一个问题。
但令人惊讶的是(对我来说),许多候选人告诉我循环后增量将打印从0到4的数字,而预增量循环将打印0到5或1到5.他们通常解释前后递增正确,但他们误解了for()
循环的机制。
在这种情况下,我要求他们用while()
重写循环,这真的给了我一个思路的好主意。 这就是为什么我首先提出这个问题的原因:我想知道他们是如何处理一个问题的,以及当我怀疑他们的世界如何运作时,他们是如何继续下去的。
在这一点上,大多数候选人意识到他们的错误,并find正确的答案。 但是我有一个坚持原来的答案是正确的,然后改变了他把for()
转换成while()
。 它做了一个有趣的采访,但我们没有提供报价!
希望有所帮助!
因为在任何情况下,增量都是在循环体之后完成的,因此不会影响循环的任何计算。 如果编译器是愚蠢的,那么使用后增量可能会稍微低效(因为通常需要保留前值的副本供以后使用),但是我希望在这种情况下可以优化任何差异。
考虑如何实现for循环可能会很方便,基本上可以将其转换为一组赋值,testing和分支指令。 在伪代码中,预增量将如下所示:
set i = 0 test: if i >= 5 goto done call printf,"%d",i set i = i + 1 goto test done: nop
后增加至less会有另一个步骤,但是优化起来是微不足道的
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
如果你是这样写的,那么重要的是:
for(i=0; i<5; i=j++) { printf("%d",i); }
如果写成这样的话,会迭代一次:
for(i=0; i<5; i=++j) { printf("%d",i); }
你可以在这里阅读谷歌的答案: http : //google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preincrement_and_Predecrement
所以,要点是,简单的对象没有什么区别,但对于迭代器和其他模板对象,你应该使用预增量。
编辑:
没有区别,因为你使用简单的types,所以没有任何副作用,在循环体之后执行后或preincrements,所以没有影响循环体中的值。
你可以用这样一个循环来检查它:
for (int i = 0; i < 5; cout << "we still not incremented here: " << i << endl, i++) { cout << "inside loop body: " << i << endl; }
printf(“%d”,i)每次执行后,i ++和++ i都执行,所以没有区别。
是的,你会得到完全相同的输出。 你为什么认为他们应该给你不同的产出?
在这样的情况下后增或前增:
int j = ++i; int k = i++; f(i++); g(++i);
在那里你提供一些价值,无论是通过分配或通过传递参数。 你没有在你的for
循环。 它只会增加。 之后和之前在这里没有道理!
for构造中的第三个语句只被执行,但其评估值被丢弃而不被处理。
当评估值被丢弃时,前后增量相等。
它们只有在取得价值时才会有所不同。
有一个区别是否:
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 incement = 0
i =循环0中的后增量
里面post incement = 1
i =循环1中的后增量
第二个循环:
内部pre incement = 0
i =循环1中的预增量
内置pre incement = 1
i =循环2中的预增量
编译器翻译
for (a; b; c) { ... }
至
a; while(b) { ... end: c; }
所以在你的情况(后/预增)没关系。
编辑:继续简单地换成goto end;