下面的for循环产生相同的结果,尽管一个使用后增量,另一个使用前增量。

代码如下:

for(i=0; i<5; i++) {
    printf("%d", i);
}

for(i=0; i<5; ++i) {
    printf("%d", i);
}

我得到了两个for循环相同的输出。我遗漏了什么吗?


是的,你会得到完全相同的输出。为什么你认为他们应该给你不同的产出?

在这样的情况下,增量后或增量前很重要:

int j = ++i;
int k = i++;
f(i++);
g(++i);

你可以通过赋值或传递参数来提供一些值。你在for循环中两者都不做。它只会递增。后-和前-在那里没有意义!


在计算i++或++i之后,i的新值在这两种情况下是相同的。前增量和后增量之间的区别在于表达式本身的求值结果。

++i增加i并计算为i的新值。

i++计算为I的旧值,并增加I。

这在for循环中无关紧要的原因是控制流大致是这样工作的:

测试条件 如果为假,终止 如果为真,则执行body 执行增量步骤

由于(1)和(4)是解耦的,可以使用前增量或后增量。


因为在任何一种情况下,增量都是在循环体之后完成的,因此不会影响循环的任何计算。如果编译器是愚蠢的,使用后增量可能会稍微降低效率(因为通常它需要保留一个预值的副本以供以后使用),但我希望在这种情况下优化消除任何差异。

考虑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

i++和++i都是在每次执行printf("%d", i)之后执行的,所以没有区别。


这很简单。上面的for循环在语义上等价于

int i = 0;
while(i < 5) {
    printf("%d", i);
    i++;
}

and

int i = 0;
while(i < 5) {
    printf("%d", 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的值。


编译器翻译

for (a; b; c)
{
    ...
}

to

a;
while(b)
{
    ...
 end:
    c;
}

所以在你的情况下(后/前增量),这并不重要。

EDIT: continue被goto end取代;


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

解决方案:

答案是这两个代码段都输出0到4的数字。这是因为for()循环通常等价于while()循环:

for (INITIALIZER; CONDITION; OPERATION) {
    do_stuff();
}

可以写成:

INITIALIZER;
while(CONDITION) {
    do_stuff();
    OPERATION;
}

可以看到OPERATION总是在循环的底部执行。在这种形式中,应该很清楚i++和++i将具有相同的效果:它们都将增加i并忽略结果。直到下一次迭代开始时,在循环的顶部才测试i的新值。


编辑:感谢Jason指出,如果循环包含阻止在while()循环中执行OPERATION的控制语句(如continue),则for() to while()等价性将不成立。OPERATION总是在for()循环的下一次迭代之前执行。


为什么这是一个很好的面试问题

首先,如果考生立即说出正确答案,只需要一两分钟,所以我们可以直接进入下一个问题。

但令人惊讶的是(对我来说),许多候选人告诉我,后增量循环将打印从0到4的数字,而前增量循环将打印0到5,或1到5。他们通常正确地解释了前递增和后递增之间的区别,但他们误解了for()循环的机制。

在这种情况下,我要求他们使用while()重写循环,这确实让我对他们的思维过程有了很好的了解。这就是我问这个问题的原因:我想知道他们是如何处理一个问题的,以及当我对他们的世界的运作方式产生怀疑时,他们是如何继续下去的。

这时,大多数考生都会意识到自己的错误并找到正确答案。但我有一个人坚持他原来的答案是正确的,然后改变了他翻译for()的方式为while()。这次面试很精彩,但我们没有录用!

希望有帮助!


在以下情况下会有区别:

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;
}

结果:

内接缝= 0

I =循环0后的增量

内贴= 1

I =循环1的增量后

第二个for循环:

内部预启动= 0

I =循环1的前增量

内部pre - incement = 1

I =循环2中的前增量


for构造中的第三条语句只被执行,但它的求值被丢弃而不被处理。 当评估值被丢弃时,前后增量相等。 只有当它们的价值被取走时,它们才会有所不同。


代码的结果将是相同的。原因是这两个递增操作可以看作是两个不同的函数调用。两个函数都会导致变量的递增,只有它们的返回值不同。在本例中,返回值被丢弃,这意味着输出中没有可区分的差异。

However, under the hood there's a difference: The post-incrementation i++ needs to create a temporary variable to store the original value of i, then performs the incrementation and returns the temporary variable. The pre-incrementation ++i doesn't create a temporary variable. Sure, any decent optimization setting should be able to optimize this away when the object is something simple like an int, but remember that the ++-operators are overloaded in more complicated classes like iterators. Since the two overloaded methods might have different operations (one might want to output "Hey, I'm pre-incremented!" to stdout for example) the compiler can't tell whether the methods are equivalent when the return value isn't used (basically because such a compiler would solve the unsolvable halting problem), it needs to use the more expensive post-incrementation version if you write myiterator++.

你应该进行预增量的三个原因:

你不必考虑变量/对象是否有重载的增量后方法(例如在模板函数中),并区别对待它(或忘记区别对待它)。 一致的代码看起来更好。 当有人问你“为什么要预增量?”时,你就有机会教他们关于停止问题和编译器优化的理论限制。:)


如果你这样写,那就很重要了:

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

重点是,对于简单对象没有区别,但是对于迭代器和其他模板对象你应该使用预增量。

编辑:

这两者没有区别,因为您使用的是简单类型,所以没有副作用,而后增量或前增量在循环体之后执行,所以对循环体中的值没有影响。

你可以用这样一个循环检查它:

for (int i = 0; i < 5; cout << "we still not incremented here: " << i << endl, i++)
{
    cout << "inside loop body: " << i << endl;
}