Quelle est la différence entre i ++ et ++ i?

Je les ai vus tous les deux être utilisés dans de nombreux morceaux de code C #, et j’aimerais savoir quand utiliser i++ ou ++i (étant une variable numérique comme int , float , double , etc.). Quelqu’un qui le sait?

Curieusement, il semblerait que les deux autres réponses ne l’expliquent pas clairement, et cela vaut la peine de le dire:


i++ signifie «dites-moi la valeur de i , puis augmentez»

++i veux dire ‘incrémente i , alors dis-moi la valeur’


Ce sont des opérateurs de pré-incrémentation, post-incrémentation. Dans les deux cas, la variable est incrémentée , mais si vous deviez prendre la valeur des deux expressions exactement dans les mêmes cas, le résultat sera différent.

La réponse typique à cette question, malheureusement publiée ici déjà, est que l’incrément “avant” rest les opérations restantes et que l’autre fait l’incrément “après” les opérations restantes. Bien que cette idée soit intuitive, cette affirmation est complètement erronée . La séquence des événements dans le temps est extrêmement bien définie en C #, et ce n’est absolument pas le cas si les versions préfixes et postfixes de ++ font les choses dans un ordre différent de celui des autres opérations.

Il n’est pas surprenant que vous verrez beaucoup de mauvaises réponses à cette question. Un grand nombre de livres “enseignez-vous C #” se trompent aussi. En outre, la manière dont C # le fait est différente de la façon dont C le fait. Beaucoup de gens raisonnent comme si C # et C étaient la même langue; ils ne sont pas. La conception des opérateurs d’incrémentation et de décrémentation en C # évite à mon avis les défauts de conception de ces opérateurs en C.

Il faut répondre à deux questions pour déterminer exactement le fonctionnement du préfixe et du postfix ++ en C #. La première question est quel est le résultat? et la deuxième question est de savoir quand l’effet secondaire de l’incrémentation se produit?

La réponse à l’une ou l’autre de ces questions n’est pas évidente, mais elle est en réalité assez simple une fois que vous la voyez. Laissez-moi vous expliquer précisément ce que x ++ et ++ x font pour une variable x.

Pour le formulaire de préfixe:

  1. x est évalué pour produire la variable
  2. La valeur de la variable est copiée dans un emplacement temporaire
  3. La valeur temporaire est incrémentée pour produire une nouvelle valeur (sans écraser le temporaire!)
  4. La nouvelle valeur est stockée dans la variable
  5. Le résultat de l’opération est la nouvelle valeur (c’est-à-dire la valeur incrémentée du temporaire)

Pour la forme postfixe:

  1. x est évalué pour produire la variable
  2. La valeur de la variable est copiée dans un emplacement temporaire
  3. La valeur temporaire est incrémentée pour produire une nouvelle valeur (sans écraser le temporaire!)
  4. La nouvelle valeur est stockée dans la variable
  5. Le résultat de l’opération est la valeur du temporaire

Quelques choses à noter:

Premièrement, l’ordre des événements dans le temps est exactement le même dans les deux cas . Encore une fois, l’ ordre des événements dans le temps ne change pas entre le préfixe et le postfixe. Il est tout à fait faux de dire que l’évaluation a lieu avant d’autres évaluations ou après d’autres évaluations. Les évaluations se déroulent exactement dans le même ordre dans les deux cas, comme vous pouvez le voir avec les étapes 1 à 4 identiques. La seule différence est la dernière étape – que le résultat soit la valeur du temporaire ou la nouvelle valeur incrémentée.

Vous pouvez facilement le démontrer avec une simple application de console C #:

 public class Application { public static int currentValue = 0; public static void Main() { Console.WriteLine("Test 1: ++x"); (++currentValue).TestMethod(); Console.WriteLine("\nTest 2: x++"); (currentValue++).TestMethod(); Console.WriteLine("\nTest 3: ++x"); (++currentValue).TestMethod(); Console.ReadKey(); } } public static class ExtensionMethods { public static void TestMethod(this int passedInValue) { Console.WriteLine("Current:{0} Passed-in:{1}", Application.currentValue, passedInValue); } } 

Voici les résultats…

 Test 1: ++x Current:1 Passed-in:1 Test 2: x++ Current:2 Passed-in:1 Test 3: ++x Current:3 Passed-in:3 

Dans le premier test, vous pouvez voir que currentValue et ce qui a été transmis à l’extension TestMethod() affichent la même valeur, comme prévu.

Cependant, dans le second cas, les utilisateurs essaieront de vous dire que l’incrément de currentValue se produit après l’appel de TestMethod() , mais comme vous pouvez le voir sur les résultats, cela se produit avant l’appel, comme indiqué par le ‘Current: 2’ résultat.

Dans ce cas, d’abord la valeur de currentValue est stockée dans un temporaire. Ensuite, une version incrémentée de cette valeur est stockée dans currentValue mais sans toucher au temporaire qui stocke toujours la valeur d’origine. Enfin, ce temporaire est passé à TestMethod() . Si l’incrément se produisait après l’appel à TestMethod() il écrirait la même valeur non incrémentée deux fois, mais ce n’est pas le cas.

Il est important de noter que la valeur renvoyée par les deux opérations currentValue++ et ++currentValue est basée sur la valeur temporaire et non la valeur réelle stockée dans la variable au moment de la ++currentValue opération.

