Pour i = 0, pourquoi (i + = i ++) est-il égal à 0?

253

Prenez le code suivant (utilisable comme application console):

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

Le résultat iest 0. Je m'attendais à 2 (comme certains de mes collègues l'ont fait). Le compilateur crée probablement une sorte de structure qui se traduit par izéro.

La raison pour laquelle je m'attendais à 2 est que, dans ma ligne de pensée, l'énoncé de droite serait évalué en premier, incrémentant i avec 1. Puis il est ajouté à i. Puisque i est déjà 1, il ajoute 1 à 1. Donc 1 + 1 = 2. Évidemment, ce n'est pas ce qui se passe.

Pouvez-vous expliquer ce que fait le compilateur ou ce qui se passe à l'exécution? Pourquoi le résultat est-il nul?

Une sorte d'avertissement: je suis absolument conscient que vous n'utiliserez pas (et ne devriez probablement pas) utiliser ce code. Je sais que je ne le ferai jamais. Néanmoins, je trouve intéressant de savoir pourquoi il agit ainsi et ce qui se passe exactement.

Peter
la source
57
le résultat attendu ne devrait-il pas être 1? i (0) + = i ++ (1) donc 0 + = 1 = 1
aleation
11
Cela fonctionne comme prévu
Steve est un D
177
Combien de variantes de cette question vont être posées?
mowwwalker
20
La pré-incrémentation augmentera la valeur avant de faire l'action i + = ++ je vous donnerai 1
Pierluc SS
21
Pourquoi tout le monde se concentre-t-il sur le pré-vs post-incrémentation? La chose "étrange" est que la valeur de isur le côté gauche de +="est mise en cache" avant que le côté droit soit évalué. Ceci est contre-intuitif, car cela nécessiterait, par exemple, une opération de copie s'il is'agissait d'un objet. (S'il vous plaît, ne me comprenez pas mal: je suis absolument d'accord pour dire que 0c'est la réponse correcte et conforme aux normes.)
JohnB

Réponses:

425

Ce:

int i = 0;
i += i++

Peut être vu comme vous le faites (ce qui suit est une simplification grossière excessive):

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 réellement est plus complexe que cela - jetez un œil à MSDN, aux opérateurs d'incrémentation et de décrémentation de Postfix 7.5.9 :

Le traitement au moment de l'exécution d'une opération d'incrémentation ou de décrémentation postfixe de la forme x ++ ou x-- comprend les étapes suivantes:

  • Si x est classé comme variable:

    • x est évalué pour produire la variable.
    • La valeur de x est enregistrée.
    • L'opérateur sélectionné est invoqué avec la valeur enregistrée de x comme argument.
    • La valeur renvoyée par l'opérateur est stockée à 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 priorité , le suffixe ++se produit avant += , mais le résultat finit par être inutilisé (car la valeur précédente de iest utilisée).


Une décomposition plus approfondie de i += i++les parties , il est fait d'une demande de savoir que les deux +=et ++ne sont pas atomique (qui est, ni l' un est une seule opération), même si elles ressemblent ils sont. La façon dont celles-ci sont implémentées implique des variables temporaires, des copies d' iavant les opérations - une pour chaque opération. (J'utiliserai les noms iAddet iAssignles variables temporaires utilisées pour ++et +=respectivement).

Ainsi, une approximation plus proche 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;
Oded
la source
3
@Oded L' ++opération est effectuée avant la fin de l'évaluation de l'instruction. +=Remplace donc la valeur. C'est ce qui s'est passé?
Anirudh Ramanathan
6
@Oded fait son: int i = 0; i = i + 1; (postfix) i = 0; (assignment). Si vous avez utilisé i ailleurs dans cette déclaration, il serait évalué à 1 à l'époque.
drch
@Cthulhu - Essentiellement. La réponse de dtb entre dans les détails.
Oded
6
Je n'achète pas celui-ci. La réponse de @yoriy est beaucoup plus précise. D'une part, dans votre réponse, vous dites que la dernière ligne serait i+1alors qu'elle devrait l'être i=i+1. N'est-ce pas ce que i++c'est?
recluze
3
La première partie de la réponse est redondante. Votre dernier exemple de code aurait pu le faire à mon humble avis. +1 cependant.
corazza
194

