La Priorité de l'opérateur vs Ordre d'Évaluation
Les termes "la priorité de l'opérateur" et "l'ordre de l'évaluation" sont très couramment utilisés termes de programmation et c'est extrêmement important pour un programmeur de savoir. Et, comme je le comprends, les deux concepts sont étroitement liés; on ne peut pas faire sans l'autre quand on parle d'expressions.
Prenons un exemple simple:
int a=1; //Line 1
a = a++ + ++a; //Line 2
printf("%d",a); //Line 3
Maintenant, il est évident que Line 2
conduit à un Comportement Indéfini, depuis Séquence de points en C et C++ comprennent:
Entre l'évaluation de la gauche et de la droite des opérandes de l' && (logique
ET), || (OU logique), et par des virgules
des opérateurs. Par exemple, dans le
expression*p++ != 0 && *q++ != 0
, tous les
effets secondaires de la sous-expression
*p++ != 0
sont terminées avant toute tentative d'accès àq
.Entre l'évaluation de la première opérande de l'ternaire
"point d'interrogation" de l'opérateur et de l'
deuxième ou troisième opérande. Par exemple,
dans l'expressiona = (*p++) ? (*p++)
il y a un point de séquence après
: 0
la première*p++
, sens qu'il a déjà
été incrémenté par le temps de l'
la deuxième instance est exécuté.À la fin d'une pleine expression. Cette catégorie comprend expression
états (comme la cession
a=b;
), de retour des états, la
le contrôle des expressions de if, switch,
tout ou-même si des déclarations, et toutes les
trois expressions dans une instruction for.Avant qu'une fonction est entré dans un appel de fonction. L'ordre dans lequel
les arguments sont évalués n'est pas
spécifié, mais ce point de séquence
signifie que l'ensemble de leurs effets secondaires
avant la fonction est
entré. Dans l'expressionf(i++) + g(j++) + h(k++)
,
f
est appelée avec un
paramètre de la valeur d'origine dei
,
maisi
est incrémenté avant d'entrer dans
le corps def
. De même,j
etk
sont
mis à jour avant d'entrer dansg
eth
respectivement. Cependant, il n'est pas
spécifié dans quel ordref()
,g()
,h()
sont exécutées, ni dans quel ordrei
,j
,
k
sont incrémentés. Les valeurs dej
et
k
dans le corps def
sont donc
undefined.Trois Notez qu'une fonction
appelf(a,b,c)
n'est pas une utilisation de l'
opérateur virgule et de l'ordre de
évaluation poura
,b
, etc
est
non spécifié.À un retour de la fonction, après le retour de la valeur est copiée dans le
l'appel de contexte. (De ce point de séquence
n'est spécifié dans la norme C++;
elle n'est présente que de manière implicite dans
C.)À la fin d'un initialiseur; par exemple, après l'évaluation de 5
dans la déclarationint a = 5;
.
Ainsi, passant par le Point n ° 3:
À la fin d'une pleine expression. Cette catégorie comprend l'expression des états (comme l'affectation a=b;), de retour des états, le contrôle des expressions de if, switch, while, ou le faire-même si des déclarations, et toutes les trois expressions dans une instruction for.
Line 2
clairement conduit à un Comportement Indéfini. Cela montre comment Comportement Indéfini est étroitement couplé avec Séquence de Points de.
Prenons maintenant un autre exemple:
int x=10,y=1,z=2; //Line 4
int result = x<y<z; //Line 5
Maintenant son évident que Line 5
fera la variable result
magasin 1
.
Maintenant l'expression x<y<z
dans Line 5
peut être évalué comme:
x<(y<z)
ou (x<y)<z
. Dans le premier cas, la valeur de result
sera 0
et dans le second cas result
sera 1
. Mais nous savons que, lorsque le Operator Precedence
est Equal/Same
- Associativity
entre en jeu, donc, est évaluée comme (x<y)<z
.
C'est ce qui est dit dans ce Article MSDN:
La priorité et associativité des opérateurs de C affectent le regroupement et l'évaluation des opérandes dans les expressions. Un opérateur de priorité n'a de sens que si d'autres opérateurs plus ou moins élevés de précédence sont présents. Expressions avec plus de précédence des opérateurs sont évalués en premier. La priorité peut aussi être décrit par le mot "contraignantes". Les opérateurs avec une priorité plus élevée sont dit d'avoir le resserrement de liaison.
Maintenant, à propos de l'article ci-dessus:
Il mentionne les "Expressions avec plus de précédence des opérateurs sont évalués en premier."
Il peut sembler incorrecte. Mais, je pense que l'article n'est pas de dire quelque chose de mal si l'on considère que ()
est également un opérateur x<y<z
est la même que (x<y)<z
. Mon raisonnement est que si l'associativité n'est en jeu, alors les expressions complètes évaluation devient ambigu car <
n'est pas un Point de Séquence.
Aussi, un autre lien que j'ai trouvé dit cela à Priorité et Associativité des opérateurs:
Cette page répertorie les opérateurs de C dans l'ordre de priorité (de la plus haute à la plus basse). Leur associativité indique dans quel ordre les opérateurs de même priorité, dans une expression sont appliquées.
Donc, en prenant, le deuxième exemple de int result=x<y<z
, nous pouvons voir ici qu'il y a en tout 3 expressions, x
, y
et z
, depuis, la forme la plus simple d'une expression est composée d'une seule constante littérale ou d'un objet. Donc le résultat des expressions x
, y
, z
serait là rvalues, c'est à dire, 10
, 1
et 2
respectivement. Donc, maintenant nous pouvons interpréter x<y<z
comme 10<1<2
.
Maintenant, n'est-ce pas l'Associativité entrent en jeu depuis maintenant, nous avons 2 expressions à évaluer, soit 10<1
ou 1<2
et puisque la priorité de l'opérateur est même, ils sont évaluées de gauche à droite?
Prenant ce dernier exemple que mon argument:
int myval = ( printf("Operator\n"), printf("Precedence\n"), printf("vs\n"),
printf("Order of Evaluation\n") );
Maintenant dans l'exemple ci-dessus, depuis le comma
opérateur a même ordre de priorité, les expressions sont évaluées left-to-right
et la valeur de retour de la dernière printf()
est stocké dans myval
.
Dans DONC/IEC 9899:201x sous J. 1 comportement non spécifié il mentionne:
L'ordre dans lequel les sous-expressions sont évaluées et l'ordre dans lequel les effets secondaires
prendre place, sauf tel que spécifié pour la fonction call (), &&, ||, ?:, et la virgule
les opérateurs (6.5).
Maintenant je voudrais savoir, serait-il faux de dire:
Ordre de l'Évaluation dépend de la priorité des opérateurs, laissant les cas de Comportement non spécifié.
Je voudrais être corrigé si des erreurs ont été faits dans quelque chose je l'ai dit dans ma question.
La raison que j'ai posté cette question est en raison de la confusion créée dans mon esprit par l'Article MSDN. Est-il dans Erreur ou pas?
- Je suis avec vous tout le chemin jusqu'à la conclusion, mais ne voyez pas où vous obtenez le comportement quelconque.
- Comme je l'ai lu la norme,
(x<y)<z
ne fait que clarifier l'associativité/priorité. Les deux sous-expressions(x<y)
etz
pu être évalués dans l'ordre. - Persson:: La raison pour laquelle j'ai de l'etat un comportement non spécifié est parce que dans des situations comme:
obj + obj++
le comportement est indéterminé, même si l'ordre de préséance est le même. - Je ne sais pas à propos de
C++
. LeC
Norme ne définit pas une "priorité"; il définit une grammaire. La grammaire pour l'évaluation de l'expression impose un certain groupement de sous-expressions. - La priorité des opérateurs n'est pas spécifiée directement, mais elle peut être obtenue à partir de la syntaxe.
- Précisément mon point de vue!! Donc, qui devrait
MSDN
article être considéré imparfait ou pas? - Dépend. Cette affirmation est peut-être vrai pour MSVC - mais ce n'est pas le cas pour le c++ en général, autant que je puisse en dire.
- Le comportement de l'obj + obj++ n'est pas défini, pas juste non spécifié. C'est undefined, car la norme explicitement dit, mais la priorité n'a rien à voir avec la question.
- Je voudrais savoir à partir d'un C Standard point de vue.
- Aucune idée sur C. Pour C++, je suis pratiquement certaine que mon commentaire initial est correct.
- Lire ma réponse. La priorité et associativité des règles de définir la forme de l'expression de l'arbre, et non pas l'ordre dans lequel il sera évalué.
- Kanze:: Désolé, c'était une faute de frappe. je voulais dire
++obj + obj++
oùobj
est un objet de certains type de classe et où++
est surchargé, d'où l'évaluation est indéterminé et semblable àf1() + f2()
, . - La confusion est surgissent à cause de mon les commentaires ici.
- Lire Jerry Cercueil de réponse. Il sera clairement de toutes vos confusions.
- double possible de l'ordre d'évaluation des opérandes
Vous devez vous connecter pour publier un commentaire.
Oui, l'article MSDN est dans l'erreur, au moins à l'égard de la norme de C et de C++1.
Cela dit, permettez-moi de commencer par une remarque sur la terminologie: dans la norme C++, ils ont (pour la plupart--il y a quelques dérapages) l'utilisation "d'évaluation" pour se référer à l'évaluation d'un opérande, et la "valeur de calcul" pour se référer à la réalisation d'une opération. Donc, quand (par exemple) vous ne
a + b
, chacun dea
etb
est évaluée, la valeur de calcul est effectué afin de déterminer le résultat.Il est clair que l'ordre de la valeur des calculs est (principalement) contrôlé par priorité et associativité des opérateurs--le contrôle de la valeur des calculs est fondamentalement la définition de ce que la priorité et l'associativité sont. Le reste de cette réponse utilise "évaluation" pour se référer à l'évaluation des opérandes, et non pas à la valeur des calculs.
Maintenant, quant à l'évaluation de l'ordre étant déterminé par priorité, non, il n'est pas! C'est aussi simple que cela. Juste pour exemple, prenons votre exemple de
x<y<z
. Selon les règles d'associativité, cette analyse comme(x<y)<z
. Maintenant, pensez à évaluer cette expression sur une pile de linge. Il est parfaitement admissible qu'un à faire quelque chose comme ceci:Cette évalue
z
avantx
ouy
, mais encore évalue(x<y)
, puis de comparer le résultat de cette comparaison àz
, tout comme il est censé le faire.Résumé: de l'Ordre d'évaluation est indépendante de l'associativité.
L'ordre de préséance est le même manière. On peut changer l'expression de
x*y+z
, et encore évaluerz
avantx
ouy
:Résumé: de l'Ordre d'évaluation est indépendante de la préséance.
Quand/si on ajoute des effets secondaires, cela reste la même. Je pense que c'est l'éducation à penser à des effets secondaires comme étant effectuées par un autre thread d'exécution, avec un
join
à la séquence suivante (par exemple, la fin de l'expression). Donc, quelque chose commea=b++ + ++c;
pourrait être exécuté à quelque chose comme ceci:Cela montre aussi pourquoi une apparente de dépendance n'a pas nécessairement d'incidence sur l'ordre d'évaluation. Même si
a
est la cible de la mission, c'est encore évaluea
avant l'évaluation soitb
ouc
. Notez également que, bien que je l'ai écrit comme "fil" ci-dessus, cela pourrait tout aussi bien être un piscine de threads s'exécutant en parallèle, de sorte que vous n'obtenez pas de garantie sur l'ordre d'un incrément par rapport à une autre, soit.À moins que le matériel avait direct (et pas cher) le support pour les threads, les files d'attente, ce ne serait probablement pas être utilisé dans une réelle mise en œuvre (et même alors, il est très peu probable). Mettre quelque chose dans un thread-safe file d'attente aura normalement un peu plus de généraux que de le faire une seule fois, de sorte qu'il est difficile d'imaginer qu'on ait jamais fait cela dans la réalité. Sur le plan conceptuel, cependant, l'idée est s'adapte aux exigences de la norme: lorsque vous utilisez un pré/post incrémentation/décrémentation de l'opération, vous êtes en spécifiant une opération qui va se produire peu de temps après que la partie de l'expression est évaluée, et prendra fin lors de la prochaine séquence de point.
Edit: si ce n'est pas exactement le filetage, certaines architectures ne permettent une telle exécution parallèle. Pour un couple d'exemples, l'Itanium d'Intel et les processeurs VLIW comme certains Dsp, de permettre à un compilateur pour désigner un certain nombre d'instructions qui seront exécutées en parallèle. La plupart des VLIW machines ont une instruction spécifique "paquet" de taille qui limite le nombre d'instructions exécutées en parallèle. L'Itanium utilise également des paquets d'instructions, mais désigne un peu dans une instruction de paquets-à-dire que les instructions contenues dans le paquet actuel peut être exécutée en parallèle avec ceux du paquet suivant. À l'aide de mécanismes comme cela, vous obtenez des instructions à exécuter en parallèle, tout comme si vous avez utilisé plusieurs threads sur des architectures avec laquelle la plupart d'entre nous sont plus familiers.
Résumé: de l'Ordre d'évaluation est indépendante de l'apparente dépendances
Toute tentative à l'aide de la valeur avant le prochain point de séquence donne un comportement non défini-en particulier, "l'autre "thread" est (potentiellement) la modification des données au cours de ce temps, et vous avez pas mode de synchronisation d'accès avec les autres fil. Toute tentative d'utilisation elle conduit à un comportement indéfini.
Juste pour un (certes, maintenant, plutôt farfelue), par exemple, pensez à votre code en cours d'exécution sur une machine virtuelle 64 bits, mais le matériel réel est une 8-bits processeur. Lorsque vous incrémenter une version 64-bit de la variable, il exécute une séquence de quelque chose comme:
Si vous lisez la valeur quelque part au milieu de cette séquence, vous pourriez obtenir quelque chose avec uniquement quelques octets modifiés, de sorte que vous obtenez n'est ni l'ancienne valeur ni la nouvelle.
Cet exemple précis peut être assez farfelue, mais une version moins extrême (par exemple, un 64-bit de la variable sur une machine 32 bits) est en fait assez commun.
Conclusion
De l'ordre d'évaluation n' pas dépendent de priorité, l'associativité, ou (forcément) sur l'apparente dépendances. Tentative d'utilisation d'une variable à laquelle un pré/post incrémentation/décrémentation a été appliquée dans toute autre partie de l'expression donne vraiment complètement un comportement indéfini. Alors que d'une réelle panne est peu probable, vous êtes certainement pas la garantie d'obtenir soit l'ancien ou le nouveau -- vous pourriez trouver quelque chose d'autre entièrement.
1 je n'ai pas vérifié cet article en particulier, mais tout à fait quelques articles MSDN parler de Microsoft Managed C++ et/ou C++/CLI (ou sont spécifiques à leur mise en œuvre de C++), mais faire peu ou rien à signaler qu'ils ne s'appliquent pas à la norme de C ou de C++. Cela peut donner la fausse impression qu'ils sont en prétendant les règles qu'ils ont décidé d'appliquer à leurs propres langues réellement s'appliquer à la norme langues. Dans ces cas, les articles ne sont pas techniquement faux -- ils n'ont rien à voir avec la norme C ou C++. Si vous essayez d'appliquer ces déclarations à la norme C ou C++, le résultat est faux.
a*(b+c)
(en supposant que a, b, et c sont des entiers). Dans l'original, deux multiplications précèdent plus, mais la version transformée a une multiplication ultérieure de l'addition. En fin de compte, oui, il y a à évaluer quelques arbres, feuilles, racine, mais de l'arbre, il évalue souvent ne reflètent directement ce que vous avez écrit.(a++ + b) * c
, puis l'addition est nécessairement évalués avant la multiplication, car le résultat de la multiplication dépend le résultat de l'addition. C'est juste la façon dont l'expression de l'arbre s'avère look. C'est la raison pour laquelle l'ensemblef().g()
va d'abord exécuterf
et puisg
, parce quef()
est évalué avantg
est appelée, comme il y a une apparente dépendance.(a++*c)+(b*c)
si elle ne viole pas le cas de la règle. Mais il peut faire tout et tout, si quelque chose ne va pas à l'encontre de l', comme si la règle. Par exemple, sic
serait une variable volatile, il ne pouvait pas évaluer dans qui a transformé la manière. Notez que je ne dis pas quea
a été incrémenté au moment de la multiplication. Qui aurait besoin d'un point de séquence, ce qui n'est pas là. En gros, je suis d'exprimer ce @JamesKanze a dit dans sa réponse.temp=c
, puis utiliseztemp
dans la modification de l'expression. Ce qui nous reste est assez simple: en fonction de l'ordre d'évaluation au-delà de ce qui est défini par une séquence de points est une très mauvaise idée, au mieux. Oui, vous pourriez être en mesure de déduire une partie de ce qui est susceptible de se produire de temps en temps, mais selon qu'il est toujours une très mauvaise idée, presque tout ce que vous pensez que vous avez déterminé stands au moins quelque chance d'être mal-et même si vous avez raison, selon elle conduit à la fragilité (c'est à dire, quelqu'un qui en sait moins que vous ne susceptible de le casser).temp=c
et puis, à l'aide detemp
dans la modification de l'expression. Je pense que c'est très bien. "selon l'ordre de l'évaluation au-delà de ce qui est défini par une séquence de points est une très mauvaise idée, au mieux". Oui je suis d'accord, que c'est seulement en fonction de l'ordre d'évaluation est presque inutile si il y a des effets secondaires dans le jeu, parce que les effets secondaires ne sont séquencés par la séquence de points.a=b++ + ++c;
exemple, que la cessiona
est également un effet secondaire? C'est pourquoi (au moins en C++11)i=i++;
est pas défini: parce que les effets secondaires de l'incrémentation et de l'affectation ài
sont séquencé à l'égard l'un de l'autre.a
,b
etc
peut être évalué dans n'importe quel ordre. C++17 ajout de quelques nouvelles contraintes sur l'ordre d'évaluationLa seule façon de priorité influence de l'ordre d'évaluation est qu'il
crée des dépendances; sinon, les deux sont orthogonaux. Vous avez
soigneusement choisis trivial exemples où les dépendances créées par
la priorité ne finissent par complètement la définition de l'ordre d'évaluation, mais ce n'est pas
généralement vrai. Et n'oubliez pas, non plus, que de nombreuses expressions ont
deux effets: ils entraînent une valeur, et ils ont des effets secondaires. Ces
deux ne sont pas tenus de se produire ensemble, de sorte que même lorsque les dépendances
la force d'une commande spécifique de l'évaluation, c'est seulement de l'ordre de
l'évaluation des valeurs; il n'a pas d'effet sur les effets secondaires.
foo(a) + foo(b)
,foo(b)
pourrait avoir été évalués beaucoup plus tôt quefoo(a)
.Une bonne façon de voir cela est de prendre l'expression de l'arbre.
Si vous avez une expression, permet de dire
x+y*z
vous pouvez réécrire que dans une arborescence d'expression:L'application de la priorité et l'associativité des règles:
Après l'application de la priorité et l'associativité des règles, vous pouvez les oublier.
Dans la forme de l'arbre:
Maintenant les feuilles de cette expression sont
x
,y
etz
. Ce que cela signifie est que vous pouvez évaluerx
,y
etz
dans n'importe quel ordre que vous voulez, et aussi cela signifie que vous pouvez évaluer le résultat de*
etx
dans n'importe quel ordre.Maintenant, depuis que ces expressions n'ont pas d'effets secondaires, vous ne m'en soucie pas vraiment. Mais s'ils le font, la commande peut changer le résultat, et depuis la commande peut être tout ce que le compilateur décide, vous avez un problème.
Maintenant, la séquence des points d'apporter un peu d'ordre dans ce chaos. Ils ont effectivement couper l'arbre en sections.
x + y * z, z = 10, x + y * z
après la priorité et l'associativité
x + ( y * z ) , z = 10, x + ( y * z)
l'arbre:
La partie supérieure de l'arbre sera évalué avant le milieu, et le milieu avant d'en bas.
x+y*z
signifie que pour l'évaluation de+
vous avez besoin de la suite de*
. C'est la commande qui est défini par la priorité de l'opérateur. Maintenant, l'ordre d'évaluation doit suivre cette partielle de la commande, mais en dehors de cela, il peut faire ce qu'il veut. Donc non, vous ne pouvez pas vraiment dire qu'ils sont totalement indépendants. Mais ils ne sont pas définies par les uns des autres, soit.First, precedence defines partial order. x+y*z means that for evaluating + you need the result of *.
. C'est simplement faux. Les opérateurs ne sont pas évaluées, les opérandes ne.that for evaluating +
? Comment quelqu'un pourrait éventuellement évaluer+
?+
dansx + y * z
vous demandez-vous? Par l'évaluation de ses opérandes en premier et quand ils sont évalués, par l'évaluation de l'expression elle-même.Je vais juste répéter ce que j'ai dit ici. Standard C et C++ sont concernés que l'article est défectueux. La priorité n'affecte que les jetons sont considérés comme les opérandes de chaque opérateur, mais il n'a pas d'incidence sur l'ordre d'évaluation.
Donc, le lien n'explique comment Microsoft a mis en place des choses, pas la façon dont le langage lui-même fonctionne.
1 + 2 + 3 * 4
, où 1 + 2 peuvent être évalués avant d'3 * 4 temps * a une priorité supérieure à +Priorité n'a rien à voir avec l'ordre d'évaluation et vice-versa.
Priorité règles de décrire comment un underparenthesized expression doivent être mis entre parenthèses lors de l'expression des mélanges de différents types d'opérateurs. Par exemple, la multiplication est de plus grande priorité que plus, donc
2 + 3 x 4
est équivalent à2 + (3 x 4)
, pas(2 + 3) x 4
.Ordre d'évaluation règles de décrire l'ordre dans lequel chaque opérande dans une expression est évaluée.
Prendre un exemple
En exploitant la règle de priorité, il sera parenthesize comme (
++/--
a une priorité plus élevée que||
qui a une priorité plus élevée que=
):L'ordre de l'évaluation de logique OU
||
états (C11 6.5.14)Cela signifie que l'opérande de gauche, j'.e la sous-expression
(x++)
sera évalué en premier. Par court-circuit comportement; Si le premier opérande compare pas0
, le deuxième opérande n'est pas évalué, opérande de droite--y
ne seront pas évalués, même si elle est parenthesize avant que(++x) || (--y)
.Je pense que c'est seulement le
epxression problématique, parce que les
s'inscrit d'abord dans le 3. mais ensuite, dans les 6. règle: l'évaluation complète avant la cession.
Donc,
obtient pour a=1 entièrement évalué à:
Le résultat est le même = 4.
Un
aurait des résultats indéfinis. N'est-ce pas?
a++ + ++a
modifiea
deux fois entre la séquence de points, le comportement est indéfini; il n'y a pas de résultat correct (ou chaque résultat est correct, même si la réponse est -2,000,000,000).