'for'循环内的后递增和预递增产生相同的输出

时间:2011-01-16 15:33:14

标签: c++ c operator-precedence

以下for循环产生相同的结果,即使一个使用后增量和另一个预增量。

以下是代码:

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

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

我为两个'for'循环得到相同的输出。我错过了什么吗?

12 个答案:

答案 0 :(得分:302)

评估i++++i后,i的新值在两种情况下均相同。增量前和增量之间的差异在于评估表达式本身的结果。

++i递增i并评估为i的新值。

i++评估i的旧值,并递增i

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

  1. 测试条件
  2. 如果为false,则终止
  3. 如果是,请执行正文
  4. 执行增量步骤
  5. 因为(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,它需要使用更昂贵的增量后版本。

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

  1. 您不必考虑变量/对象是否可能具有重载后增量方法(例如在模板函数中)并以不同方式对待它(或忘记以不同方式对待它)。
  2. 一致的代码看起来更好。
  3. 当有人问你“你为什么预先增加?”你将有机会向他们讲述停止问题和theoretical limits of compiler optimization。 :)

答案 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)阻止OPERATIONwhile()循环中执行。 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;

替换