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 i
est 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 i
zé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.
i
sur 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'ili
s'agissait d'un objet. (S'il vous plaît, ne me comprenez pas mal: je suis absolument d'accord pour dire que0
c'est la réponse correcte et conforme aux normes.)Réponses:
Ce:
Peut être vu comme vous le faites (ce qui suit est une simplification grossière excessive):
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 :
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 dei
est 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'i
avant les opérations - une pour chaque opération. (J'utiliserai les nomsiAdd
etiAssign
les variables temporaires utilisées pour++
et+=
respectivement).Ainsi, une approximation plus proche de ce qui se passe serait:
la source
++
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é?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.i+1
alors qu'elle devrait l'êtrei=i+1
. N'est-ce pas ce quei++
c'est?Démontage du code en cours d'exécution:
Code équivalent
Il se compile dans le même code que le code suivant:
Démontage du deuxième code (juste pour prouver qu'ils sont les mêmes)
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" :
a = i++ + i
le second i est affecté par l'incrémentFunc(i++, i)
le second i est affecté||
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:
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
i
est affecté par l'incrément.L'ordre d'évaluation est le suivant:
la source
a++ + a
n'est pas la même chose quea + 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.est évalué comme suit:
ie
i
est changé deux fois: une fois par l'i++
expression et une fois par l'+=
instruction.Mais les opérandes de la
+=
déclaration sonti
avant l'évaluation dei++
(côté gauche de+=
) eti
avant l'évaluation dei++
(côté droit de+=
).la source
Tout d'abord,
i++
renvoie 0. Ensuite, ili
est incrémenté de 1. Enfin,i
la valeur initialei
est 0, plus la valeuri++
renvoyée, qui est également zéro. 0 + 0 = 0.la source
i += i++;
pas le casi = i++;
, donc la valeur dei++
(0) est ajoutée ài
, et non "i
est définie sur la valeuri++
renvoyée". Maintenant, la question est, lors de l'ajout de la valeuri++
retournée ài
, serai
la valeur incrémentée ou la valeur non incrémentée? La réponse, mon ami, est écrite dans le cahier des charges.i = 0
initialement,i += something
est équivalent ài = 0 + something
ce qui esti = something
.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.
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,i
qui est évalué à la fois comme source d'une valeur et comme endroit où une valeur doit être stockée. L'opérateur évaluei
, recherche0
et, par conséquent, stocke un1
dans cet emplacement. Il renvoie la valeur précédente,0
conformé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 dei
) ainsi que la valeur antérieure, et il a la valeur à ajouter à la valeur antérieure, à savoir0
. Donc,i
finit 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.
la source
SetSum(ref i, Inc(ref i))
avecint SetSum(ref int a, int b) { return a += b; }
etint Inc(ref int a) { return a++; }
... bien sûr, je ne m'attends plus à ça.Set(ref i, Sum(i, Inc(ref i)))
avecint Set(ref int a, int b) { return a = b; }
etint Sum(int a, int b) { return a + b; }
.SetSum
est qu'il n'évalue pas l'opérande de gauchei
, 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 commeSetSum(ref i, i, PostInc(ref i))
. Le deuxième argument deSetSum
est la valeur à ajouter, où nous utilisons simplementi
pour spécifier la valeur antérieure dei
.SetSum
est justeint SetSum(ref int dest, int a, int b) { return dest = a + b; }
.a += b
para = a + b
... montrant que l'opérateur + = n'est pas atomique ... c'est juste du sucre syntaxique.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.la source
La méthode post-incrémentation ressemble à ceci
Donc, fondamentalement, lorsque vous appelez
i++
,i
est incrément mais la valeur d'origine est retournée dans votre cas, c'est 0 qui est retourné.la source
Réponse simple
la source
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:
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.
la source
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).la source
i += i++;
sera égal à zéro, car il fait la++
suite.i += ++i;
le fera avantla source
++
par la suite, je m'attendrais à ce que le résultat soit1
.Le suffixe ++ évalue
i
avant de l'incrémenter et+=
n'évaluei
qu'une seule fois.Par conséquent, 0 + 0 = 0, tel
i
qu'évalué et utilisé avant d'être incrémenté, car le format de suffixe de++
est utilisé. Pour êtrei
incré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 (++)
la source
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:
Je m'attendais
i += i++
à ce que ce soit le même queSetSum(ref i, Inc(ref i))
. La valeur de i après cette déclaration est 1 :Mais alors je suis arrivé à une autre conclusion ...
i += i++
est en fait la même chose quei = i + i++
... alors j'ai créé un autre exemple similaire, en utilisant ces fonctions:Après avoir appelé cela,
Set(ref i, Sum(i, Inc(ref i)))
la valeur de i est 0 :Cela explique non seulement ce que fait C # ... mais aussi pourquoi beaucoup de gens se sont confondus avec lui ... y compris moi.
la source
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 suivantest 1, car il
a
était de 1 avant qu'il ne soit augmenté par la++
position debout aprèsa
. 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:b
est deux ici.Donc, pour votre code, cela signifie
i++
renvoie 0 (comme décrit ci-dessus), donc0 + 0 = 0
.Scott Meyers décrit la différence entre ces deux notations dans la «programmation efficace C ++». En interne,
i++
(Postfix) se souvient de la valeuri
est, et appelle le préfixe notation (++i
) et renvoie la valeur ancienne,i
. C'est pourquoi vous devez utiliser AllWays++i
dans lesfor
boucles (même si je pense que tous les compilateurs modernes se traduisenti++
à++i
enfor
boucles).la source
int i = 0; i += (++i)
eti
est 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 écrivezi += (++i)
versi = i + (++i)
l'i
est évaluée avant++i
, entraînanti = 0 + (++i)
et en fin de comptei = 0 + 1
.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ésulteri=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ésultat0
n'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'
t
attribué 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++
eti=i
donne le même résultat pouri
?tandis que
t=i++
ett=i
ont des résultats différents pouri
.L'opération de post est quelque chose qui devrait se produire après l'évaluation de l'instruction.
Par conséquent:
Devrait être le même si nous écrivions:
et donc le même que:
et donc le même que:
Tout résultat qui
1
n'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.
la source
i=++i
fournira un résultat différent dei=i++
. Par conséquent, ma réponse est valable.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.
la source
++
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+=
.+=
écrase la modification en raison du pré ou post-incrément de l'expression.Les étapes du calcul sont les suivantes:
int i=0
// Initialisé à 0i+=i++
//Équationi=i+i++
// après avoir simplifié l'équation par le compilateuri=0+i++
// i substitution de valeuri=0+0
// i ++ vaut 0 comme expliqué ci-dessousi=0
// Résultat final i = 0Ici, initialement, la valeur de
i
est 0. WKT,i++
n'est rien d'autre que: utilisez d'abord lai
valeur, puis incrémentez lai
valeur de 1. Il utilise donc lai
valeur, 0, lors du calculi++
, puis l'incrémente de 1. Il en résulte donc une valeur de 0.la source
Il y a deux options:
La première option: si le compilateur lit l'instruction comme suit,
alors le résultat est 2.
Pour
le résultat est 1.
la source
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.
la source
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
Ici, le résultat i affiche 0 résultat. Considérez maintenant les cas ci-dessous:
Cas 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:
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
la source
En espérant y répondre dans une perspective de type programmation C 101.
Il me semble que cela se passe dans cet ordre:
i
est évalué à 0, ce qui entraînei = 0 + 0
l'opération d'incrémentationi++
"en file d'attente", mais l'attribution de 0 ài
ne s'est pas encore produite non plus.i++
se produiti = 0
ci-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
++i
au lieu dei++
je crois.la source
2
avec C ++: ideone.com/8dH8tfTout 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é.
la source
Ensuite, le 1 est ajouté à
i
.i + = i ++
Donc, avant d'ajouter 1 à
i
, ai
pris la valeur de 0. Seulement si nous ajoutons 1 avant,i
obtenez la valeur 0.la source
La réponse est
i
sera1
.Voyons comment:
Au départ
i=0;
.Ensuite, lors du calcul en
i +=i++;
fonction de la valeur de, nous aurons quelque chose comme0 +=0++;
, donc selon la priorité de l'opérateur,0+=0
le premier sera exécuté et le résultat sera0
.Ensuite, l'opérateur d'incrémentation sera appliqué comme
0++
, as0+1
et la valeur dei
sera1
.la source
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.