#include <stdio.h>
int main(void)
{
int i = 0;
i = i++ + ++i;
printf("%d\n", i); // 3
i = 1;
i = (i++);
printf("%d\n", i); // 2 Should be 1, no ?
volatile int u = 0;
u = u++ + ++u;
printf("%d\n", u); // 1
u = 1;
u = (u++);
printf("%d\n", u); // 2 Should also be one, no ?
register int v = 0;
v = v++ + ++v;
printf("%d\n", v); // 3 (Should be the same as u ?)
int w = 0;
printf("%d %d\n", ++w, w); // shouldn't this print 1 1
int x[2] = { 5, 8 }, y = 0;
x[y] = y ++;
printf("%d %d\n", x[0], x[1]); // shouldn't this print 0 8? or 5 0?
}
815
(i++)
évalue toujours à 1, sans tenir compte des parenthèsesi = (i++);
soit, il existe certainement un moyen plus clair de l'écrire. Ce serait vrai même s'il était bien défini. Même en Java, qui définit le comportement dei = (i++);
, c'est toujours du mauvais code. Il suffit d'écrirei++;
Réponses:
C a le concept de comportement non défini, c'est-à-dire que certaines constructions de langage sont syntaxiquement valides mais vous ne pouvez pas prédire le comportement lorsque le code est exécuté.
Pour autant que je sache, la norme ne dit pas explicitement pourquoi le concept de comportement indéfini existe. Dans mon esprit, c'est simplement parce que les concepteurs de langage voulaient qu'il y ait une certaine latitude dans la sémantique, au lieu d'exiger que toutes les implémentations gèrent le débordement d'entier de la même manière, ce qui imposerait très probablement de sérieux coûts de performance, ils ont juste laissé le comportement non défini de sorte que si vous écrivez du code qui provoque un débordement d'entier, tout peut arriver.
Donc, dans cet esprit, pourquoi ces «problèmes»? Le langage dit clairement que certaines choses conduisent à un comportement indéfini . Il n'y a pas de problème, il n'y a pas de "devrait" impliqué. Si le comportement non défini change lorsqu'une des variables impliquées est déclarée
volatile
, cela ne prouve ni ne change rien. Ce n'est pas défini ; vous ne pouvez pas raisonner sur le comportement.Votre exemple le plus intéressant, celui avec
est un exemple de manuel de comportement indéfini (voir l'entrée de Wikipedia sur les points de séquence ).
la source
i = ++i + 1;
.Compilez et démontez simplement votre ligne de code, si vous êtes si enclin à savoir exactement comment vous obtenez ce que vous obtenez.
Voici ce que j'obtiens sur ma machine, avec ce que je pense se passe:
(Je ... suppose que l'instruction 0x00000014 était une sorte d'optimisation du compilateur?)
la source
gcc evil.c -c -o evil.bin
etgdb evil.bin
→disassemble evil
, ou quels que soient leurs équivalents Windows :)Why are these constructs undefined behavior?
.gcc -S evil.c
), ce qui est tout ce qui est nécessaire ici. Le monter puis le démonter n'est qu'un moyen détourné de le faire.Je pense que les parties pertinentes de la norme C99 sont 6.5 Expressions, §2
et 6.5.16 Opérateurs d'affectation, §4:
la source
i=i=5
c'est aussi un comportement indéfiniA=B=5;
"Write-lock A; Write-Lock B; Store 5 to A; store 5 to B; Unlock B ; Déverrouiller A; ", et une instruction telleC=A+B;
que" Verrou de lecture A; Verrou de lecture B; Calculer A + B; Déverrouiller A et B; Verrou d'écriture C; Stocker le résultat; Déverrouiller C; ". Cela garantirait que si un thread le faisaitA=B=5;
tandis qu'un autre le faisait,C=A+B;
ce dernier thread verrait les deux écritures avoir eu lieu ou aucun. Potentiellement une garantie utile. Si un fil l'a faitI=I=5;
, cependant ...La plupart des réponses citées ici de la norme C soulignent que le comportement de ces constructions n'est pas défini. Pour comprendre pourquoi le comportement de ces constructions n'est pas défini , comprenons d'abord ces termes à la lumière de la norme C11:
Séquencé: (5.1.2.3)
Non séquencé:
Les évaluations peuvent être l'une des deux choses suivantes:
Point de séquence:
Venons-en maintenant à la question, pour les expressions comme
la norme dit que:
6.5 Expressions:
Par conséquent, l'expression ci-dessus appelle UB car deux effets secondaires sur le même objet ne
i
sont pas séquencés l'un par rapport à l'autre. Cela signifie qu'il n'est pas séquencé si l'effet secondaire par affectation ài
sera effectué avant ou après l'effet secondaire par++
.Selon que l'affectation se produit avant ou après l'incrément, différents résultats seront produits et c'est le cas du comportement indéfini .
Permet de renommer l'être
i
à gauche de l'affectationil
et à droite de l'affectation (dans l'expressioni++
)ir
, puis l'expression soit commeUn point important concernant l'
++
opérateur Postfix est que:Cela signifie que l'expression
il = ir++
pourrait être évaluée soit commeou
résultant en deux résultats différents
1
et2
qui dépend de la séquence d'effets secondaires par affectation++
et donc appelle UB.la source
Le comportement ne peut pas vraiment être expliqué car il invoque à la fois un comportement non spécifié et un comportement indéfini , donc nous ne pouvons pas faire de prédictions générales sur ce code, bien que si vous lisez le travail d' Olve Maudal tel que Deep C et Unspecified and Undefined, parfois vous pouvez faire du bien devine dans des cas très spécifiques avec un compilateur et un environnement spécifiques, mais ne le faites pas à proximité de la production.
Donc, pour passer à un comportement non spécifié , dans le projet de norme C99 , le
6.5
paragraphe 3 dit ( soulignement le mien ):Donc, quand nous avons une ligne comme celle-ci:
nous ne savons pas si
i++
ou++i
seront évalués en premier. C'est principalement pour donner au compilateur de meilleures options d'optimisation .Nous avons aussi un comportement non défini ici aussi puisque le programme est en train de modifier les variables (
i
,u
, etc ..) plus d'une fois entre les points de séquence . Extrait du projet de section standard,6.5
paragraphe 2 ( je souligne ):il cite les exemples de code suivants comme non définis:
Dans tous ces exemples, le code tente de modifier un objet plusieurs fois dans le même point de séquence, ce qui se terminera par
;
dans chacun de ces cas:Le comportement non spécifié est défini dans le projet de norme c99 dans la section
3.4.4
:et le comportement indéfini est défini dans la section
3.4.3
comme:et note que:
la source
Une autre façon de répondre à cela, plutôt que de s'enliser dans les détails obscurs des points de séquence et des comportements indéfinis, consiste simplement à demander, que sont-ils censés signifier? Qu'est-ce que le programmeur essayait de faire?
Le premier fragment demandé
i = i++ + ++i
, est assez clairement fou dans mon livre. Personne ne l'écrirait jamais dans un vrai programme, ce n'est pas évident, il n'y a pas d'algorithme concevable que quelqu'un aurait pu essayer de coder qui aurait entraîné cette séquence d'opérations artificielle particulière. Et comme ce n'est pas évident pour vous et moi ce qu'il est censé faire, c'est bien dans mon livre si le compilateur ne peut pas comprendre ce qu'il est censé faire non plus.Le deuxième fragment,,
i = i++
est un peu plus facile à comprendre. Quelqu'un essaie clairement d'incrémenter i et d'assigner le résultat à i. Mais il y a plusieurs façons de le faire en C. La façon la plus simple d'ajouter 1 à i et de réattribuer le résultat à i est la même dans presque tous les langages de programmation:C, bien sûr, a un raccourci pratique:
Cela signifie "ajouter 1 à i et attribuer le résultat à i". Donc, si nous construisons un méli-mélo des deux, en écrivant
ce que nous disons vraiment, c'est "ajouter 1 à i, et attribuer le résultat à i, et attribuer le résultat à i". Nous sommes confus, donc cela ne me dérange pas trop si le compilateur est également confus.
De façon réaliste, la seule fois où ces expressions folles sont écrites, c'est lorsque les gens les utilisent comme des exemples artificiels de la façon dont ++ est censé fonctionner. Et bien sûr, il est important de comprendre comment ++ fonctionne. Mais une règle pratique pour l'utilisation de ++ est: "Si ce n'est pas évident ce que signifie une expression utilisant ++, ne l'écrivez pas."
Nous avions l'habitude de passer d'innombrables heures sur comp.lang.c à discuter d'expressions comme celles-ci et pourquoi elles n'étaient pas définies. Deux de mes réponses plus longues, qui tentent d'expliquer vraiment pourquoi, sont archivées sur le Web:
Voir aussi question 3.8 et le reste des questions à l' article 3 de la liste C FAQ .
la source
*p=(*q)++;
pour signifierif (p!=q) *p=(*q)++; else *p= __ARBITRARY_VALUE;
que n'est plus le cas. Le C hyper-moderne nécessiterait d'écrire quelque chose comme cette dernière formulation (bien qu'il n'y ait pas de moyen standard d'indiquer que le code ne se soucie pas de ce qu'il contient*p
) pour atteindre le niveau d'efficacité des compilateurs utilisés pour fournir le premier (laelse
clause est nécessaire afin de laisser le compilateur optimise ceif
dont certains compilateurs plus récents auraient besoin).assert
instructions, afin que le programmeur puisse précéder la ligne en question avec un simpleassert(p != q)
. (Bien sûr, suivre ce cours nécessiterait également une réécriture<assert.h>
pour ne pas supprimer purement et simplement les assertions dans les versions non déboguées, mais plutôt les transformer en quelque chose comme__builtin_assert_disabled()
le compilateur lui-même peut voir, puis ne pas émettre de code pour.)Souvent, cette question est liée comme un doublon de questions liées au code comme
ou
ou des variantes similaires.
Bien qu'il s'agisse également d' un comportement indéfini, comme indiqué précédemment, il existe de subtiles différences lorsqu'il
printf()
est impliqué lors de la comparaison avec une déclaration telle que:Dans la déclaration suivante:
l' ordre d'évaluation des arguments dans
printf()
n'est pas spécifié . Cela signifie que les expressionsi++
et++i
pourraient être évaluées dans n'importe quel ordre. La norme C11 contient quelques descriptions pertinentes à ce sujet:Annexe J, comportements non spécifiés
3.4.4, comportement non spécifié
Le comportement non spécifié lui-même n'est PAS un problème. Considérez cet exemple:
Cela aussi a un comportement non spécifié car l'ordre d'évaluation de
++x
ety++
n'est pas spécifié. Mais c'est une déclaration parfaitement légale et valide. Il n'y a aucun comportement indéfini dans cette déclaration. Parce que les modifications (++x
ety++
) sont effectuées sur des objets distincts .Ce qui rend la déclaration suivante
comme comportement indéfini est le fait que ces deux expressions modifient le même objet
i
sans point de séquence intermédiaire .Un autre détail est que la virgule impliquée dans l'appel printf () est un séparateur , pas l' opérateur virgule .
Il s'agit d'une distinction importante car l' opérateur virgule introduit un point de séquence entre l'évaluation de leurs opérandes, ce qui rend les éléments suivants légaux:
L'opérateur virgule évalue ses opérandes de gauche à droite et ne donne que la valeur du dernier opérande. Donc
j = (++i, i++);
, par++i
incrémentsi
de6
eti++
rendements ancienne valeuri
(6
) qui est attribué àj
. Devienti
alors7
dû au post-incrément.Donc, si la virgule dans l'appel de fonction devait être un opérateur de virgule,
ne sera pas un problème. Mais il invoque un comportement indéfini car la virgule est ici un séparateur .
Pour ceux qui découvrent un comportement indéfini, il serait avantageux de lire ce que tout programmeur C devrait savoir sur le comportement indéfini pour comprendre le concept et de nombreuses autres variantes de comportement indéfini en C.
Ce message: Un comportement non défini, non spécifié et défini par la mise en œuvre est également pertinent.
la source
int a = 10, b = 20, c = 30; printf("a=%d b=%d c=%d\n", (a = a + b + c), (b = b + b), (c = c + c));
semble donner un comportement stable (évaluation des arguments de droite à gauche dans gcc v7.3.0; résultat "a = 110 b = 40 c = 60"). Est-ce parce que les affectations sont considérées comme des «déclarations complètes» et introduisent ainsi un point de séquence? Cela ne devrait-il pas se traduire par une évaluation des arguments / déclarations de gauche à droite? Ou s'agit-il simplement d'une manifestation d'un comportement indéfini?b
etc
dans les arguments 3ème et 4ème respectivement , et la lecture dans le 2ème argument. Mais il n'y a pas de séquence entre ces expressions (2e, 3e et 4e arguments). gcc / clang a une option-Wsequence-point
qui peut aussi aider à les trouver.Bien qu'il soit peu probable que des compilateurs et des processeurs le fassent réellement, il serait légal, selon la norme C, que le compilateur implémente "i ++" avec la séquence:
Bien que je ne pense pas que les processeurs prennent en charge le matériel pour permettre qu'une telle chose soit faite efficacement, on peut facilement imaginer des situations où un tel comportement rendrait le code multithread plus facile (par exemple, cela garantirait que si deux threads essayaient d'effectuer ce qui précède) séquence simultanément,
i
serait incrémenté de deux) et il n'est pas totalement inconcevable que certains futurs processeurs fournissent une fonctionnalité similaire.Si le compilateur devait écrire
i++
comme indiqué ci-dessus (légal en vertu de la norme) et intercaler les instructions ci-dessus tout au long de l'évaluation de l'expression globale (également légal), et s'il ne s'est pas avéré que l'une des autres instructions s'est produite pour y accéderi
, il serait possible (et légal) pour le compilateur de générer une séquence d'instructions qui se bloquerait. Pour être sûr, un compilateur détecterait presque certainement le problème dans le cas où la même variablei
est utilisée aux deux endroits, mais si une routine accepte des références à deux pointeursp
etq
, et utilise(*p)
et(*q)
dans l'expression ci-dessus (plutôt que d'utiliseri
deux fois), le compilateur ne serait pas tenu de reconnaître ou d'éviter le blocage qui se produirait si la même adresse d'objet était transmise pour les deux .p
etq
la source
Bien que la syntaxe des expressions comme
a = a++
oua++ + a++
soit légale, le comportement de ces constructions n'est pas défini car un devoir en standard C n'est pas respecté. C99 6.5p2 :La note de bas de page 73 précisant davantage que
Les différents points de séquence sont énumérés à l'annexe C de C11 (et C99 ):
Le libellé du même paragraphe dans C11 est le suivant:
Vous pouvez détecter de telles erreurs dans un programme en utilisant par exemple une version récente de GCC avec
-Wall
et-Werror
, puis GCC refusera carrément de compiler votre programme. Ce qui suit est la sortie de gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005:La partie importante est de savoir ce qu'est un point de séquence - et qu'est-ce qu'un point de séquence et ce qui ne l'est pas . Par exemple, l' opérateur virgule est un point de séquence, donc
est bien défini et augmentera
i
d'une unité, ce qui l'ancienne valeur, rejetez cette valeur; puis chez l'opérateur virgule, régler les effets secondaires; puis incrémenteri
de un, et la valeur résultante devient la valeur de l'expression - c'est-à-dire que c'est juste une façon artificielle d'écrirej = (i += 2)
qui est encore une fois une façon "intelligente" d'écrireCependant, la
,
liste des arguments de la fonction est pas un opérateur virgule et il n'y a pas de point de séquence entre les évaluations d'arguments distincts; au lieu de cela, leurs évaluations ne sont pas séquencées les unes par rapport aux autres; donc l'appel de fonctiona un comportement indéfini car il n'y a pas de point de séquence entre les évaluations de
i++
et++i
dans les arguments de fonction , et la valeur dei
est donc modifiée deux fois, par les deuxi++
et++i
, entre le point de séquence précédent et le suivant.la source
La norme C indique qu'une variable ne doit être affectée qu'au plus une fois entre deux points de séquence. Un point-virgule, par exemple, est un point de séquence.
Donc, chaque déclaration du formulaire:
et ainsi de suite violer cette règle. La norme indique également que le comportement n'est pas défini et n'est pas spécifié. Certains compilateurs les détectent et produisent des résultats, mais ce n'est pas conforme à la norme.
Cependant, deux variables différentes peuvent être incrémentées entre deux points de séquence.
Ce qui précède est une pratique de codage courante lors de la copie / analyse de chaînes.
la source
Dans /programming/29505280/incrementing-array-index-in-c, quelqu'un a posé des questions sur une déclaration comme:
qui imprime 7 ... l'OP s'attendait à ce qu'il imprime 6.
Les
++i
incréments ne sont pas garantis d'être tous terminés avant le reste des calculs. En fait, différents compilateurs obtiendront des résultats différents ici. Dans l'exemple que vous avez fourni, le premier 2++i
exécuté, les valeurs dek[]
ont été lus, la dernière++i
alorsk[]
.Les compilateurs modernes optimiseront très bien cela. En fait, peut-être mieux que le code que vous avez écrit à l'origine (en supposant qu'il a fonctionné comme vous l'aviez espéré).
la source
Une bonne explication de ce qui se passe dans ce type de calcul est fournie dans le document n1188 du site ISO W14 .
J'explique les idées.
La règle principale de la norme ISO 9899 qui s'applique dans cette situation est 6.5p2.
Les points de séquence dans une expression comme
i=i++
sont avanti=
et aprèsi++
.Dans le document que j'ai cité ci-dessus, il est expliqué que vous pouvez comprendre que le programme est formé de petites cases, chaque case contenant les instructions entre 2 points de séquence consécutifs. Les points de séquence sont définis à l'annexe C de la norme, dans le cas de
i=i++
il y a 2 points de séquence qui délimitent une expression complète. Une telle expression est syntaxiquement équivalente à une entrée deexpression-statement
sous la forme Backus-Naur de la grammaire (une grammaire est fournie dans l'annexe A de la norme).Ainsi, l'ordre des instructions à l'intérieur d'une boîte n'a pas d'ordre clair.
peut être interprété comme
ou comme
parce que toutes ces formes pour interpréter le code
i=i++
sont valides et parce que les deux génèrent des réponses différentes, le comportement n'est pas défini.Ainsi, un point de séquence peut être vu par le début et la fin de chaque boîte qui compose le programme [les boîtes sont des unités atomiques en C] et à l'intérieur d'une boîte, l'ordre des instructions n'est pas défini dans tous les cas. Changer cet ordre peut parfois changer le résultat.
ÉDITER:
Une autre bonne source pour expliquer ces ambiguïtés sont les entrées du site c-faq (également publié sous forme de livre ), à savoir ici et ici et ici .
la source
i=i++
sont très similaires à cette réponse .Votre question n'était probablement pas: "Pourquoi ces constructions ont-elles un comportement indéfini en C?". Votre question était probablement: "Pourquoi ce code (en utilisant
++
) ne m'a- t-il pas donné la valeur que j'attendais?", Et quelqu'un a marqué votre question en double et vous a envoyé ici.Cette réponse tente de répondre à cette question: pourquoi votre code ne vous a-t-il pas donné la réponse que vous attendiez, et comment pouvez-vous apprendre à reconnaître (et éviter) les expressions qui ne fonctionneront pas comme prévu.
Je suppose que vous avez déjà entendu la définition de base des C
++
et des--
opérateurs, et comment la forme du préfixe++x
diffère de la forme du postfixx++
. Mais ces opérateurs sont difficiles à penser, alors pour vous assurer que vous avez compris, vous avez peut-être écrit un tout petit programme de test impliquant quelque chose commeMais, à votre grande surprise, ce programme ne vous a pas aidé à comprendre - il a imprimé une sortie étrange, inattendue, inexplicable, suggérant que peut-être
++
fait quelque chose de complètement différent, pas du tout ce que vous pensiez qu'il faisait.Ou, peut-être que vous regardez une expression difficile à comprendre comme
Peut-être que quelqu'un vous a donné ce code comme un puzzle. Ce code n'a également aucun sens, surtout si vous l'exécutez - et si vous le compilez et l'exécutez sous deux compilateurs différents, vous obtiendrez probablement deux réponses différentes! Qu'est-ce qui se passe? Quelle réponse est correcte? (Et la réponse est que les deux le sont, ou aucun d'eux ne l'est.)
Comme vous l'avez déjà entendu, toutes ces expressions ne sont pas définies , ce qui signifie que le langage C ne garantit pas ce qu'elles feront. C'est un résultat étrange et surprenant, car vous pensiez probablement que tout programme que vous pourriez écrire, tant qu'il serait compilé et exécuté, générerait une sortie unique et bien définie. Mais dans le cas d'un comportement indéfini, ce n'est pas le cas.
Qu'est-ce qui rend une expression indéfinie? Les expressions impliquant
++
et--
toujours indéfinies? Bien sûr que non: ce sont des opérateurs utiles, et si vous les utilisez correctement, ils sont parfaitement bien définis.Pour les expressions dont nous parlons, ce qui les rend indéfinies, c'est quand il y a trop de choses à la fois, quand nous ne savons pas dans quel ordre les choses vont se passer, mais quand l'ordre compte pour le résultat que nous obtenons.
Revenons aux deux exemples que j'ai utilisés dans cette réponse. Quand j'ai écrit
la question est, avant d'appeler
printf
, le compilateur calcule-t-il la valeur dex
premier, oux++
, ou peut++x
- être ? Mais il s'avère que nous ne savons pas . Il n'y a pas de règle en C qui dit que les arguments d'une fonction sont évalués de gauche à droite, de droite à gauche ou dans un autre ordre. Donc , nous ne pouvons pas dire si le compilateur fera d'x
abord, puis++x
, puisx++
, oux++
alors++x
alorsx
, ou d' un autre ordre. Mais l'ordre est clairement important, car selon l'ordre utilisé par le compilateur, nous obtiendrons clairement des résultats différents imprimés parprintf
.Et cette expression folle?
Le problème avec cette expression est qu'elle contient trois tentatives différentes pour modifier la valeur de x: (1) la
x++
partie essaie d'ajouter 1 à x, de stocker la nouvelle valeur dansx
et de renvoyer l'ancienne valeur dex
; (2) la++x
partie essaie d'ajouter 1 à x, de stocker la nouvelle valeur dansx
et de renvoyer la nouvelle valeur dex
; et (3) lax =
partie essaie de réattribuer la somme des deux autres à x. Laquelle de ces trois tentatives de mission "gagnera"? À laquelle des trois valeurs sera effectivement attribuéex
? Encore une fois, et peut-être de façon surprenante, il n'y a pas de règle en C pour nous le dire.Vous pourriez imaginer que la priorité ou l'associativité ou l'évaluation de gauche à droite vous indique dans quel ordre les choses se produisent, mais ce n'est pas le cas. Vous ne me croyez peut-être pas, mais croyez-moi, je le redis: la priorité et l'associativité ne déterminent pas tous les aspects de l'ordre d'évaluation d'une expression en C. En particulier, si au sein d'une même expression, il y en a plusieurs différents endroits où nous essayons d'assigner une nouvelle valeur à quelque chose comme
x
, la priorité et associativité ne pas nous dire que ces tentatives se premier ou dernier, ou quoi que ce soit.Donc, avec tout cet arrière-plan et cette introduction à l'écart, si vous voulez vous assurer que tous vos programmes sont bien définis, quelles expressions pouvez-vous écrire et lesquelles ne pouvez-vous pas écrire?
Ces expressions sont toutes très bien:
Ces expressions sont toutes indéfinies:
Et la dernière question est, comment pouvez-vous dire quelles expressions sont bien définies et quelles expressions ne sont pas définies?
Comme je l'ai dit plus tôt, les expressions non définies sont celles où il y a trop de choses à la fois, où vous ne pouvez pas être sûr de l'ordre dans lequel les choses se passent et où l'ordre est important:
Comme exemple de # 1, dans l'expression
il y a trois tentatives pour modifier `x.
Comme exemple de # 2, dans l'expression
nous utilisons tous les deux la valeur de
x
et la modifions.Voilà donc la réponse: assurez-vous que dans n'importe quelle expression que vous écrivez, chaque variable est modifiée au plus une fois, et si une variable est modifiée, vous n'essayez pas également d'utiliser la valeur de cette variable ailleurs.
la source
La raison en est que le programme exécute un comportement non défini. Le problème réside dans l'ordre d'évaluation, car aucun point de séquence n'est requis selon la norme C ++ 98 (aucune opération n'est séquencée avant ou après une autre selon la terminologie C ++ 11).
Cependant, si vous vous en tenez à un seul compilateur, vous trouverez le comportement persistant, tant que vous n'ajoutez pas d'appels de fonction ou de pointeurs, ce qui rendrait le comportement plus compliqué.
Donc d'abord le GCC: En utilisant Nuwen MinGW 15 GCC 7.1, vous obtiendrez:
}
Comment fonctionne GCC? il évalue les sous-expressions dans un ordre de gauche à droite pour le côté droit (RHS), puis attribue la valeur au côté gauche (LHS). C'est exactement ainsi que Java et C # se comportent et définissent leurs normes. (Oui, le logiciel équivalent en Java et C # a des comportements définis). Il évalue chaque sous-expression une par une dans la déclaration RHS dans un ordre de gauche à droite; pour chaque sous-expression: le ++ c (pré-incrément) est évalué d'abord puis la valeur c est utilisée pour l'opération, puis le post-incrément c ++).
selon GCC C ++: Opérateurs
le code équivalent en comportement défini C ++ tel que GCC le comprend:
Ensuite, nous allons à Visual Studio . Visual Studio 2015, vous obtenez:
Comment fonctionne Visual Studio, il adopte une autre approche, il évalue toutes les expressions pré-incrémentées lors de la première passe, puis utilise les valeurs des variables dans les opérations lors de la deuxième passe, attribue de RHS à LHS lors de la troisième passe, puis enfin il évalue toutes les expressions post-incrémentation en une seule passe.
Ainsi, l'équivalent en comportement défini C ++ tel que Visual C ++ le comprend:
comme l'indique la documentation de Visual Studio sur Priorité et Ordre d'évaluation :
la source