ChatGPT解决这个技术问题 Extra ChatGPT

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

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

我很惊讶有多少答案完全错过了问题的重点。
也许我们应该感到惊讶的是,没有人将问题编辑得更清楚:)
这个问题可能适用于 C、Java、C++、PHP、C#、Javascript、JScript、Objective C:en.wikipedia.org/wiki/Category:C_programming_language_family
此处发布的好答案:stackoverflow.com/a/4706225/214296

T
TT--

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

这甚至不是用户要求的。
A
Adrian Mole

预增量 ++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# 中调用 ++ 的结果未分配给变量或用作复杂表达式的一部分,那么编译器会将 ++ 的前后版本减少为执行等效的代码。
C
Cœur

在 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
V
Vinz

问题是:

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


这实际上是第一个直入主题的答案。谢谢。
这不是最佳答案,因为如果 for 循环正在递增一个复杂对象(不是 int!),则 ++x 的实现可能比 x++ 更快...(请参阅 herbsutter.com/2013/05/13/gotw-2-solution-temporary-objects
J
Johannes Schaub - litb

既然您询问循环中的差异,我想您的意思是

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 递增。


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

一 (++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循环没有实际区别,但它可以在其他情况下发挥作用。


J
Joe Erickson

正如此代码所示(请参阅注释中的已解密 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++;
            }
        }
    }
}

C
Cœur

这是一个 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

}

u
user3304868

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

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


D
David Morton

就在这里。不同之处在于返回值。 "++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. 

C
Christian C. Salvadó

两种情况没有实际区别,“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

T
Tanveer Badar

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

M
Mont Pierce

这让我难以置信,为什么人们可以将 for-loop 中的增量表达式写为 i++。

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

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

或者

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

结果执行没有区别。


这是一个答案,还是一个问题?
既然没关系,为什么有人写 i++ 会让人难以置信呢?有人愿意写 ++i 有什么原因吗?
C
Community

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

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

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

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

两个反对票?我写的有什么问题吗?它与问题有关(尽管它很模糊)。
x
xaddict

在 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 开始。


C
Community

了解 FOR 循环的作用

https://i.stack.imgur.com/LAKa1.png

上图显示 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 的简单版本: FOR (int i) 的第一个参数在循环之前在外部执行。 FOR 的第三个参数(i++ 或 ++i)在循环的最后一行内部执行。 TL:DR:无论是 i++ 还是 ++i,我们都知道,当它们独立时,它们没有区别,只是对它们自己 +1。在学校,他们通常教授 i++ 方式,但也有很多人更喜欢 ++i 方式,原因有几个。注意:过去 i++ 对性能的影响很小,因为它不仅自己加一,而且在寄存器中保持原来的值。但就目前而言,这没有什么区别,因为编译器使加一部分相同。


L
Leonidas

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

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


s
sçuçu

是的,for 循环中的 ++ii++ 之间存在差异,尽管在不寻常的用例中;当 在 for block在循环测试表达式中 使用带有递增/递减运算符的循环变量时,或 与循环变量之一一起使用时。不,它不仅仅是一个语法问题。

因为代码中的 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)
}

这对现有答案增加了什么?
它比我读过的答案更直接地回答了所问的问题。
M
Mohit Tomar

在某些情况下 ++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 的值,但由于一个简单的 ++/-- 运算符,我们更改了值。

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

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


C
Community

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

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


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

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


J
Java Main

我++; ++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