Разница между i ++ и ++ i в цикле? - PullRequest
277 голосов
/ 27 января 2009

Есть ли разница в ++i и i++ в цикле for? Это просто синтаксис?

Ответы [ 21 ]

215 голосов
/ 27 января 2009

a ++ известен как постфикс.

добавить 1 к a, вернуть старое значение.

++ a известен как префикс.

добавляет 1 к a, возвращает новое значение.

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
Циклы

foreach и while зависят от того, какой тип приращения вы используете. С циклами 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++) { }
206 голосов
/ 27 января 2009

Предварительное увеличение ++ 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 и т. Д.) Нет разницы в производительности. Но стоит руководствоваться оператором предварительного увеличения в качестве ориентира, если только оператор последующего приращения определенно не является тем, что требуется.

Здесь есть еще несколько дискуссий:
https://web.archive.org/web/20170405054235/http://en.allexperts.com/q/C-1040/Increment-operators.htm

В C ++, если вы используете STL, вы можете использовать циклы for с итераторами. В основном они имеют переопределенные операторы ++, поэтому придерживаться предварительного увеличения - хорошая идея. Тем не менее, компиляторы становятся все умнее, а более новые могут выполнять оптимизацию, что означает отсутствие разницы в производительности, особенно если тип приращения определен встроенным в заголовочном файле (как это часто бывает с реализациями STL), так что компилятор может видеть, как метод реализован и может знать, какие оптимизации безопасно выполнить. Несмотря на это, вероятно, все еще стоит придерживаться предварительного увеличения, поскольку циклы выполняются много раз, и это означает, что небольшое ухудшение производительности может вскоре усилиться.


В других языках, таких как C #, где оператор ++ не может быть перегружен, разницы в производительности нет. Используемые в цикле для продвижения переменной цикла, операторы до и после приращения эквивалентны.

Исправление: перегрузка ++ в C # разрешена. Тем не менее, кажется, что по сравнению с C ++, в C # вы не можете перегружать предварительную и последующую версии независимо. Итак, я бы предположил, что если результат вызова ++ в C # не будет назначен переменной или использован как часть сложного выражения, то компилятор сократит предварительную и последующую версии ++ до кода, который работает эквивалентно.

77 голосов
/ 27 января 2009

В C # нет разницы при использовании в цикле for .

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

выводит то же самое, что и

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

Как уже отмечали другие, при использовании в целом i ++ и ++ у меня есть небольшая, но существенная разница:

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

i ++ читает значение i, затем увеличивает его.

++ i увеличивает значение i, затем читает его.

36 голосов
/ 30 марта 2016

Вопрос:

Есть ли разница в ++ i и i ++ в цикле for?

Ответ: Нет .

Почему каждый ответ должен входить в подробные объяснения до и после увеличения, когда об этом даже не спрашивают?

Это цикл 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.

29 голосов
/ 27 января 2009

Поскольку вы спрашиваете о разнице в цикле, я думаю, вы имеете в виду

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 увеличивается.

15 голосов
/ 27 января 2009

Как показывает этот код (см. MSIL в комментариях), компилятор C # 3 не делает различий между i ++ и ++ i в цикле for. Если взять значение 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 голосов
/ 27 января 2009

Один (++ 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

Edit: Woops, полностью игнорируется сторона цикла вещей. Фактически нет разницы в циклах for, когда это часть 'step' (for (...; ...;)), но она может вступить в игру в других случаях.

8 голосов
/ 28 января 2009

Вот пример 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

}
6 голосов
/ 28 июня 2015

Нет разницы, если вы не используете значение после приращения в цикле.

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 и, следовательно, результаты могут быть объяснены.

5 голосов
/ 27 января 2009

Да, есть. Разница в возвращаемом значении. Возвращаемое значение "++ 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. 
...