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
12 回答
在评估
i++
或++i
之后,i
的新值在两种情况下都是相同的 . 增量前和增量之间的差异在于评估表达式本身的结果 .++i
递增i
并计算为i
的新值 .i++
求值为i
的旧值,并递增i
.在for循环中这无关紧要的原因是控制流大致如下:
测试条件
如果为false,则终止
如果是,则执行正文
执行增量步骤
因为(1)和(4)是去耦的,所以可以使用前增量或后增量 .
嗯,这很简单 . 上面的
for
循环在语义上等同于和
请注意,行
i++;
和++i;
具有与此代码块的视角相同的语义 . 它们对i
的值具有相同的效果(将其递增1),因此对这些循环的行为具有相同的效果 .请注意,如果将循环重写为,则会有所不同
这是因为在第一个代码块中
j
在增量之后看到i
的值(i
先递增,或者先递增,因此得名),而在第二个代码块j
中看到i
的值在递增之前 .您的代码的结果将是相同的 . 原因是两个增量操作可以看作是两个不同的函数调用 . 这两个函数都会导致变量递增,只有它们的返回值不同 . 在这种情况下,返回值只是丢弃,这意味着输出中没有可区别的差异 .
但是, under the hood 有区别:后增量
i++
需要创建一个临时变量来存储i
的原始值,然后执行增量并返回临时变量 . 预增量++i
不会创建临时变量 . 当然,任何体面的优化设置都应该能够在对象像int
这样的简单对象时进行优化,但请记住-operators在更复杂的类(如迭代器)中会被重载 . 由于两个重载方法可能有不同的操作(例如,可能想要输出"Hey, I'm pre-incremented!"到stdout),编译器可以使用__98171_t(基本上因为这样的编译器会解决无法解析的halting problem),它需要使用更昂贵的后增量版本如果你写myiterator++
.你应该预先增加的三个原因:
您不必考虑变量/对象是否可能具有重载的增量后方法(例如在模板函数中)并以不同方式对待它(或者忘记以不同方式对待它) .
一致的代码看起来更好 .
当有人问你"Why do you pre-increment?"时,你将有机会向他们讲述停止问题和theoretical limits of compiler optimization . :)
这是我最喜欢的面试问题之一 . 我先解释一下答案,然后告诉你为什么我喜欢这个问题 .
Solution:
答案是两个片段都打印从0到4的数字,包括0和4 . 这是因为
for()
循环通常等同于while()
循环:可写:
您可以看到OPERATION始终在循环的底部完成 . 在这种形式中,应该清楚
i++
和++i
将具有相同的效果:它们都将递增i
并忽略结果 .i
的新值直到下一次迭代开始时才会在循环顶部进行测试 .编辑:感谢Jason指出如果循环包含控制语句(例如
continue
)会阻止OPERATION
在while()
循环中执行,则for()
到while()
等价不成立 .OPERATION
始终在for()
循环的下一次迭代之前执行 .Why it's a Good Interview Question
首先,如果候选人立即告诉正确答案,则只需一两分钟,因此我们可以直接进入下一个问题 .
但令人惊讶的是(对我而言),许多候选人告诉我,后增量的循环将打印从0到4的数字,预增量循环将打印0到5,或1到5.他们通常解释之间的区别正确地进行前后递增,但是他们误解了
for()
循环的机制 .在这种情况下,我要求他们使用
while()
重写循环,这真的给了我一个他们思想过程的好主意 . 这就是为什么我首先提出这个问题的原因:我想知道他们如何解决问题,以及当我对他们的世界运作方式产生怀疑时他们如何处理 .此时,大多数候选人意识到他们的错误并找到了正确的答案 . 但我有一个人坚持他的原始答案是正确的,然后改变了他将
for()
翻译成while()
的方式 . 这是一次精彩的采访,但我们没有提出要约!希望有所帮助!
因为在任何一种情况下,增量都是在循环体之后完成的,因此不会影响循环的任何计算 . 如果编译器是愚蠢的,使用后增量可能效率稍低(因为通常它需要保留pre值的副本供以后使用),但我希望在这种情况下可以优化任何差异 .
考虑如何实现for循环可能很方便,基本上可以转换为一组赋值,测试和分支指令 . 在伪代码中,预增量看起来像:
后增量至少会有另一个步骤,但优化之后将是微不足道的
如果你这样写,那么它很重要:
比这样编写的迭代次数更多:
您可以在此处阅读Google的答案:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preincrement_and_Predecrement
所以,重点是,简单对象没什么区别,但是对于迭代器和其他模板对象,你应该使用preincrement .
编辑:
没有区别,因为你使用简单类型,所以没有副作用,并且在循环体之后执行post或preincrements,因此对循环体中的值没有影响 .
你可以用这样一个循环检查它:
i和i都在printf(“%d”,i)每次执行后执行,因此没有区别 .
是的,你会得到两者完全相同的输出 . 为什么你认为他们应该给你不同的产出?
在这种情况下,后增量或预增量很重要:
您可以通过分配或传递参数来提供某些值 . 你在
for
循环中都没有 . 它只会增加 . 后期和之前没有意义!for构造中的第三个语句仅执行,但其评估值将被丢弃而不予处理 .
当评估值被丢弃时,前后增量相等 .
它们只有在取得 Value 时才会有所不同 .
如果:
结果:
内部帖子incement = 0
i =循环0中的后增量
内部帖子incement = 1
i =循环1中的后增量
第二个for循环:
在pre pre incement = 0
i =循环1中的预增量
在pre pre incement = 1
i =循环2中的预增量
编译器翻译
至
所以在你的情况下(后期/预增量)并不重要 .
编辑:继续简单地被
goto end;
取代