Démontage du code en cours d'exécution:

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 se compile dans 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;

Démontage du 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 la fenêtre de démontage

La plupart des gens ne savent pas ou ne se souviennent même pas qu'ils peuvent voir le code d'assemblage final en mémoire à l'aide de la fenêtre de désassemblage de Visual Studio . Il montre le code machine en cours d'exécution, ce n'est pas CIL.

Utilisez ceci lors du débogage:

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

Que se passe-t-il avec postfix ++?

Le suffixe ++ indique que nous aimerions augmenter la valeur de l'opérande après l'évaluation ... que tout le monde sait ... ce qui déroute un peu, c'est la signification de "après l'évaluation" .

Que signifie "après l'évaluation" :

  • les autres usages de l'opérande, sur la même ligne de code doivent être affectés:
    • a = i++ + i le second i est affecté par l'incrément
    • Func(i++, i) le second i est affecté
  • d'autres usages sur la même ligne respectent les opérateurs de court-circuit comme ||et &&:
    • (false && i++ != i) || i == 0 le troisième i n'est pas affecté par i ++ car il n'est pas évalué

Alors, quel est le sens de i += i++;:?

C'est la même chose que i = i + i++;

L'ordre d'évaluation est le suivant:

  1. Stocker i + i (c'est-à-dire 0 + 0)
  2. Incrément i (i devient 1)
  3. Attribuez la valeur de l'étape 1 à i (i devient 0)

Non pas que l'incrément soit ignoré.

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

Ce n'est pas la même chose que l'exemple précédent. Le 3ème iest affecté par l'incrément.

L'ordre d'évaluation est le suivant:

  1. Store i (c'est-à-dire 0)
  2. Incrément i (i devient 1)
  3. Enregistrer la valeur de l'étape 1 + i (c'est-à-dire 0 + 1)
  4. Attribuez la valeur de l'étape 3 à i (i devient 1)
Miguel Angelo
la source
22
+ 1 ++ - pour une dissection pure et dure. Chuck Norris serait fier :) Je suppose que vous faites l'hypothèse que l'OP est sur Intel, pas un port mono cependant ...
StuartLC
19
C # a un ordre d'évaluation bien défini pour l'expression, et le code objet implémente simplement cet ordre. La sortie du code machine n'est pas la raison ou l'explication de la commande d'évaluation.
Kaz
8
Le code machine permet de comprendre facilement comment l'ordre d'évaluation est mis en œuvre à l'OMI.
Kevin
5
@StuartLC Je vois ce que vous y avez fait. Honte cependant à ce vote rejeté.
Steffan Donal
2
a++ + an'est pas la même chose que a + a++parce que ce n'est plus des mathématiques pures. La loi de commutativité en algèbre ne prend pas en compte la possibilité que les variables changent de valeur au milieu d'une expression. Les mathématiques ne correspondent parfaitement à la programmation que lorsque la programmation est une programmation fonctionnelle. Et même pas à ce moment-là, à cause des limitations de représentation. Par exemple, les nombres à virgule flottante se comportent parfois comme des réels et parfois non. Même sans effets secondaires, les lois de commutativité et d'associativité qui s'appliquent aux nombres réels en mathématiques se brisent sur les nombres à virgule flottante.
Kaz
61
int i = 0;
i += i++;

est évalué comme suit:

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

ie iest changé deux fois: une fois par l' i++expression et une fois par l' +=instruction.

Mais les opérandes de la +=déclaration sont

  • la valeur iavant l'évaluation de i++(côté gauche de +=) et
  • la valeur iavant l'évaluation de i++(côté droit de +=).
