web-dev-qa-db-fra.com

Différence entre pré-incrémentation et post-incrémentation dans une boucle?

Existe-t-il une différence entre ++i et i++ dans une boucle for? Est-ce simplement une chose de syntaxe?

288
GurdeepS

a ++ est appelé postfix.

ajouter 1 à a, retourne l'ancienne valeur.

++ a est appelé préfixe.

ajouter 1 à a, retourne la nouvelle valeur.

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

Sortie:

1
2
3
4

0
1
2
3

Les boucles foreach et while dépendent du type d'incrément utilisé. Avec les boucles for comme ci-dessous, cela ne fait aucune différence, car vous n'utilisez pas la valeur de retour de 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

Si la valeur évaluée est utilisée, le type d'incrément devient significatif:

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

Pré-incrémentation ++ i incrémente la valeur de i et est évaluée à la nouvelle valeur incrémentée.

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

Post-incrémentation i ++ incrémente la valeur de i et correspond à la valeur d'origine non incrémentée.

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

En C++, la pré-incrémentation est généralement préférable lorsque vous pouvez utiliser l'un ou l'autre.

En effet, si vous utilisez la post-incrémentation, le compilateur peut être obligé de générer du code créant une variable temporaire supplémentaire. En effet, les valeurs précédentes et nouvelles de la variable en cours d'incrémentation doivent être conservées quelque part car elles peuvent être nécessaires ailleurs dans l'expression en cours d'évaluation.

Donc, en C++ au moins, il peut y avoir une différence de performance qui détermine votre choix d'utilisation.

Cela pose principalement un problème lorsque la variable à incrémenter est un type défini par l'utilisateur avec un opérateur ++ surchargé. Pour les types primitifs (int, etc.), il n'y a pas de différence de performance. Toutefois, il vaut la peine de s’en tenir à l’opérateur de pré-incrémentation à moins que l’opérateur de post-incrément ne soit vraiment ce qu’il faut.

Il y a un peu plus de discussion ici:
https://web.archive.org/web/20170405054235/http://fr.allexperts.com/q/C-1040/Increment-operators.htm

En C++, si vous utilisez STL, vous utilisez peut-être des boucles for avec des itérateurs. Ceux-ci ont principalement des opérateurs ++ surchargés, il est donc judicieux de rester sur la pré-incrémentation. Les compilateurs sont de plus en plus intelligents et les plus récents peuvent effectuer des optimisations sans aucune différence de performances, en particulier si le type à incrémenter est défini en ligne dans le fichier d’en-tête (comme le sont souvent les implémentations STL) afin que le compilateur puisse voir comment. la méthode est mise en œuvre et peut alors savoir quelles optimisations peuvent être effectuées en toute sécurité. Même dans ce cas, il est probablement utile de s'en tenir au pré-incrément, car les boucles sont exécutées de nombreuses fois et cela signifie qu'une légère pénalité de performance pourrait bientôt être amplifiée.


Dans d'autres langages tels que C #, où l'opérateur ++ ne peut pas être surchargé, il n'y a pas de différence de performances. Utilisés dans une boucle pour faire avancer la variable de boucle, les opérateurs de pré-incrémentation et de post-incrémentation sont équivalents.

Correction: la surcharge ++ en C # est autorisée. Il semble cependant que par rapport au C++, en c #, vous ne pouvez pas surcharger les versions antérieure et postérieure de manière indépendante. Donc, je suppose que si le résultat de l'appel de ++ en C # n'est pas affecté à une variable ou utilisé dans le cadre d'une expression complexe, le compilateur réduirait les versions antérieure et postérieure de ++ au code qui fonctionne de manière équivalente.

212
Scott Langham

En C #, il n'y a pas de différence lorsqu'il est utilisé dans une boucle for.

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

sort la même chose que

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

Comme d'autres l'ont fait remarquer, lorsqu'ils sont utilisés en général, i ++ et ++ i présentent une différence subtile mais significative:

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

i ++ lit la valeur de i puis l'incrémente.

++ i incrémente la valeur de i puis la lit.

79
Jon B

La question est:

Existe-t-il une différence entre ++ i et i ++ dans une boucle for?

La réponse est: No.

Pourquoi chaque réponse doit-elle entrer dans des explications détaillées sur l'incrémentation avant et après, alors que cela n'est même pas demandé?

Cette boucle for:

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

Traduirait ce code sans utiliser de boucles:

int i = 0; // Initialization

loopStart:
if (i < 5) // Condition
{
   Output(i);

   i++ or ++i; // Increment

   goto loopStart;
}

Maintenant, est-ce important si vous mettez i++ ou ++i comme incrément ici? Non, ce n'est pas le cas car la valeur de retour de l'opération d'incrémentation est non significative. i sera incrémenté APRÈS l'exécution du code à l'intérieur du corps de la boucle for.

40
Vinz

Puisque vous demandez la différence dans une boucle, je suppose que vous voulez dire

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

Dans ce cas, vous n'avez aucune différence dans la plupart des langues: la boucle se comporte de la même manière que vous écriviez i++ et ++i. En C++, vous pouvez écrire vos propres versions des opérateurs ++, et vous pouvez définir des significations distinctes pour eux, si la i est d'un type défini par l'utilisateur (votre propre classe, par exemple).

La raison pour laquelle cela n'a pas d'importance ci-dessus est que vous n'utilisez pas la valeur de i++. Une autre chose est quand vous faites

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

Maintenant, il y a est une différence, parce que, comme d'autres le soulignent, i++ signifie incrément, mais évalue à la valeur précédente, mais ++i signifie - incrémente, mais évalue à i (ainsi, la valeur sera évaluée). Dans le cas ci-dessus, a se voit attribuer la valeur précédente de i, tandis que i est incrémenté.

