在“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程大致如下所示:

  1. testing条件
  2. 如果是错误的,终止
  3. 如果这是真的,执行正文
  4. 执行增量步骤

因为(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++ ,它需要使用更昂贵的后增量版本。

你应该预先增加的三个原因:

  1. 你不必考虑variables/对象是否可能有一个重载的增量后方法(例如在一个模板函数中),并以不同的方式处理(或忘记对待它)。
  2. 一致的代码看起来更好。
  3. 当有人问你“你为什么预增?” 您将有机会向他们介绍编译器优化的暂停问题和理论极限 。 🙂

这是我最喜欢的面试问题之一。 我会先解释答案,然后告诉你为什么我喜欢这个问题。

解:

答案是这两个片段都打印从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;