dtb
la source
Ah c'est une explication fantastique. Me rappelle quand j'ai travaillé sur une calculatrice basée sur la pile en utilisant la notation de polissage inversé.
Nathan
36

Tout d'abord, i++renvoie 0. Ensuite, il iest incrémenté de 1. Enfin, ila valeur initiale iest 0, plus la valeur i++renvoyée, qui est également zéro. 0 + 0 = 0.

Jong
la source
2
Mais ce n'est i += i++;pas le cas i = i++;, donc la valeur de i++(0) est ajoutée à i, et non " iest définie sur la valeur i++renvoyée". Maintenant, la question est, lors de l'ajout de la valeur i++retournée à i, sera ila valeur incrémentée ou la valeur non incrémentée? La réponse, mon ami, est écrite dans le cahier des charges.
Daniel Fischer
C'est vrai, je vais le réparer. Mais de toute façon puisque i = 0initialement, i += somethingest équivalent à i = 0 + somethingce qui est i = something.
Jong
32

Il s'agit simplement d'une évaluation ascendante de gauche à droite de l'arbre de syntaxe abstraite. Sur le plan conceptuel, l'arborescence de l'expression est parcourue de haut en bas, mais l'évaluation se déroule à mesure que la récursivité remonte dans l'arbre à partir du bas.

// source code
i += i++;

// abstract syntax tree

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

L'évaluation commence par considérer le nœud racine +=. C'est le principal constituant de l'expression. L'opérande gauche de +=doit être évalué pour déterminer l'endroit où l'on stocke la variable, et pour obtenir la valeur précédente qui est nulle. Ensuite, le côté droit doit être évalué.

Le côté droit est un ++opérateur de post-incrémentation . Il a un opérande, iqui est évalué à la fois comme source d'une valeur et comme endroit où une valeur doit être stockée. L'opérateur évalue i, recherche 0et, par conséquent, stocke un 1dans cet emplacement. Il renvoie la valeur précédente, 0conformément à sa sémantique de retour de la valeur précédente.

Maintenant, le contrôle revient à l' +=opérateur. Il a maintenant toutes les informations pour terminer son opération. Il connaît l'endroit où stocker le résultat (l'emplacement de stockage de i) ainsi que la valeur antérieure, et il a la valeur à ajouter à la valeur antérieure, à savoir 0. Donc, ifinit par zéro.

Comme Java, C # a aseptisé un aspect très asinin du langage C en fixant l'ordre d'évaluation. De gauche à droite, de bas en haut: l'ordre le plus évident susceptible d'être attendu par les codeurs.

Kaz
la source
+1: Je suis d'accord avec vous, sauf que chaque codeur s'attend à ce que ... je m'attendais à ce que ce soit la même chose que ceci: SetSum(ref i, Inc(ref i))avec int SetSum(ref int a, int b) { return a += b; }et int Inc(ref int a) { return a++; }... bien sûr, je ne m'attends plus à ça.
Miguel Angelo
De plus, ce à quoi je m'attendais est incohérent! Ce ne serait pas égal à Set(ref i, Sum(i, Inc(ref i)))avec int Set(ref int a, int b) { return a = b; }et int Sum(int a, int b) { return a + b; }.
Miguel Angelo
Merci; vous faites allusion à une faille / incomplétude dans ma réponse que je dois corriger.
Kaz
Le problème avec SetSumest qu'il n'évalue pas l'opérande de gauche i, mais ne prend que son adresse, donc ce n'est pas équivalent à une évaluation complète de gauche à droite de l'opérande. Vous avez besoin de quelque chose comme SetSum(ref i, i, PostInc(ref i)). Le deuxième argument de SetSumest la valeur à ajouter, où nous utilisons simplement ipour spécifier la valeur antérieure de i. SetSumest juste int SetSum(ref int dest, int a, int b) { return dest = a + b; }.
Kaz
La confusion se produit (au moins pour moi) avec l'opérateur + =, car l'opérateur d'affectation a une évaluation de droite à gauche (par exemple a = b = c = d) ... donc on peut imaginer que + = suit la même règle, comme une opération atomique (comme je l'ai fait avec ma méthode SetSum) ... mais ce qui se passe en fait, c'est que C # se traduit a += bpar a = a + b... montrant que l'opérateur + = n'est pas atomique ... c'est juste du sucre syntaxique.
Miguel Angelo
30

