255 votes

Pour i = 0, pourquoi est (j’ai += i ++) égal à 0 ?

Prenez le code suivant (utilisable en tant qu'Application Console):

static void Main(string[] args)
{
    int i = 0;
    i += i++;
    Console.WriteLine(i);
    Console.ReadLine();
}

Le résultat de l' i est de 0. J'ai prévu 2 (comme certains de mes collègues n'ont). Probablement le compilateur crée une sorte de structure qui résultats en i zéro.

La raison que j'ai attendu 2 est que, dans ma ligne de pensée, la main droite déclaration serait évalué en premier, l'incrémentation de i avec 1. Qu'il est ajouté-je. Depuis que j'ai est déjà le 1, il est l'ajout de 1 à 1. Donc 1 + 1 = 2. Évidemment, ce n'est pas ce qui se passe.

Pouvez-vous expliquer ce qu'est le compilateur qui fonctionne ou ce qui se passe au moment de l'exécution? Pourquoi le résultat de zéro?

Quelques-sort-de-disclaimer: je ne suis absolument conscient que vous n'aurez pas (et ne devrait probablement pas) utiliser ce code. Je sais que je ne le fera jamais. Néanmoins, je trouve que c'est intéressant de savoir pourquoi il agit de telle manière, et ce qui se passe exactement.

424voto

Oded Points 271275

Ce:

int i = 0;
i += i++

Peut être que vous en faites (ce qui suit est une simplification grossière):

int i = 0;
i = i + i; // i=0 because the ++ is a postfix operator and hasn't been executed
i + 1; // Note that you are discarding the calculation result

Ce qui se passe en réalité est plus complexe que cela, jetez un oeil sur le site MSDN, 7.5.9 Postfix d'incrémentation et de décrémentation les opérateurs:

Le moment de l'exécution de la transformation d'un postfix d'incrémentation ou de décrémentation de l'opération de la forme x++ ou x-- comprend les étapes suivantes:

  • Si x est classé comme une variable:

    • x est évalué à produire de la variable.
    • La valeur de x est enregistré.
    • L'opérateur sélectionné est appelée avec la valeur enregistrée de x comme argument.
    • La valeur retournée par l'opérateur est stocké à l'emplacement donné par l'évaluation de x.
    • La valeur enregistrée de x devient le résultat de l'opération.

Notez qu'en raison de l'ordre de préséance, le postfix ++ se produit avant +=, mais le résultat finit par être utilisées (comme la précédente valeur de i est utilisé).


Plus approfondie de la décomposition de l' i += i++ à la pièce à laquelle elle est faite, il faut savoir que les deux += et ++ ne sont pas atomiques (qui est, aucun des deux n'est une seule opération), même si on dirait qu'ils sont. La manière dont ils sont mis en œuvre impliquent des variables temporaires, des copies de i avant les opérations de lieu - une pour chaque opération. (Je vais utiliser le nom, l' iAdd et iAssign pour les variables temporaires utilisés pour ++ et += respectivement).

Donc, une meilleure approximation de ce qui se passe serait:

int i = 0;
int iAdd = i; // Copy of the current value of i, for ++
int iAssign = i; // Copy of the current value of i, for +=

i = i + 1; // i++ - Happens before += due to order of precedence
i = iAdd + iAssign;

196voto

Miguel Angelo Points 12471

Le démontage de l'exécution de code:

int i = 0;
  xor         edx, edx
  mov         dword ptr i, edx         // set i = 0
i += i++;
  mov         eax, dword ptr i         // set eax = i (=0)
  mov         dword ptr tempVar1, eax  // set tempVar1 = eax (=0)
  mov         eax, dword ptr i         // set eax = 0 ( again... why??? =\ )
  mov         dword ptr tempVar2, eax  // set tempVar2 = eax (=0)
  inc         dword ptr i              // set i = i+1 (=1)
  mov         eax, dword ptr tempVar1  // set eax = tempVar1 (=0)
  add         eax, dword ptr tempVar2  // set eax = eax+tempVar2 (=0)
  mov         dword ptr i, eax         // set i = eax (=0)

Code équivalent

Il compile sur le même code que le code suivant:

int i, tempVar1, tempVar2;
i = 0;
tempVar1 = i; // created due to postfix ++ operator
tempVar2 = i; // created due to += operator
++i;
i = tempVar1 + tempVar2;