Rappel dans l’ordre des opérations ci-dessus, les deux premières étapes copient la valeur alors en cours de la variable dans le temporaire. C’est ce qui est utilisé pour calculer la valeur de retour; dans le cas de la version préfixe, c’est cette valeur temporaire qui est incrémentée alors que dans le cas de la version suffixe, c’est cette valeur directement / non incrémentée. La variable elle-même n’est pas relue après le stockage initial dans le temporaire.

En d’autres termes, la version postfixe renvoie la valeur lue dans la variable (c.-à-d. La valeur du temporaire) tandis que la version préfixe renvoie la valeur qui a été réécrite dans la variable (la valeur incrémentée du temporaire). Ni retourne la valeur de la variable.

Ceci est important à comprendre car la variable elle-même peut être volatile et avoir changé sur un autre thread, ce qui signifie que la valeur de retour de ces opérations peut différer de la valeur actuelle stockée dans la variable.

Il est étonnamment fréquent que les gens soient très confus au sujet de la préséance, de l’associativité et de l’ordre dans lequel les effets secondaires sont exécutés, surtout parce qu’il est si déroutant que C. C # a été soigneusement conçu pour être moins déroutant. Pour une parsing supplémentaire de ces problèmes, y compris moi, démontrant encore plus la fausseté de l’idée que les opérations de préfixe et de postfixe «déplacent les choses dans le temps», voir:

http://blogs.msdn.com/b/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx

ce qui a conduit à cette question SO:

int [] arr = {0}; int value = arr [arr [0] ++]; Valeur = 1?

Vous pourriez également être intéressé par mes articles précédents sur le sujet:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx

et

http://blogs.msdn.com/b/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx

et un cas intéressant où C rend difficile de raisonner sur l’exactitude:

http://blogs.msdn.com/b/ericlippert/archive/2005/04/28/bad-recursion-revisited.aspx

En outre, nous rencontrons des problèmes subtils similaires lorsque nous considérons d’autres opérations ayant des effets secondaires, telles que les affectations simples enchaînées:

http://blogs.msdn.com/b/ericlippert/archive/2010/02/11/chaining-simple-assignments-is-not-so-simple.aspx

Et voici un article intéressant sur les raisons pour lesquelles les opérateurs d’incrémentation produisent des valeurs en C # plutôt que dans des variables :

Pourquoi je ne peux pas faire ++ i ++ dans des langages de type C?

Si tu as:

 int i = 10; int x = ++i; 

alors x sera 11 .

Mais si vous avez:

 int i = 10; int x = i++; 

alors x sera 10 .

Notez qu’Eric souligne que l’incrément se produit en même temps dans les deux cas, mais c’est la valeur qui est donnée comme résultat différent (merci Eric!).

En général, j’aime utiliser ++i moins qu’il y ait une bonne raison de ne pas le faire. Par exemple, lorsque j’écris une boucle, j’aime utiliser:

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

Ou, si j'ai juste besoin d'incrémenter une variable, j'aime utiliser:

 ++x; 

Normalement, d'une manière ou d'une autre, cela n'a pas beaucoup de sens et revient au style de codage, mais si vous utilisez les opérateurs dans d'autres affectations (comme dans mes exemples originaux), il est important de connaître les effets secondaires potentiels.

L’opérateur travaille de la même manière qu’il s’incrémente en même temps, mais s’il est avant une variable, l’expression sera évaluée avec la variable incrémentée / décrémentée:

 int x = 0; //x is 0 int y = ++x; //x is 1 and y is 1 

Si c’est après la variable, l’instruction courante sera exécutée avec la variable d’origine, comme si elle n’avait pas encore été incrémentée / décrémentée:

 int x = 0; //x is 0 int y = x++; //'y = x' is evaluated with x=0, but x is still incremented. So, x is 1, but y is 0 

Je suis d’accord avec dcp en utilisant pré-incrémentation / décrémentation (++ x), sauf si nécessaire. Vraiment, la seule fois où j’utilise le post-incrémentation / décrémentation est dans les boucles while ou les boucles de ce type. Ces boucles sont les mêmes:

 while (x < 5) //evaluates conditional statement { //some code ++x; //increments x } 

ou

 while (x++ < 5) //evaluates conditional statement with x value before increment, and x is incremented { //some code } 

Vous pouvez également faire cela en indexant les tableaux et autres:

 int i = 0; int[] MyArray = new int[2]; MyArray[i++] = 1234; //sets array at index 0 to '1234' and i is incremented MyArray[i] = 5678; //sets array at index 1 to '5678' int temp = MyArray[--i]; //temp is 1234 (becasue of pre-decrement); 

Etc...

Juste pour le compte rendu, en C ++, si vous pouvez utiliser l’un ou l’autre (c.-à-d. Vous ne vous souciez pas de l’ordre des opérations), l’opérateur de préfixe est plus efficace puisqu’il ne le fait pas. créer une copie temporaire de l’object. Malheureusement, la plupart des gens utilisent posfix (var ++) au lieu du préfixe (++ var), simplement parce que c’est ce que nous avons appris initialement. (J’ai été interrogé à ce sujet dans une interview). Je ne sais pas si cela est vrai en C #, mais je suppose que ce serait le cas.

 int i = 0; Console.WriteLine(i++); // Prints 0. Then value of "i" becomes 1. Console.WriteLine(--i); // Value of "i" becomes 0. Then prints 0. 

Est-ce que cela répond à votre question ?