Parce que i++renvoie d'abord la valeur, puis l'incrémente. Mais une fois que i est défini sur 1, vous le remettez à 0.

Yuriy Faktorovich
la source
17

La méthode post-incrémentation ressemble à ceci

int ++(ref int i)
{
    int c = i;
    i = i + 1;
    return c;
}

Donc, fondamentalement, lorsque vous appelez i++, iest incrément mais la valeur d'origine est retournée dans votre cas, c'est 0 qui est retourné.

Ash Burlaczenko
la source
12

Réponse simple

int i = 0;
i += i++;
// Translates to:
i = i + 0; // because post increment returns the current value 0 of i
// Before the above operation is set, i will be incremented to 1
// Now i gets set after the increment,
// so the original returned value of i will be taken.
i = 0;
Praveen Kumar Purushothaman
la source
12

i ++ signifie: retourner la valeur de i THEN l'incrémenter.

i + = i ++ signifie: Prendre la valeur actuelle de i. Ajoutez le résultat d'i ++.

Maintenant, ajoutons i = 0 comme condition de départ. i + = i ++ est maintenant évalué comme ceci:

  1. Quelle est la valeur actuelle de i? Il vaut 0. Stockez-le pour que nous puissions y ajouter le résultat d'i ++.
  2. Évaluer i ++ (évalue à 0 car c'est la valeur actuelle de i)
  3. Chargez la valeur stockée et ajoutez-y le résultat de l'étape 2. (ajouter 0 à 0)

Remarque: à la fin de l'étape 2, la valeur de i est en fait 1. Cependant, à l'étape 3, vous la supprimez en chargeant la valeur de i avant qu'elle ne soit incrémentée.

Contrairement à i ++, ++ i renvoie la valeur incrémentée.

Par conséquent, i + = ++ je vous donnerais 1.

Carl
la source
C'est aider Full
sonsha
11

L'opérateur d'incrémentation de post-correction ++, donne à la variable une valeur dans l'expression, puis refaiti l'incrément que vous avez attribué, retournant la valeur zéro (0) qui écrase l'incrémenté de un (1) , de sorte que vous obtenez zéro. Vous pouvez en savoir plus sur l'opérateur d'incrémentation dans l'opérateur ++ (MSDN).

Adil
la source
8

i += i++;sera égal à zéro, car il fait la ++suite.

i += ++i; le fera avant

Wes Cossick
la source
4
Si c'est le cas ++par la suite, je m'attendrais à ce que le résultat soit 1.
comecme
8

Le suffixe ++ évalue iavant de l'incrémenter et +=n'évalue iqu'une seule fois.

Par conséquent, 0 + 0 = 0, tel iqu'évalué et utilisé avant d'être incrémenté, car le format de suffixe de ++est utilisé. Pour être iincrémenté en premier, utilisez le préfixe form ( ++i).

(De plus, juste une note: vous ne devriez obtenir que 1, car 0 + (0 + 1) = 1)

Références: http://msdn.microsoft.com/en-us/library/sa7629ew.aspx (+ =)
http://msdn.microsoft.com/en-us/library/36x43w8w.aspx (++)

Nate Koppenhaver
la source
8

Que fait C # et le "pourquoi" de la confusion

Je m'attendais également à ce que la valeur soit 1 ... mais certaines explorations à ce sujet ont clarifié certains points.

Cochez les méthodes suivantes:

    static int SetSum(ref int a, int b) { return a += b; }

    static int Inc(ref int a) { return a++; }

Je m'attendais i += i++à ce que ce soit le même que SetSum(ref i, Inc(ref i)). La valeur de i après cette déclaration est 1 :

int i = 0;
SetSum(ref i, Inc(ref i));
Console.WriteLine(i); // i is 1

Mais alors je suis arrivé à une autre conclusion ... i += i++est en fait la même chose que i = i + i++... alors j'ai créé un autre exemple similaire, en utilisant ces fonctions:

    static int Sum(int a, int b) { return a + b; }

    static int Set(ref int a, int b) { return a = b; }

Après avoir appelé cela, Set(ref i, Sum(i, Inc(ref i)))la valeur de i est 0 :

int i = 0;
Set(ref i, Sum(i, Inc(ref i)));
Console.WriteLine(i); // i is 0

Cela explique non seulement ce que fait C # ... mais aussi pourquoi beaucoup de gens se sont confondus avec lui ... y compris moi.

Miguel Angelo
la source
2
Veuillez l'ajouter à votre réponse d'origine, cela n'ajoute aucun avantage à l'avoir comme réponse distincte.
casperOne
2
Je l'ai fait pour ne pas polir l'autre réponse, car il s'agit du code décompilé ... alors que dans celle-ci, j'ai essayé une approche différente pour expliquer les choses. Qu'est-ce que tu penses? Dois-je modifier l'autre réponse et ajouter celle-ci? Peut-être, ajoutez celui-ci ... je ne sais pas! Merci pour vos suggestions!
Miguel Angelo
7

Un bon mnémonique dont je me souviens toujours est le suivant:

S'il ++se trouve après l'expression, il renvoie la valeur qu'il était auparavant . Donc, le code suivant

int a = 1;
int b = a++;

est 1, car il aétait de 1 avant qu'il ne soit augmenté par la ++position debout après a . Les gens appellent cette notation post fix. Il y a aussi une notation pré- fix, où les choses sont exactement l'inverse: si elle ++se trouve avant , l'expression renvoie la valeur qu'elle est après l'opération:

int a = 1;
int b = ++a;

b est deux ici.

Donc, pour votre code, cela signifie

int i = 0;
i += (i++);

i++renvoie 0 (comme décrit ci-dessus), donc 0 + 0 = 0.

i += (++i); // Here 'i' would become two

Scott Meyers décrit la différence entre ces deux notations dans la «programmation efficace C ++». En interne, i++(Postfix) se souvient de la valeur iest, et appelle le préfixe notation ( ++i) et renvoie la valeur ancienne, i. C'est pourquoi vous devez utiliser AllWays ++idans les forboucles (même si je pense que tous les compilateurs modernes se traduisent i++à ++ien forboucles).

Carsten
la source
1
J'ai testé int i = 0; i += (++i)et iest réglé sur un plutôt que deux. Il est logique pour moi aussi, car en utilisant le préfixe au lieu de Postfix ne change pas le fait que, si vous écrivez i += (++i)vers i = i + (++i)l' iest évaluée avant ++i, entraînant i = 0 + (++i)et en fin de compte i = 0 + 1.
Wutz
6

La seule réponse à votre question qui est correcte est: Parce qu'elle n'est pas définie.

Ok, avant que vous ne me brûliez tous ..

Vous avez tous répondu pourquoi il i+=i++est logique et logique d'en résulter i=0.

J'ai été tenté de voter contre chacune de vos réponses, mais la réputation que j'ai calculée serait trop élevée.

Pourquoi je suis tellement en colère contre vous les gens? pas à cause de ce que vos réponses expliquent ..
Je veux dire, chaque réponse que j'ai lue avait fait un effort remarquable pour expliquer l'impossible, I Applaudissements!

Mais quel est le résultat ?? est-ce un résultat intuitif - est-ce un résultat acceptable ??

Chacun de vous a vu le «roi nu» et l'a accepté en quelque sorte comme un roi rationnel.

Vous avez tous tort!

i+=i++;résultat 0n'est pas défini.

un bug dans le mécanisme d'évaluation du langage si vous voulez .. ou pire encore! un bug dans la conception.

voulez une preuve? bien sûr que vous voulez!

int t=0; int i=0; t+=i++; //t=0; i=1

Maintenant ceci ... est un résultat intuitif! parce que nous avons d'abord évalué l' tattribué avec une valeur et ce n'est qu'après l'évaluation et l'affectation que nous avons eu l'opération postérieure - rationnelle, n'est-ce pas?

est-il rationnel que: i=i++et i=idonne le même résultat pour i?

tandis que t=i++et t=iont des résultats différents pour i.

L'opération de post est quelque chose qui devrait se produire après l'évaluation de l'instruction.
Par conséquent:

int i=0;
i+=i++;

Devrait être le même si nous écrivions:

int i=0;
i = i + i ++;

et donc le même que:

int i=0;
i= i + i;
i ++;

et donc le même que:

int i=0;
i = i + i;
i = i + 1;

Tout résultat qui 1n'indique pas un bogue dans le compliant ou un bogue dans la conception du langage si nous optons pour une pensée rationnelle - cependant MSDN et de nombreuses autres sources nous disent "hé - ce n'est pas défini!"

Maintenant, avant de continuer, même cet ensemble d'exemples que j'ai donné n'est soutenu ou reconnu par personne. Cependant, c'est ce qui, selon la manière intuitive et rationnelle, aurait dû être le résultat.

Le codeur ne doit pas savoir comment l'assemblage est écrit ou traduit!

S'il est écrit d'une manière qui ne respecte pas les définitions du langage - c'est un bug!

Et pour terminer, j'ai copié cela à partir de Wikipedia, Opérateurs d'incrémentation et de décrémentation :
Puisque l' opérateur d' incrémentation / décrémentation modifie son opérande, l'utilisation d'un tel opérande plus d'une fois dans la même expression peut produire des résultats indéfinis . Par exemple, dans des expressions telles que x - ++ x, il n'est pas clair dans quelle séquence les opérateurs de soustraction et d'incrémentation doivent être effectués. Des situations comme celle-ci sont encore aggravées lorsque des optimisations sont appliquées par le compilateur, ce qui peut entraîner un ordre d'exécution des opérations différent de celui prévu par le programmeur.

Et donc.

La bonne réponse est que cela NE DEVRAIT PAS ÊTRE UTILISÉ! (car il n'est PAS DÉFINI!)

Oui .. - Il a des résultats imprévisibles même si le compliant C # essaie de le normaliser d'une manière ou d'une autre.

Je n'ai trouvé aucune documentation de C # décrivant le comportement que vous avez tous documenté comme un comportement normal ou bien défini du langage. Ce que j'ai trouvé, c'est exactement le contraire!

[ copié de la documentation MSDN pour les opérateurs d'incrémentation et de décrémentation de Postfix: ++ et - ]

Lorsqu'un opérateur suffixe est appliqué à un argument de fonction, la valeur de l'argument n'est pas garantie d'être incrémentée ou décrémentée avant d'être transmise à la fonction. Voir la section 1.9.17 dans la norme C ++ pour plus d'informations.

Remarquez ces mots non garantis ...

Pardonnez-moi si cette réponse semble arrogante - je ne suis pas une personne arrogante. Je considère simplement que des milliers de personnes viennent ici pour apprendre et les réponses que je lis les induiront en erreur et nuiront à leur logique et à leur compréhension du sujet.

GY
la source
Je ne suis pas sûr de suivre à 100%, mais vous faites référence à la documentation C ++, mais ma question concernait C #. La documentation à ce sujet est ici .
Peter
Je faisais référence à C # dans ma réponse. À partir du lien que vous avez fourni: Le résultat de x ++ ou x-- est la valeur de x avant l'opération, tandis que le résultat de ++ x ou --x est la valeur de x après l'opération. Dans les deux cas, x lui-même a la même valeur après l'opération. montre clairement que ce n'est pas le cas lors des tests .. car i=++ifournira un résultat différent de i=i++. Par conséquent, ma réponse est valable.
GY
Aha, ok, mais c'est déroutant lorsque vous faites référence à la documentation C ++. Donc, ce que vous dites, c'est que la spécification n'a pas été implémentée correctement?
Peter
Non. Ce que je dis, c'est qu'il n'est pas défini selon la spécification, et utiliser undefined aboutira à des résultats indéfinis.
GY
Non défini en C ++, mais C # indique qu'il devrait être la même valeur après l'opération , non? Ce n'est pas la même chose qu'indéfini (mais je suis d'accord que vous ne devriez pas l'utiliser, voir mon avertissement, j'essayais juste de comprendre ce qui se passait).
Peter
4

L'opérateur ++ après la variable en fait un incrément de suffixe. L'incrémentation se produit après tout le reste de l'instruction, l'ajout et l'affectation. Si, à la place, vous mettez le ++ avant la variable, cela se produira avant que la valeur de i ne soit évaluée et vous donne la réponse attendue.

KeithS
la source
2
Le ++ne se produit pas après la +=déclaration, il se produit pendant l'exécution de la +=déclaration. C'est pourquoi les effets du ++get sont annulés par le +=.
dtb
L'utilisation de ++ i donne en fait 1, pas 2 (ma «réponse attendue» à l'origine).
Peter
Il semble que l'affectation += écrase la modification en raison du pré ou post-incrément de l'expression.
Steven Lu
4

Les étapes du calcul sont les suivantes:

  1. int i=0 // Initialisé à 0
  2. i+=i++ //Équation
  3. i=i+i++ // après avoir simplifié l'équation par le compilateur
  4. i=0+i++ // i substitution de valeur
  5. i=0+0 // i ++ vaut 0 comme expliqué ci-dessous
  6. i=0 // Résultat final i = 0

Ici, initialement, la valeur de iest 0. WKT, i++n'est rien d'autre que: utilisez d'abord la ivaleur, puis incrémentez la ivaleur de 1. Il utilise donc la ivaleur, 0, lors du calcul i++, puis l'incrémente de 1. Il en résulte donc une valeur de 0.

Suresh M
la source
3

Il y a deux options:

La première option: si le compilateur lit l'instruction comme suit,

i++;
i+=i;

alors le résultat est 2.

Pour

else if
i+=0;
i++;

le résultat est 1.

NÉO
la source
5
Ni l'un ni l'autre n'est le résultat réel .
Steven Lu
3

Soyez très prudent: lisez la FAQ C : ce que vous essayez de faire (mélange d'assignation et ++de la même variable) est non seulement non spécifié, mais il est également indéfini (ce qui signifie que le compilateur peut faire n'importe quoi lors de l'évaluation !, pas seulement donner résultats "raisonnables").

Veuillez lire la section 3 . La section entière vaut bien une lecture! Surtout 3.9, ce qui explique l'implication de non spécifié. La section 3.3 vous donne un bref résumé de ce que vous pouvez et ne pouvez pas faire avec "i ++" et autres.

Selon les compilateurs internes, vous pouvez obtenir 0, 2 ou 1 ou même autre chose! Et comme cela n'est pas défini, c'est OK pour eux de le faire.

Olivier Dulac
la source
oups, c # ... J'ai été renversé par le "gcc" que certains ont traversé pour démonter le code.
Olivier Dulac
1
J'ai manqué que c'était C # aussi, mais j'ai quand même aimé la réponse.
Iain Collins
1
@Iain: merci, je crois aussi que cela valait la peine de garder la réponse disponible, beaucoup de gens ne savent pas à ce sujet (ou à propos de cette grande FAQ, du meilleur moment d'Usenet où la plupart des personnes ayant des connaissances sur un sous-groupe allaient au même endroit pour le mettre à jour)
Olivier Dulac
3

Il y a beaucoup d'excellents raisonnements dans les réponses ci-dessus, je viens de faire un petit test et je veux partager avec vous

int i = 0;
i+ = i++;

Ici, le résultat i affiche 0 résultat. Considérez maintenant les cas ci-dessous:

Cas 1:

i = i++ + i; //Answer 1

plus tôt, je pensais que le code ci-dessus ressemble à ceci, donc à première vue, la réponse est 1, et vraiment la réponse de i pour celui-ci est 1.

Cas 2:

i = i + i++; //Answer 0 this resembles the question code.

ici, l'opérateur d'incrémentation ne vient pas dans le chemin d'exécution, contrairement au cas précédent où i ++ a la chance de s'exécuter avant l'ajout.

J'espère que cela aide un peu. Merci

ThomasBecker
la source
2

En espérant y répondre dans une perspective de type programmation C 101.

Il me semble que cela se passe dans cet ordre:

  1. iest évalué à 0, ce qui entraîne i = 0 + 0l'opération d'incrémentation i++"en file d'attente", mais l'attribution de 0 à ine s'est pas encore produite non plus.
  2. L'incrément i++se produit
  3. L'affectation i = 0ci-dessus se produit, écrasant efficacement tout ce que # 2 (le post-incrément) aurait fait.

Maintenant, # 2 peut ne jamais arriver (probablement pas?) Parce que le compilateur se rend probablement compte qu'il ne servira à rien, mais cela pourrait dépendre du compilateur. Quoi qu'il en soit, d'autres réponses plus compétentes ont montré que le résultat est correct et conforme à la norme C #, mais il n'est pas défini ce qui se passe ici pour C / C ++.

Comment et pourquoi est au-delà de mon expertise, mais le fait que l'affectation du côté droit précédemment évaluée se produise après la post-incrémentation est probablement ce qui prête à confusion ici.

De plus, vous ne vous attendriez pas à ce que le résultat soit 2, sauf si vous l'avez fait ++iau lieu de i++je crois.

gkimsey
la source
1
La version pré-incrémentée produit un résultat 2avec C ++: ideone.com/8dH8tf
Steven Lu
Ça a du sens. Mais le pré-incrément est une situation légèrement moins compliquée que le post-incrément.
gkimsey
2

Tout simplement,

i ++, ajoutera 1 à "i" une fois l'opérateur "+ =" terminé.

Ce que vous voulez, c'est ++ i, afin qu'il ajoute 1 à "i" avant que l'opérateur "+ =" ne soit exécuté.

balançoire
la source
0
i=0

i+=i

i=i+1

i=0;

Ensuite, le 1 est ajouté à i.

i + = i ++

Donc, avant d'ajouter 1 à i, a ipris la valeur de 0. Seulement si nous ajoutons 1 avant, iobtenez la valeur 0.

i+=++i

i=2
apprendre quoi
la source
-4

La réponse est isera 1.

Voyons comment:

Au départ i=0;.

Ensuite, lors du calcul en i +=i++;fonction de la valeur de, nous aurons quelque chose comme 0 +=0++;, donc selon la priorité de l'opérateur, 0+=0le premier sera exécuté et le résultat sera 0.

Ensuite, l'opérateur d'incrémentation sera appliqué comme 0++, as 0+1et la valeur de isera 1.

Shivam Sharma
la source
3
Cette réponse est fausse. Vous n'obtiendrez pas 1 car lorsque vous effectuez l' 0 += 0++;affectation, c'est après l'incrémentation ++mais avec la valeur de i interprétée avant de ++(car c'est un opérateur de poste.
PhoneixS
2
Désolé, mais c'est incorrect. Lisez ma question et vous verrez que je dis que le résultat est 0. Si vous exécutez le code, vous verrez que c'est effectivement 0.
Peter