Le démontage de la deuxième code (juste pour prouver qu'ils sont les mêmes)

int i, tempVar1, tempVar2;
i = 0;
    xor         edx, edx
    mov         dword ptr i, edx
tempVar1 = i; // created due to postfix ++ operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar1, eax
tempVar2 = i; // created due to += operator
    mov         eax, dword ptr i
    mov         dword ptr tempVar2, eax
++i;
    inc         dword ptr i
i = tempVar1 + tempVar2;
    mov         eax, dword ptr tempVar1
    add         eax, dword ptr tempVar2
    mov         dword ptr i, eax

Ouverture de démontage de la fenêtre

La plupart des gens ne savent pas, ou même ne me souviens pas, qu'ils puissent voir la finale de la mémoire de l'assemblée de code, à l'aide de Visual Studio Démontage de la fenêtre. Il montre le code machine est en cours d'exécution, il n'est pas CIL.

Utilisez ce alors que le débogage:

Debug (menu) -> Windows (submenu) -> Disassembly

Donc ce qui se passe avec postfix++?

Le postfix++ indique que nous aimerions pour incrémenter la valeur de l'opérande après l'évaluation... que tout le monde le sait... ce qui brouille un peu est le sens de "après l'évaluation".

Donc, ce n'est "après l'évaluation" signifie:

  • les autres usages de l'opérande, sur la même ligne de code doit être affecté:
    • a = i++ + i la seconde je l'ai est affectée par la valeur de l'échelon
    • Func(i++, i) le deuxième i est affecté
  • d'autres usages sur la même ligne que le respect de court-circuit de l'opérateur comme || et &&:
    • (false && i++ != i) || i == 0 le troisième que je n'est pas affecté par i++ parce qu'il n'est pas évalué

Alors, quelle est la signification de: i += i++;?

C'est le même que i = i + i++;

L'ordre d'évaluation est:

  1. Magasin i + i (0 + 0)
  2. Incrémenter i (i devient 1)
  3. Affecter la valeur de l'étape 1 à i (i devient 0)

Non pas que l'incrément est d'être jetés.

Quelle est la signification de: i = i++ + i;?

Ce n'est pas la même chose que l'exemple précédent. La 3e i est affectée par la valeur de l'échelon.

L'ordre d'évaluation est:

  1. Magasin que j' (qui est 0)
  2. Incrémenter i (i devient 1)
  3. Conserver de la valeur de l'étape 1 + i (0 + 1)
  4. Affecter la valeur de l'étape 3 à i (i devient 1)

61voto

dtb Points 104373
int i = 0;
i += i++;

est évaluée de la façon suivante:

Stack<int> stack = new Stack<int>();
int i;

// int i = 0;
stack.Push(0);                   // push 0
i = stack.Pop();                 // pop 0 --> i == 0

// i += i++;
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(i);                   // push 0
stack.Push(1);                   // push 1
i = stack.Pop() + stack.Pop();   // pop 0 and 1 --> i == 1
i = stack.Pop() + stack.Pop();   // pop 0 and 0 --> i == 0

c'est à dire i est changé deux fois: une fois par l' i++ expression et une fois par l' += déclaration.

Mais les opérandes de l' += déclaration sont

  • la valeur i avant l'évaluation d' i++ (sur la gauche de l' +=) et
  • la valeur i avant l'évaluation d' i++ (sur la droite de l' +=).

36voto

Jong Points 6452

Tout d’abord, retourne 0. Alors est incrémentée de 1. Enfin est définie sur la valeur initiale de qui est 0, plus la valeur `` retournée, qui est zéro trop. 0 + 0 = 0.

32voto

Kaz Points 18072

C'est tout simplement de gauche à droite, en bas à l'évaluation de l'efficacité de l'arbre de syntaxe abstraite. Sur le plan conceptuel, l'expression de l'arbre est parcouru de haut en bas, mais l'évaluation se déroule de la récursivité pop retour en haut de l'arborescence à partir du bas.

// source code
i += i++;

// abstract syntax tree

     +=
    /  \
   i    ++ (post)
         \
         i

L'évaluation commence par l'examen du nœud racine +=. C'est le constituant majeur de l'expression. L'opérande gauche de l' += doivent être évaluées afin de déterminer l'endroit où l'on stocke la variable, et à obtenir, au préalable, la valeur qui est égale à zéro. Ensuite, le côté droit doit être évalué.

Le côté droit est une post-incrémentation ++ de l'opérateur. Il a un seul opérande, i qui est évalué à la fois comme source de valeur, et comme un lieu où une valeur est stockée. L'exploitant évalue i, trouvant 0, et, par conséquent, les magasins un 1 dans cet endroit. Il renvoie l'état de la valeur, en 0, en conformité avec sa sémantique du retour de l'état de la valeur.

Maintenant, le contrôle est de retour à l' += de l'opérateur. Il a maintenant toutes les infos pour compléter son fonctionnement. Il connaît l'endroit où stocker le résultat (l'emplacement de stockage de l' i) ainsi que l'état de la valeur, et il a de la valeur ajoutée à l'état de la valeur, à savoir l' 0. Donc, i se termine par zéro.

Comme Java, C# a aseptisé une très stupide aspect du langage C par la fixation de l'ordre d'évaluation. De gauche à droite, de bas en haut: le plus évident de l'ordre qui est susceptible d'être attendu par des codeurs.

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