35 votes

Post-incrément dans une auto-affectation

Je comprends les différences entre i++ and ++i, mais je ne suis pas tout à fait sûr pourquoi je reçois les résultats ci-dessous:

static void Main(string[] args)
{
    int c = 42;
    c = c++;
    Console.WriteLine(c);   //Output: 42
}

Dans le code ci-dessus, que c'est de l'affectation de la variable elle-même, puis incrémentation de la valeur, je m'attends à ce que le résultat soit 43. Cependant, il est de retour 42. J'obtiens le même résultat lors de l'utilisation d' c = c--; ainsi.

Je me rends compte que je pourrait tout simplement utiliser c++; et être fait avec elle, mais je suis plus curieux de savoir pourquoi elle se comporte de la façon dont il est. Quelqu'un peut-il expliquer ce qui se passe ici?

34voto

poke Points 64398

Jetons un coup d'oeil à l'intermédiaire le code de langue pour que:

IL_0000:  nop
IL_0001:  ldc.i4.s    2A
IL_0003:  stloc.0     // c
IL_0004:  ldloc.0     // c

Cela charge la constante de type entier 42 sur la pile, puis les stocke dans la variable c, et le charge à nouveau sur la pile.

IL_0005:  stloc.1
IL_0006:  ldloc.1

Cette fonction permet de copier la valeur dans un autre registre, et aussi beaucoup de nouveau.

IL_0007:  ldc.i4.1
IL_0008:  add

Cela ajoute de la constante de 1 chargé de la valeur

IL_0009:  stloc.0     // c

... et stocke le résultat (43) dans la variable c.

IL_000A:  ldloc.1
IL_000B:  stloc.0     // c

Ensuite, la valeur de l'autre registre est chargé (c'est encore de la de 42!) et stockée dans la variable c.

IL_000C:  ldloc.0     // c
IL_000D:  call        System.Console.WriteLine
IL_0012:  nop
IL_0013:  ret

Ensuite, la valeur (42) est chargé à partir de la variable, et imprimé.


Donc vous pouvez voir, c'est que tout c++ incrémente la variable par un après le résultat fut de retour, que l'incrémentation se passe toujours avant assinging la valeur de la variable. Si la séquence est plus comme ceci:

  1. Obtenir la valeur de c
  2. La Post-incrémentation c
  3. Attribuer auparavant de lire la valeur de c

Et qu'il faut expliquer pourquoi vous obtenez ce résultat :)


Pour ajouter un exemple de plus, depuis que cela a été mentionné dans un commentaire qui a été depuis supprimée:

c = c++ + c;

Cela fonctionne de façon très similaire: en Supposant une valeur initiale de 2 encore une fois, le côté gauche de l'addition est évalué en premier. Si la valeur est lue à partir de la variable (2), puis c est incrémenté (c devient 3). Puis le côté droit de l'outre-ci est évaluée. La valeur de c est lire (maintenant 3). Ensuite, l'ajout (2 + 3) et le résultat (5) est affectée à la variable.


Les plats à emporter à partir de ce que vous devriez éviter de mélanger d'incrémentation et de décrémentation des opérations normales d'expressions. Certes, le comportement est très bien défini et fait sens absolu (comme illustré ci-dessus), il est encore parfois difficile pour envelopper votre tête autour de lui. Surtout quand vous assigner quelque chose à la même variable que vous incrémenter dans l'expression, cela devient confus rapidement. Donc, ne vous-même et d'autres personnes une faveur et éviter d'incrémenter/décrémenter les opérations lorsqu'ils ne sont pas complètement sur leur propre :)

20voto

CompuChip Points 3381

Selon la page MSDN sur C# les opérateurs l'opérateur d'affectation (=) a priorité moindre que n'importe quel opérateur principal, comme ++x ou x++. Cela signifie que, dans la ligne

c = c++;

