【问题标题】:Difference between pre-increment and post-increment in a loop?循环中预增量和后增量之间的区别?
【发布时间】:2010-10-03 19:46:04
【问题描述】:

for 循环中的++ii++ 有区别吗?它只是一个语法的东西吗?

【问题讨论】:

标签: for-loop language-agnostic post-increment pre-increment


【解决方案1】:

一个 (++i) 是前置增量,一个 (i++) 是后置增量。不同之处在于表达式立即返回什么值。

// Psuedocode
int i = 0;
print i++; // Prints 0
print i; // Prints 1
int j = 0;
print ++j; // Prints 1
print j; // Prints 1

编辑:糟糕,完全忽略了循环方面的事情。当它是“步骤”部分(for(...; ...; ))时,for 循环没有实际区别,但它可以在其他情况下发挥作用。

【讨论】:

    【解决方案2】:

    在 C# 中,在 for 循环中使用时没有区别

    for (int i = 0; i < 10; i++) { Console.WriteLine(i); }
    

    输出与

    相同的东西
    for (int i = 0; i < 10; ++i) { Console.WriteLine(i); }
    

    正如其他人所指出的,一般使用时 i++ 和 ++i 有一个微妙但显着的区别:

    int i = 0;
    Console.WriteLine(i++);   // Prints 0
    int j = 0;
    Console.WriteLine(++j);   // Prints 1
    

    i++ 读取 i 的值,然后将其递增。

    ++i 增加 i 的值然后读取它。

    【讨论】:

    • 结论:与 C++ 中相同的后/前增量语义。
    • @xtofl - 不知道你的意思是什么?我只是碰巧选择了 c# 作为我的示例。
    • 我不认为第一点是相关的。在 for 循环(c# 与否)中,增量部分总是在循环体之后执行。执行后,无论使用后增量还是前增量,都会修改变量。
    • @MatthieuP - 我将问题解读为“在 for 循环中使用 i++ 还是 ++i 是否重要”。答案是“不,它没有”。
    • @JonB 答案中的操作顺序不完全正确。 ++ii++ 都以相同的顺序执行相同的操作:创建 i 的临时副本;增加 temp 值以产生新值(而不是覆盖 temp);将新值存储在i;现在如果是++i,返回的结果就是新值;如果是i++,则返回的结果是临时副本。更详细的答案在这里:stackoverflow.com/a/3346729/3330348
    【解决方案3】:

    是的,有。不同之处在于返回值。 "++i" 的返回值将是 递增 i 之后的值。 "i++" 的返回值将是 before 递增的值。这意味着代码如下所示:

    int a = 0;
    int b = ++a; // a is incremented and the result after incrementing is saved to b.
    int c = a++; // a is incremented again and the result before incremening is saved to c.
    

    因此,a 为 2,b 和 c 均为 1。

    我可以这样重写代码:

    int a = 0; 
    
    // ++a;
    a = a + 1; // incrementing first.
    b = a; // setting second. 
    
    // a++;
    c = a; // setting first. 
    a = a + 1; // incrementing second. 
    

    【讨论】:

      【解决方案4】:

      这两种情况没有实际区别,'i' 会加 1。

      但在表达式中使用时有区别,例如:

      int i = 1;
      int a = ++i;
      // i is incremented by one and then assigned to a.
      // Both i and a are now 2.
      int b = i++;
      // i is assigned to b and then incremented by one.
      // b is now 2, and i is now 3
      

      【讨论】:

        【解决方案5】:

        预增量 ++i 递增 i 的值并计算为新的递增值。

        int i = 3;
        int preIncrementResult = ++i;
        Assert( preIncrementResult == 4 );
        Assert( i == 4 );
        

        后递增 i++ 递增 i 的值并计算为原始未递增的值。

        int i = 3;
        int postIncrementResult = i++;
        Assert( postIncrementtResult == 3 );
        Assert( i == 4 );
        

        在 C++ 中,预增量通常是首选,您可以使用其中任何一个。

        这是因为如果您使用后增量,它可能要求编译器必须生成创建额外临时变量的代码。这是因为要递增的变量的先前值和新值都需要保存在某个地方,因为它们可能在正在评估的表达式的其他地方需要。

        因此,至少在 C++ 中,可能存在指导您选择使用哪一个的性能差异。

        这主要是当被递增的变量是一个用户定义的类型并且覆盖了 ++ 运算符时才会出现问题。对于原始类型(int 等),没有性能差异。但是,值得坚持使用前增量运算符作为指导,除非后增量运算符绝对是必需的。

        还有一些讨论here

        在 C++ 中,如果您使用的是 STL,那么您可能会使用带有迭代器的 for 循环。这些主要具有覆盖的 ++ 运算符,因此坚持预增量是一个好主意。编译器一直在变得更聪明,并且较新的编译器可能能够执行优化,这意味着没有性能差异 - 特别是如果正在递增的类型是在头文件中内联定义的(因为 STL 实现通常是这样),以便编译器可以看到如何该方法被实施,然后可以知道哪些优化可以安全地执行。即便如此,可能仍然值得坚持预增量,因为循环会执行很多次,这意味着小的性能损失很快就会被放大。


        在其他语言(例如 C#)中,++ 运算符不能重载,因此没有性能差异。在循环中用于推进循环变量,前后自增运算符是等价的。

        更正:允许在 C# 中重载 ++。不过,与 C++ 相比,在 C# 中,您似乎不能独立地重载 pre 和 post 版本。因此,我假设如果在 C# 中调用 ++ 的结果没有分配给变量或用作复杂表达式的一部分,那么编译器会将 ++ 的前后版本缩减为执行等效的代码。

        【讨论】:

        • 如果 C++ 被命名为 ++C 表明你可以使用它编写一个优化良好的代码,那不是很好吗..
        • 当结果值显然会被丢弃时,现代编译器难道不应该对此进行优化吗?
        • @che - 当它是一个简单类型时他们会这样做,但是重载 operator++ 的类(例如迭代器)是另一回事。
        • @che:这是个好问题。 C++ 编译器不替换“CustomType++”的原因;与“++自定义类型;”是因为不能保证两个用户定义的函数具有相同的效果。他们应该……但不能保证。
        • @michael.bartnett:好点,在 C# 中重载 ++ 似乎是可用的。不过,与 c++ 相比,在 c# 中,您似乎不能独立地重载 pre 和 post 版本。因此,我假设如果在 C# 中调用 ++ 的结果没有分配给变量或用作复杂表达式的一部分,那么编译器会将 ++ 的前后版本缩减为执行等效的代码。
        【解决方案6】:

        既然你问的是循环中的区别,我猜你的意思是

        for(int i=0; i<10; i++) 
            ...;
        

        在这种情况下,您在大多数语言中没有区别:无论您编写 i++ 还是 ++i,循环的行为都是相同的。在 C++ 中,您可以编写自己的 ++ 运算符版本,并且可以为它们定义单独的含义,如果 i 是用户定义的类型(例如,您自己的类)。

        上面之所以没有关系,是因为你没有使用i++的值。另一件事是当你这样做时

        for(int i=0, a = 0; i<10; a = i++) 
            ...;
        

        现在,不同,因为正如其他人指出的那样,i++ 表示 增量,但计算为以前的值,但 ++i 表示 increment,但计算为i(因此它将计算为新值)。在上述情况下,a 被分配了 i 的前一个值,而 i 递增。

        【讨论】:

        • 在 C++ 中,编译器并不总是可以避免生成临时值,因此首选预增量形式。
        • 正如我所写,如果你有一个用户定义类型的 i,它们可能有不同的语义。但是如果你使用原始类型的 i ,那么它对第一个循环没有影响。因为这是一个与语言无关的问题,所以我想不要写太多关于 C++ 特定的东西。
        【解决方案7】:

        正如@Jon B 所说,for 循环没有区别。

        但是在whiledo...while 循环中,如果您与++ii++ 进行比较,您会发现一些差异

        while(i++ < 10) { ... } //compare then increment
        
        while(++i < 10) { ... } //increment then compare
        

        【讨论】:

        • 两张反对票?我写的有什么问题吗?它与问题有关(虽然含糊不清)。
        【解决方案8】:

        对于i 的用户定义类型,这些运算符可以(但不应该)在循环索引的上下文中具有有意义的不同语义,这可以(但不应该)影响所描述的循环的行为。

        另外,在c++ 中,使用预增量形式 (++i) 通常是最安全的,因为它更容易优化。 (斯科特朗廷beat me to this tidbit。诅咒你,斯科特)

        【讨论】:

        • 后缀的语义应该大于前缀。 -1
        【解决方案9】:

        a++ 被称为后缀。

        a 加 1,返回旧值。

        ++a 被称为前缀。

        a 加 1,返回新值。

        C#:

        string[] items = {"a","b","c","d"};
        int i = 0;
        foreach (string item in items)
        {
            Console.WriteLine(++i);
        }
        Console.WriteLine("");
        
        i = 0;
        foreach (string item in items)
        {
            Console.WriteLine(i++);
        }
        

        输出:

        1
        2
        3
        4
        
        0
        1
        2
        3
        

        foreachwhile 循环取决于您使用的增量类型。使用下面这样的 for 循环没有区别,因为您没有使用 i 的返回值:

        for (int i = 0; i < 5; i++) { Console.Write(i);}
        Console.WriteLine("");
        for (int i = 0; i < 5; ++i) { Console.Write(i); }
        

        0 1 2 3 4
        0 1 2 3 4

        如果使用评估的值,则增量类型变得重要:

        int n = 0;
        for (int i = 0; n < 5; n = i++) { }
        

        【讨论】:

        • 这甚至不是用户要求的。
        【解决方案10】:

        正如这段代码所示(请参阅 cmets 中的已解密 MSIL),C# 3 编译器在 for 循环中不区分 i++ 和 ++i。如果取 i++ 或 ++i 的值,肯定会有区别(这是在 Visutal Studio 2008/Release Build 中编译的):

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        
        namespace PreOrPostIncrement
        {
            class Program
            {
                static int SomethingToIncrement;
        
                static void Main(string[] args)
                {
                    PreIncrement(1000);
                    PostIncrement(1000);
                    Console.WriteLine("SomethingToIncrement={0}", SomethingToIncrement);
                }
        
                static void PreIncrement(int count)
                {
                    /*
                    .method private hidebysig static void  PreIncrement(int32 count) cil managed
                    {
                      // Code size       25 (0x19)
                      .maxstack  2
                      .locals init ([0] int32 i)
                      IL_0000:  ldc.i4.0
                      IL_0001:  stloc.0
                      IL_0002:  br.s       IL_0014
                      IL_0004:  ldsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                      IL_0009:  ldc.i4.1
                      IL_000a:  add
                      IL_000b:  stsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                      IL_0010:  ldloc.0
                      IL_0011:  ldc.i4.1
                      IL_0012:  add
                      IL_0013:  stloc.0
                      IL_0014:  ldloc.0
                      IL_0015:  ldarg.0
                      IL_0016:  blt.s      IL_0004
                      IL_0018:  ret
                    } // end of method Program::PreIncrement             
                     */
                    for (int i = 0; i < count; ++i)
                    {
                        ++SomethingToIncrement;
                    }
                }
        
                static void PostIncrement(int count)
                {
                    /*
                        .method private hidebysig static void  PostIncrement(int32 count) cil managed
                        {
                          // Code size       25 (0x19)
                          .maxstack  2
                          .locals init ([0] int32 i)
                          IL_0000:  ldc.i4.0
                          IL_0001:  stloc.0
                          IL_0002:  br.s       IL_0014
                          IL_0004:  ldsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                          IL_0009:  ldc.i4.1
                          IL_000a:  add
                          IL_000b:  stsfld     int32 PreOrPostIncrement.Program::SomethingToIncrement
                          IL_0010:  ldloc.0
                          IL_0011:  ldc.i4.1
                          IL_0012:  add
                          IL_0013:  stloc.0
                          IL_0014:  ldloc.0
                          IL_0015:  ldarg.0
                          IL_0016:  blt.s      IL_0004
                          IL_0018:  ret
                        } // end of method Program::PostIncrement
                     */
                    for (int i = 0; i < count; i++)
                    {
                        SomethingToIncrement++;
                    }
                }
            }
        }
        

        【讨论】:

          【解决方案11】:

          循环可能有所不同。这是后/前增量的实际应用。

                  int i = 0;
                  while(i++ <= 10) {
                      Console.Write(i);
                  }
                  Console.Write(System.Environment.NewLine);
          
                  i = 0;
                  while(++i <= 10) {
                      Console.Write(i);
                  }
                  Console.ReadLine();
          

          虽然第一个计数到 11 并循环了 11 次,但第二个不计数。

          这主要是在一个简单的 while(x-- > 0) 中使用; - - 循环以迭代例如数组的所有元素(此处不包括 foreach-constructs)。

          【讨论】:

            【解决方案12】:

            ++i 和 i++ 的意义远不止循环和性能差异。 ++i 返回一个左值,而 i++ 返回一个右值。基于此,您可以对 ( ++i ) 做很多事情,但对 ( i++ ) 做不到。

            1- It is illegal to take the address of post increment result. Compiler won't even allow you.
            2- Only constant references to post increment can exist, i.e., of the form const T&.
            3- You cannot apply another post increment or decrement to the result of i++, i.e., there is no such thing as I++++. This would be parsed as ( i ++ ) ++ which is illegal.
            4- When overloading pre-/post-increment and decrement operators, programmers are encouraged to define post- increment/decrement operators like:
            
            T& operator ++ ( )
            {
               // logical increment
               return *this;
            }
            
            const T operator ++ ( int )
            {
                T temp( *this );
                ++*this;
                return temp;
            }
            

            【讨论】:

              【解决方案13】:

              在 javascript 中由于以下 i++ 可能更好用:

              var i=1;
              alert(i++); // before, 1. current, 1. after, 2.
              alert(i); // before, 2. current, 2. after, 2.
              alert(++i); // before, 2. current, 3 after, 3.
              

              虽然数组(我认为全部)和其他一些函数和调用使用 0 作为起点,但您必须将 i 设置为 -1 以使循环在使用 ++i时与数组一起工作>。

              当使用 i++ 时,后面的值将使用增加的值。您可以说 i++ 是人类计数的方式,因为您可以从 0 开始。

              【讨论】:

                【解决方案14】:

                这是一个 Java 示例,字节码、后增量和前增量在字节码中没有区别:

                public class PreOrPostIncrement {
                
                    static int somethingToIncrement = 0;
                
                    public static void main(String[] args) {
                        final int rounds = 1000;
                        postIncrement(rounds);
                        preIncrement(rounds);
                    }
                
                    private static void postIncrement(final int rounds) {
                        for (int i = 0; i < rounds; i++) {
                            somethingToIncrement++;
                        }
                    }
                
                    private static void preIncrement(final int rounds) {
                        for (int i = 0; i < rounds; ++i) {
                            ++somethingToIncrement;
                        }
                    }
                }
                

                现在是字节码(javap -private -c PreOrPostIncrement):

                public class PreOrPostIncrement extends java.lang.Object{
                static int somethingToIncrement;
                
                static {};
                Code:
                0:  iconst_0
                1:  putstatic   #10; //Field somethingToIncrement:I
                4:  return
                
                public PreOrPostIncrement();
                Code:
                0:  aload_0
                1:  invokespecial   #15; //Method java/lang/Object."<init>":()V
                4:  return
                
                public static void main(java.lang.String[]);
                Code:
                0:  sipush  1000
                3:  istore_1
                4:  sipush  1000
                7:  invokestatic    #21; //Method postIncrement:(I)V
                10: sipush  1000
                13: invokestatic    #25; //Method preIncrement:(I)V
                16: return
                
                private static void postIncrement(int);
                Code:
                0:  iconst_0
                1:  istore_1
                2:  goto    16
                5:  getstatic   #10; //Field somethingToIncrement:I
                8:  iconst_1
                9:  iadd
                10: putstatic   #10; //Field somethingToIncrement:I
                13: iinc    1, 1
                16: iload_1
                17: iload_0
                18: if_icmplt   5
                21: return
                
                private static void preIncrement(int);
                Code:
                0:  iconst_0
                1:  istore_1
                2:  goto    16
                5:  getstatic   #10; //Field somethingToIncrement:I
                8:  iconst_1
                9:  iadd
                10: putstatic   #10; //Field somethingToIncrement:I
                13: iinc    1, 1
                16: iload_1
                17: iload_0
                18: if_icmplt   5
                21: return
                
                }
                

                【讨论】:

                  【解决方案15】:

                  他们都增加了数字。 ++i 等价于i = i + 1

                  i++++i 非常相似,但并不完全相同。两者都增加数字,但++i 在计算当前表达式之前增加数字,而i++ 在计算表达式后增加数字。

                  int i = 3;
                  int a = i++; // a = 3, i = 4
                  int b = ++a; // b = 4, a = 
                  

                  检查this link

                  【讨论】:

                    【解决方案16】:

                    如果不使用循环中递增后的值,则没有区别。

                    for (int i = 0; i < 4; ++i){
                    cout<<i;       
                    }
                    for (int i = 0; i < 4; i++){
                    cout<<i;       
                    }
                    

                    两个循环都将打印 0123。

                    但是当您在循环中使用递增/递减后的值时,区别就来了,如下所示:

                    预增量循环:

                    for (int i = 0,k=0; i < 4; k=++i){
                    cout<<i<<" ";       
                    cout<<k<<" "; 
                    }
                    

                    输出: 0 0 1 1 2 2 3 3

                    后增量循环:

                    for (int i = 0, k=0; i < 4; k=i++){
                    cout<<i<<" ";       
                    cout<<k<<" "; 
                    }
                    

                    输出: 0 0 1 0 2 1 3 2

                    我希望通过比较输出可以清楚地看出差异。这里需要注意的是,递增/递减总是在for循环结束时执行,因此可以解释结果。

                    【讨论】:

                      【解决方案17】:

                      我不知道其他语言,但在 Java 中 ++i前缀增量,这意味着:将 i 增加 1,然后在 i 所在的表达式中使用 i 的新值,并且 i++后缀增量,其含义如下:使用当前值i 在表达式中,然后将其增加 1。 示例:

                      public static void main(String [] args){
                      
                          int a = 3;
                          int b = 5;
                          System.out.println(++a);
                          System.out.println(b++);
                          System.out.println(b);
                      

                      } 输出是:

                      • 4
                      • 5
                      • 6

                      【讨论】:

                        【解决方案18】:

                        问题是:

                        for循环中的++i和i++有区别吗?

                        答案是:

                        为什么每个其他答案都必须详细解释前后递增,而这甚至没有被问到?

                        这个for循环:

                        for (int i = 0; // Initialization
                             i < 5;     // Condition
                             i++)       // Increment
                        {
                           Output(i);
                        }
                        

                        在不使用循环的情况下转换为这段代码:

                        int i = 0; // Initialization
                        
                        loopStart:
                        if (i < 5) // Condition
                        {
                           Output(i);
                        
                           i++ or ++i; // Increment
                        
                           goto loopStart;
                        }
                        

                        现在是否将i++++i 作为增量放在此处? 不,它没有,因为增量操作的返回值是微不足道的。 i 将在 for 循环体内的代码执行后递增。

                        【讨论】:

                        【解决方案19】:

                        这让我大吃一惊,为什么人们可以将 for 循环中的增量表达式写为 i++。

                        在for循环中,当第三个组件是一个简单的增量语句时,如

                        for (i=0; i<x; i++)  
                        

                        for (i=0; i<x; ++i)   
                        

                        执行结果没有区别。

                        【讨论】:

                        • 是答案还是问题?
                        • 既然没关系,为什么有人写了 i++ 会让人难以置信呢?有人喜欢写 ++i 有什么原因吗?
                        【解决方案20】:

                        i++ ; ++i ;两者相似,因为它们没有在表达式中使用。

                        class A {
                        
                             public static void main (String []args) {
                        
                             int j = 0 ;
                             int k = 0 ;
                             ++j;
                             k++;
                            System.out.println(k+" "+j);
                        
                        }}
                        
                        prints out :  1 1
                        

                        【讨论】:

                          【解决方案21】:

                          是的,++ii++for 循环中是有区别的,尽管在不寻常的用例中; 在for块中在循环测试表达式中使用带有递增/递减运算符的循环变量,或使用循环变量之一 .不,这不仅仅是语法问题。

                          正如代码中的i 表示对表达式i 求值,而运算符并不表示求值,而只是一个操作;

                          • ++i 表示将 i 的值递增 1,然后计算 i
                          • i++ 表示评估 i,然后将 i 的值增加 1。

                          因此,从每两个表达式获得的结果不同,因为每个表达式的评估结果不同。 --ii-- 都一样

                          例如;

                          let i = 0
                          
                          i++ // evaluates to value of i, means evaluates to 0, later increments i by 1, i is now 1
                          0
                          i
                          1
                          ++i // increments i by 1, i is now 2, later evaluates to value of i, means evaluates to 2
                          2
                          i
                          2
                          

                          在不寻常的用例中,但是下一个示例听起来有用与否无关紧要,它显示了差异

                          for(i=0, j=i; i<10; j=++i){
                              console.log(j, i)
                          }
                          
                          for(i=0, j=i; i<10; j=i++){
                              console.log(j, i)
                          }
                          

                          【讨论】:

                          • 这对现有答案有什么好处?
                          • 它比我读过的答案更直接地回答了所问的问题。
                          【解决方案22】:

                          了解 FOR 循环的作用

                          上图显示 FOR 可以转换为 WHILE,因为它们最终具有完全相同的汇编代码(至少在 gcc 中)。因此,我们可以将 FOR 分解为几部分,以了解它的作用。

                          for (i = 0; i < 5; ++i) {
                            DoSomethingA();
                            DoSomethingB();
                          }
                          

                          等于WHILE版本

                          i = 0; //first argument (a statement) of for
                          while (i < 5 /*second argument (a condition) of for*/) {
                            DoSomethingA();
                            DoSomethingB();
                            ++i; //third argument (another statement) of for
                          }
                          

                          这意味着您可以将 FOR 用作 WHILE 的简单版本:

                          1. FOR (int i) 的第一个参数在循环之前执行。

                          2. FOR 的第三个参数(i++ 或 ++i)在循环的最后一行内部执行。

                          TL:DR:无论是i++ 还是++i,我们都知道,当它们独立时,它们没有任何区别,只是对自己+1。

                          在学校,他们通常教 i++ 方式,但也有很多人喜欢 ++i 方式,因为several reasons

                          注意:过去,i++对性能的影响很小,因为它不仅自己加一,而且在寄存器中保持原来的值。但就目前而言,这并没有什么区别,因为编译器使加一部分相同。

                          【讨论】:

                            【解决方案23】:

                            在某些情况下 ++i 和 i+1 可能会给出不同的结果,--i、i - 1 等也是如此。

                            这并不是因为递增和递减运算符的工作方式存在缺陷,而是因为新程序员有时会忽略一个小事实。

                            根据经验,不要在数组的方括号内使用 inc/dec。例如,我不会用 arr[++i] 代替 arr[i + 1]。虽然两者都会得到相同的 i 值,但我们在这里忽略了一些东西。

                            如果循环条件基于 i 的值执行,则将 arr[i + 1] 替换为 arr[++i] 将导致错误。为什么?

                            假设 i = 5,那么 arr[i + 1] 将表示 arr[6] 和 arr[++i] 虽然表示 arr[6] 但也会将 i 的值更改为 6,这可能不会成为我们想做的事情。我们可能不想更改 i 的值,但由于一个简单的 ++/-- 运算符,我们更改了值。

                            所以在使用 ++/-- 运算符时要小心。

                            我希望我能够让我的观点易于理解。

                            【讨论】:

                              猜你喜欢
                              • 2013-06-26
                              • 1970-01-01
                              • 1970-01-01
                              • 2018-11-11
                              • 1970-01-01
                              相关资源
                              最近更新 更多