30

Comme ce code l'indique (voir le MSIL disséminé dans les commentaires), le compilateur C # 3 ne fait aucune distinction entre i ++ et ++ i dans une boucle for. Si la valeur de i ++ ou ++ i était prise, il y aurait certainement une différence (cela a été compilé dans 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++;
            }
        }
    }
}
15
Joe Erickson

Un (++ i) est préincrémenté, un (i ++) est postincrémenté. La différence réside dans la valeur renvoyée immédiatement par l'expression.

// 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, a complètement ignoré le côté boucle des choses. Il n'y a pas de différence réelle entre les boucles for quand il s'agit de la partie "étape" (pour (...; ...;)), mais cela peut entrer en jeu dans d'autres cas.

14
Cody Brocious

Voici un exemple Java et le Byte-Code, post- et preIncrement ne montrent aucune différence dans le Bytecode:

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

Et maintenant, le byte-code (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

}
7
Mirko Friedenhagen

Il n'y a pas de différence si vous n'utilisez pas la valeur après avoir incrémenté la boucle.

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

Les deux boucles vont imprimer 0123.

Mais la différence vient lorsque vous utilisez la valeur après incrément/décrément dans votre boucle comme ci-dessous:

Boucle de pré-incrémentation:

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

Sortie: 0 0 1 1 2 2 3 3

Boucle post-incrémentation:

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

Sortie: 0 0 1 0 2 1 3 2

J'espère que la différence est claire en comparant les résultats. Il convient de noter ici que l’incrément/décrément est toujours effectué à la fin de la boucle for et que, par conséquent, les résultats peuvent être expliqués.

7
user3304868

Oui il y a. La différence est dans la valeur de retour. La valeur de retour de "++ i" sera la valeur after incrementing i. Le retour de "i ++" sera la valeur avant incrémentée. Cela signifie que le code qui ressemble à ce qui suit:

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.

Par conséquent, a serait 2 et b et c seraient chacun 1.

Je pourrais réécrire le code comme ceci:

int a = 0; 

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

// a++;
c = a; // setting first. 
a = a + 1; // incrementing second. 
5
David Morton

Il n'y a pas de différence réelle dans les deux cas, 'i' sera incrémenté de 1.

Mais il y a une différence lorsque vous l'utilisez dans une expression, par exemple:

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
4
CMS

++ i et i ++ ne se limitent pas aux boucles et aux différences de performances. ++ i renvoie une valeur l et i ++ renvoie une valeur r. Sur cette base, il y a beaucoup de choses que vous pouvez faire pour (++ i) mais pas pour (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
Tanveer Badar

Je ne comprends pas pourquoi les gens peuvent écrire l'expression d'incrémentation dans une boucle for tant que i ++.

Dans une boucle for, lorsque le troisième composant est une simple instruction d'incrémentation, comme dans

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

ou

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

il n'y a pas de différence dans les exécutions résultantes.

3
Mont Pierce

En javascript en raison de ce qui suit i ++ peut être préférable d'utiliser:

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.

Alors que les tableaux (je pense que tous) et quelques autres fonctions et appels utilisent 0 comme point de départ, vous devez définir i sur -1 pour que la boucle fonctionne avec le tableau lors de l'utilisation de ++ i.

Lors de l'utilisation de i ++, la valeur suivante utilisera la valeur augmentée. Vous pourriez dire que i ++ est la façon dont les humains comptent, car vous pouvez commencer avec un .

3
xaddict

Comme @ Jon B dit, il n'y a pas de différence dans une boucle for.

Mais dans une boucle while ou do...while, vous pouvez trouver certaines différences si vous effectuez une comparaison avec le ++i ou i++

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

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

Il peut y avoir une différence pour les boucles. C'est l'application pratique de post/pré-incrémentation.

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

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

Alors que le premier compte jusqu'à 11 et boucle 11 fois, le second ne le fait pas.

Généralement, cela est plutôt utilisé dans un simple while (x--> 0); - - Boucle pour parcourir, par exemple, tous les éléments d'un tableau (en excluant les constructions foreach ici).

1
Leonidas

Ils augmentent tous les deux le nombre. ++i est équivalent à i = i + 1.

i++ et ++i sont très similaires mais pas exactement les mêmes. Les deux incrémentent le nombre, mais ++i incrémente le nombre avant l'évaluation de l'expression actuelle, tandis que i++ incrémente le nombre après l'évaluation de l'expression.

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

Vérifiez ce lien .

1
Boubakr

Oui, il y a une différence entre ++i et i++ dans une boucle for, bien que dans des cas d'utilisation inhabituels; lorsqu'une variable de boucle avec l'opérateur d'incrémentation/décrémentation est utilisée dans le bloc for ou dans l'expression du test de boucle, ou avec l'une des variables de boucle . Non, ce n'est pas simplement une question de syntaxe.

Comme i dans un code, cela signifie évaluer l'expression i et l'opérateur ne signifie pas une évaluation, mais simplement une opération;

  • ++i signifie la valeur d'incrément de i de 1 et plus tard, évaluer i,
  • i++ signifie évaluer i et incrémenter plus tard de i par 1.

Donc, ce qui est obtenu à partir de chacune des deux expressions diffère parce que ce qui est évalué diffère dans chacune. Tous les mêmes pour --i et i--

Par exemple;

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

Dans les cas d'utilisation inhabituels, cependant, l'exemple suivant semble utile ou pas, peu importe, cela montre une différence

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)
}
0
Selçuk