le côté droit est évalué en premier. L'expression c++ par incréments c à 43, puis renvoie la valeur d'origine 42 , ce qui est utilisé pour l'affectation.

Comme la documentation liée à des états,

[La seconde forme est un] postfix opération d'incrémentation. Le résultat de l'opération est la valeur de l'opérande avant il a été incrémenté.

En d'autres termes, votre code est équivalent à

// Evaluate the right hand side:
int incrementResult = c;   // Store the original value, int incrementResult = 42
c = c + 1;                 // Increment c, i.e. c = 43

// Perform the assignment:
c = incrementResult;       // Assign c to the "result of the operation", i.e. c = 42

Comparez cela à la forme préfixe

c = ++c;

ce qui permettrait d'évaluer que

// Evaluate the right hand side:
c = c + 1;                 // Increment c, i.e. c = 43
int incrementResult = c;   // Store the new value, i.e. int incrementResult = 43

// Perform the assignment:
c = incrementResult;       // Assign c to the "result of the operation", i.e. c = 43

5voto

Yuval Itzchakov Points 13820

Les docs disent pour postfix état:

Le résultat de l'opération est la valeur de l'opérande avant il a été incrémenté.

Cela signifie que lorsque vous faites:

c = c++;

Vous êtes en train de re-affectation de 42 de c, et c'est pourquoi vous voyez l'console imprimer 42. Mais si vous le faites:

static void Main(string[] args)
{
    int c = 42;
    c++;
    Console.WriteLine(c);  
}

Vous verrez qu'il est sortie 43.

Si vous regardez ce que fait le compilateur génère (en mode Debug), vous allez voir:

private static void Main(string[] args)
{
    int num = 42;
    int num2 = num;
    num = num2 + 1;
    num = num2;
    Console.WriteLine(num);
}

Ce qui montre que la réécriture plus clairement. Si vous regardez en mode Release, vous verrez le compilateur d'optimiser l'ensemble de l'appel à:

private static void Main(string[] args)
{
    Console.WriteLine(42);
}

3voto

buffjape Points 287

L'expression sur le côté droit d'une affectation est évaluée complètement, puis l'affectation est effectuée.

    c = c++;
 

Est le même que

    // Right hand side is calculated first.
   _tmp = c;
   c = c + 1;

   // Then the assignment is performed
   c = _tmp;
 

3voto

Theodore Norvell Points 2470

Je crois que je vois ce que l'original interlocuteur a été pensée. Ils ont pensé (je pense) que post-incrémentation des moyens d'incrémentation de la variable après l'évaluation de l'ensemble de l'expression, par exemple, que

x = a[i++] + a[j++];   // (0)

est le même que

{ x = a[i] + a[j] ; i += 1 ; j += 1; }   // (1)

(et, certes, ce sont l'équivalent) et que

c = c++;  // (2)

moyens

{ c = c ; c +=1 ; } // (3)

et que

x = a[i++] + a[i++];  // (4)

moyens

{ x = a[i] + a[i] ; i += 2 ; } // (5)

Mais ce n'est pas le cas. v++ moyens d'incrémentation v tout de suite, mais l'utilisation de l'ancienne valeur que la valeur de l'expression. Ainsi, dans le cas de (4) en réalité une instruction équivalente est

{int t0 = a[i] ; i += 1 ; int t1 = a[i] ; i += 1 ; x = t0 + t1 ; } // (6)

Comme d'autres l'ont noté, des énoncés comme (2) et (4) sont bien définis en C# (et Java), mais ils ne sont pas bien définis dans le C et le C++.

En C et C++ expressions comme (2) et (4) que le changement d'une variable et également de l'utiliser d'une autre manière sont généralement pas défini, ce qui signifie le compilateur est la bienvenue (dans la mesure où les lois sur la langue go) pour les traduire en quelque sorte à tous, par exemple, de transférer de l'argent de votre compte bancaire pour le compilateur de l'écrivain.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X