循环中的前增量和后增量之间的区别?


303

是否有差异++i,并i++在一个for循环?这仅仅是语法吗?



18
我惊讶于有多少答案完全没有回答问题的要点。
Graeme Perrow

3
也许我们应该感到惊讶的是,没有人将问题编辑得更清楚:)
Jon B

2
这个问题可能适用于C,Java,C ++,PHP,C#,Javascript,JScript,目标C:en.wikipedia.org/wiki/Category
Chris S

1
好答案在这里发布:stackoverflow.com/a/4706225/214296
Jim Fell

Answers:


233

a ++被称为后缀。

将a加1,则返回旧值。

++ a被称为前缀。

将1加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++) { }

4
这甚至不是用户要求的。
Dimitri

223

预递增++ ii的值递增,并求值为新的递增值。

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等),没有性能差异。但是,值得坚持使用前递增运算符作为准则,除非绝对需要后递增运算符。

这里还有更多讨论:https : //web.archive.org/web/20170405054235/http :
//en.allexperts.com/q/C-1040/Increment-operators.htm

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


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

更正:允许在C#中重载++。但是,与C ++相比,在c#中似乎无法独立地重载pre和post版本。因此,我假设如果在C#中调用++的结果未分配给变量或用作复杂表达式的一部分,则编译器会将++的pre和post版本缩减为等效执行的代码。


102
那岂不是一直伟大的,如果C ++被评为++ C表示要您可以用它写一个很好的优化代码..
纳文

9
当结果值显然将要被废弃时,现代的编译器难道不应该对此进行优化吗?

6
@che-它们在简单类型时会执行,但是重载operator ++的类(例如迭代器)则不同。
Ferruccio

7
@che:这是一个好问题。C ++编译器不替换“ CustomType ++;”的原因 与“ ++ CustomType;” 这是因为不能保证两个用户定义的函数都具有相同的作用。他们应该...但是不能保证。
德鲁·多曼

2
@ michael.bartnett:很好,C#中的++过载似乎确实可用。但是,与c ++相比,在c#中似乎无法独立地重载pre和post版本。因此,我假设如果在C#中调用++的结果未分配给变量或用作复杂表达式的一部分,则编译器会将++的pre和post版本缩减为等效执行的代码。
Scott Langham

83

在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

@xtofl-不确定您的意思是什么?我只是碰巧选择了C#作为示例。
Jon B

3
我认为第一点无关紧要。在for循环中(无论是否为c#),增量部分始终在循环主体之后执行。一旦执行,无论使用后增量还是前增量,都将修改变量。
MatthieuP

9
@MatthieuP-我读到的问题是“在for循环中使用i ++还是++ i都无关紧要”。答案是“不,不”。
Jon B

1
@JonB答案中的操作顺序不完全正确。两者++ii++以相同的顺序执行相同的操作:创建的临时副本i;增加温度值以产生新值(不覆盖温度);将新值存储在i; 现在,如果++i返回的结果是新值;如果i++返回的结果是临时副本。更详细的答案在这里:stackoverflow.com/a/3346729/3330348
PiotrWolkowski

51

问题是:

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循环体内执行代码后增加。


2
从字面上看,这是最直接的答案。谢谢。
Yassir

1
这不是最佳答案,因为如果for循环正在递增一个复杂的对象(不是int的东西!),则++ x的实现可能比x ++更快。(请参阅herbutter.com/2013/05/13/gotw -2-solution-temporary-objects
JCx

30

既然你问一个循环的区别,我想你的意思是

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

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

上面无关紧要的原因是因为您没有使用的值i++。另一件事是当你做

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

现在,有有差别,因为其他人指出,i++手段增加,但评估的前值,但++i手段增加,但计算结果为i(因此它会评估为新值)。在上述情况下a,在i递增的同时,为其分配了i的先前值。


3
在C ++中,并非总是能够使编译器避免使用临时格式,因此首选预递增格式。
David Thornley,2009年

如我所写,如果您具有用户定义类型的i,则它们可能具有不同的语义。但是,如果您使用基本类型的i,那么对于第一个循环就没有任何区别。因为这是一个与语言无关的问题,所以我认为不要对C ++特定的东西写太多。
Johannes Schaub-litb

15

如该代码所示(请参见注释中讨论过的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++;
            }
        }
    }
}

14

一个(++ 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

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


7

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

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循环的末尾执行,因此可以解释结果。


7

这是一个Java样本,字节码,post和preIncrement在字节码上没有区别:

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

}

5

就在这里。区别在于返回值。返回值“ ++ i”将是递增i 的值。返回的“ i ++”将是递增之前的值。这意味着该代码如下所示:

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

3

++ i和i ++除了循环和性能差异之外,还有更多功能。++ i返回一个l值,而i ++返回一个r值。基于此,您可以对(++ 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;
}

3

这让我感到困惑,为什么人们可能会像i ++一样在for循环中编写增量表达式。

在for循环中,当第三个组成部分是简单的增量语句时,如下所示:

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

要么

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

执行的结果没有差异。


是答案还是问题?
Palec

2
既然没有关系,那么为什么有人写i ++会让您大吃一惊?有某些原因为什么有人更喜欢写++ i?
Dronz

2

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

但是在whileor do...while循环中,如果与++ior 进行比较,您可能会发现一些差异。i++

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

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

两张否决票?我写的东西有什么问题?它与问题有关(因为它含糊不清)。
crashmstr

2

在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时,您必须将i设置为-1才能使循环与数组一起工作。

使用i ++时,以下值将使用增加的值。您可能会说i ++是人类计算的方式,因为您可以从0开始。


2

了解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方式。

注意:过去,i ++对性能的影响很小,因为它不仅会单独加一,而且还会将原始值保留在寄存器中。但是就目前而言,这没有什么区别,因为编译器使加号部分相同。


1

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

        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)。


1

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

i++并且++i非常相似,但不完全相同。两者都会增加数字,但是会++i增加对当前表达式求值之前的数字,而会i++增加对表达式求值之后的数字。

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

检查此链接


0

是的,有之间的差异++i,并i++在一个for虽然不寻常的使用情况循环; 在for块中在循环测试表达式中,或在其中一个循环变量中使用带有增量/减量运算符的循环变量时。不,这不仅仅是语法问题。

就像i在代码中一样,它意味着对表达式进行求值,i而运算符并不意味着对求值,而仅仅意味着一个运算。

  • ++i表示将值增加i1,然后求值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)
}

这与现有答案相加了什么?
GManNickG

它比我已阅读的答案更直接地回答所要询问的内容。
塞尔丘克

-2

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

同样,c++使用预增量形式(++i)通常最安全,因为它更容易优化。(斯科特·朗厄姆击败了我,这件事使我大受诅咒。)


后缀的语义应该大于前缀。-1
xtofl

-2

我不知道其他语言,但是在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

-3

我++; ++ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.