首页 文章

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

提问于
浏览
164

这个问题在这里已有答案:

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

这是代码:

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

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

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

12 回答

  • 285

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

    ++i 递增 i 并计算为 i 的新值 .

    i++ 求值为 i 的旧值,并递增 i .

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

    • 测试条件

    • 如果为false,则终止

    • 如果是,则执行正文

    • 执行增量步骤

    因为(1)和(4)是去耦的,所以可以使用前增量或后增量 .

  • 85

    嗯,这很简单 . 上面的 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 的值在递增之前 .

  • 5

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

    但是, 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 . :)

  • -1

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

    Solution:

    答案是两个片段都打印从0到4的数字,包括0和4 . 这是因为 for() 循环通常等同于 while() 循环:

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

    可写:

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

    您可以看到OPERATION始终在循环的底部完成 . 在这种形式中,应该清楚 i++++i 将具有相同的效果:它们都将递增 i 并忽略结果 . i 的新值直到下一次迭代开始时才会在循环顶部进行测试 .


    编辑:感谢Jason指出如果循环包含控制语句(例如 continue )会阻止 OPERATIONwhile() 循环中执行,则 for()while() 等价不成立 . OPERATION 始终在 for() 循环的下一次迭代之前执行 .


    Why it's a Good Interview Question

    首先,如果候选人立即告诉正确答案,则只需一两分钟,因此我们可以直接进入下一个问题 .

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

    在这种情况下,我要求他们使用 while() 重写循环,这真的给了我一个他们思想过程的好主意 . 这就是为什么我首先提出这个问题的原因:我想知道他们如何解决问题,以及当我对他们的世界运作方式产生怀疑时他们如何处理 .

    此时,大多数候选人意识到他们的错误并找到了正确的答案 . 但我有一个人坚持他的原始答案是正确的,然后改变了他将 for() 翻译成 while() 的方式 . 这是一次精彩的采访,但我们没有提出要约!

    希望有所帮助!

  • -3

    因为在任何一种情况下,增量都是在循环体之后完成的,因此不会影响循环的任何计算 . 如果编译器是愚蠢的,使用后增量可能效率稍低(因为通常它需要保留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
    
  • 1

    如果你这样写,那么它很重要:

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

    比这样编写的迭代次数更多:

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

    您可以在此处阅读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;
    }
    
  • 2

    i和i都在printf(“%d”,i)每次执行后执行,因此没有区别 .

  • 4

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

    在这种情况下,后增量或预增量很重要:

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

    您可以通过分配或传递参数来提供某些值 . 你在 for 循环中都没有 . 它只会增加 . 后期和之前没有意义!

  • 1

    for构造中的第三个语句仅执行,但其评估值将被丢弃而不予处理 .
    当评估值被丢弃时,前后增量相等 .
    它们只有在取得 Value 时才会有所不同 .

  • 104

    如果:

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

    结果:

    内部帖子incement = 0

    i =循环0中的后增量

    内部帖子incement = 1

    i =循环1中的后增量

    第二个for循环:

    在pre pre incement = 0

    i =循环1中的预增量

    在pre pre incement = 1

    i =循环2中的预增量

  • 1

    编译器翻译

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

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

    所以在你的情况下(后期/预增量)并不重要 .

    编辑:继续简单地被 goto end; 取代

相关问题