27 votes

Pourquoi (x + = x + = 1) est-il évalué différemment en C et Javascript?

Si la valeur de la variable x est initialement 0, l'expression x += x += 1 permettra d'évaluer à 2 en C, et 1 en Javascript.

La sémantique de C me semble évident: x += x += 1 est interprété comme x += (x += 1) qui est, à son tour, l'équivalent de

x += 1
x += x  // where x is 1 at this point

Quelle est la logique derrière le Javascript de l'interprétation? Ce qui spécification applique ce genre de comportement? (Il convient de noter, en passant, que Java est d'accord avec Javascript ici).

Mise à jour: Il s'avère que l'expression x += x += 1 a un comportement indéterminé selon la norme (merci ouah, Jean-Bode, DarkDust, a Attiré Dormann), qui semble gâcher le point de l'ensemble de la question pour certains lecteurs. L'expression peut être rendu conforme aux normes par l'insertion d'une identité de fonction comme suit: x += id(x += 1). La même modification peut être faite pour le code Javascript et la question demeure comme indiqué. En supposant que la majorité des lecteurs peut comprendre le point de "non-conforme aux normes" formulation, je vais la garder comme elle est plus concise.

Mise à jour 2: Il s'avère que, selon C99 l'introduction de la fonction identité est sans doute pas la résolution de l'ambiguïté. Dans ce cas, cher lecteur, veuillez considérer la question d'origine, comme se rapportant à C++ plutôt que de C99, où "+=" peut-être plus probablement, en toute sécurité, être considérée comme une overloadable opérateur défini de manière unique la séquence des opérations. C'est, x += x += 1 est maintenant équivalent à operator+=(x, operator+=(x, 1)). Désolé pour la longue route de respect des standards.

27voto

ouah Points 75311

x += x += 1; est un comportement indéfini en C.

L'instruction d'expression viole les règles de points de séquence.

(C99, 6.5p2) "Entre le point de séquence précédent et suivant, un objet doit voir sa valeur stockée modifiée au plus une fois par l'évaluation d'une expression."

15voto

Michael Burr Points 181287

JavaScript et Java ont à peu près strictement de gauche-à-droite pour les règles d'évaluation de cette expression. C n'est pas (même dans la version que vous avez fournie, qui est la fonction identité (intervenant)).

L'ECMAScript spec que j'ai (3e Édition, qui je vous l'avoue est assez ancien – la version actuelle peut être trouvé ici: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf- dit que le composé de l'affectation des opérateurs sont évalués comme suit:

11.13.2 Composé d'Affectation ( op= )

La production AssignmentExpression : LeftHandSideExpression @ = AssignmentExpression, où@ représente l'un des opérateurs indiqué ci-dessus, est évaluée comme suit:

  1. Évaluer LeftHandSideExpression.
  2. Appel GetValue(Résultat(1)).
  3. Évaluer AssignmentExpression.
  4. Appel GetValue(Résultat(3)).
  5. Appliquer l'opérateur @ en Résultat(2) et(4).
  6. Appel PutValue(Résultat(1), (5)).
  7. Résultat De Retour(5)

Vous notez que Java a le même comportement que le JavaScript je pense que sa spécification est plus lisible, donc je vais poster quelques extraits ici (http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.7):

15.7 Ordre D'Évaluation

Le langage de programmation Java garantit que les opérandes de les opérateurs semblent être évalué dans un ordre d'évaluation, à savoir, de gauche à droite.

Il est recommandé que le code ne dépendent fondamentalement de la présente spécification. Le Code est généralement plus clair lors de chaque expression contient au plus un côté effet, comme son ultrapériphériques de l'opération, et lorsque le code ne dépend pas de exactement les exceptions qui se pose comme une conséquence de la gauche-à-droite l'évaluation des expressions.

15.7.1 Évaluer de Gauche Premier Opérande gauche de l'opérande d'un opérateur binaire semble être pleinement évalués avant qu'une partie de la de la main droite opérande est évaluée. Par exemple, si la gauche opérande contient une affectation à une variable et la partie droite de l'opérande contient une référence à la même variable, alors la valeur produite par la référence tiendra compte du fait que la cession s'est produite d'abord.

...

Si l'opérateur est un composé opérateur d'affectation (§15.26.2), puis évaluation de la gauche opérande comprend à la fois de se souvenir de la variable que la gauche opérande indique et de l'extraction et de l'économie que la valeur de la variable pour une utilisation dans l'implicite combinant l'opération.

D'autre part, dans le pas-undefined-comportement exemple, lorsque vous fournissez un intermédiaire de l'identité de la fonction:

x += id(x += 1);

alors que ce n'est pas un comportement indéfini (depuis l'appel de la fonction fournit un point de séquence), c'est toujours un comportement non spécifié si la gauche x est évaluée avant l'appel de fonction ou après. Ainsi, alors que c'est pas "n'importe quoi" va de comportement indéfini, le compilateur C est encore permis d'évaluer à la fois x variables avant l'appel de la id() fonction, auquel cas la dernière valeur stockée dans la variable sera 1:

Par exemple, si x == 0 pour commencer, l'évaluation pourrait ressembler à:

tmp = x;    // tmp == 0
x = tmp  +  id( x = tmp + 1)
// x == 1 at this point

ou il pourrait l'évaluer comme suit:

tmp = id( x = x + 1);   // tmp == 1, x == 1
x = x + tmp;
// x == 2 at this point

Note qu'un comportement non spécifié est subtilement différent de comportement indéfini, mais c'est toujours pas souhaitable de comportement.

9voto

Drew Dormann Points 25025

En C, x += x += 1 est un comportement non défini .

Vous ne pouvez pas compter sur un résultat cohérent car il n'est pas défini pour essayer de mettre à jour le même objet deux fois entre les points de séquence .

5voto

DarkDust Points 47584

Au moins en C, c'est un comportement indéfini. L'expression x += x+= 1; a deux séquence de points: implicite, juste avant l'expression commence (c'est-à: la séquence précédente, le point), puis à nouveau à l' ;. Entre ces deux séquences points de x est modifié à deux reprises et cette explicitement indiqué comme un comportement non défini par le standard C99. Le compilateur est libre de faire ce qu'il aime à ce point, y compris en rendant les démons de voler hors de votre nez. Si vous avez de la chance, il fait simplement ce que vous attendez, mais il n'y a aucune garantie pour que.

C'est la même raison pourquoi x = x++ + x++; n'est pas défini dans C. Voir aussi le C-FAQ pour plus d'exemples et d'explications de ce ou de la StackOverflow C++ entrée de la FAQ Comportement Indéfini et de la Séquence des Points (autant que je sache, le C++ règles sont les mêmes que pour le C).

5voto

John Bode Points 33046

Plusieurs questions sont en jeu ici.

Premier et le plus important, c'est cette partie du langage C spécifications:

6.5 Expressions
...
2 Entre le précédent et suivant de la séquence de point d'un objet doit avoir sa valeur stockée modifié plus d'une fois par l'évaluation d'une expression.72) en Outre, l'état de la valeur doit être en lecture seule pour déterminer la valeur à stocker.73)
...
72) A floating-point de l'indicateur d'état n'est pas un objet et peut être plus d'une fois à l'intérieur d'une expression.

73) Ce paragraphe rend pas défini déclaration des expressions telles que
 i = ++i + 1;
 a[i++] = i;
tout en permettant
 i = i + 1;
 a[i] = i;

C'est moi qui souligne.

L'expression x += 1 modifie x (effet de bord). L'expression x += x += 1 modifie x deux fois sans l'intermédiaire d'un point de séquence, et ce n'est pas la lecture de l'état de la valeur uniquement pour déterminer la nouvelle valeur stockée; par conséquent, le comportement est indéfini (ce qui signifie tout résultat est également correct). Maintenant, pourquoi sur Terre serait-ce un problème? Après tout, += est en droit associatif, et tout est évaluée de gauche à droite, de droite?

Mal.

3 Le groupement d'opérateurs et d'opérandes est indiqué par la syntaxe.74) , Sauf comme indiqué plus tard (pour la fonction appel- (), &&, ||, ?:, et la virgule opérateurs), l'ordre d'évaluation des sous-expressions et l'ordre dans lequel les effets secondaires sont à la fois non spécifié.
...
74) La syntaxe spécifie la priorité des opérateurs dans l'évaluation d'une expression, qui est la même comme l'ordre des grandes subdivisions de cette subdivision, plus haute priorité au premier. Ainsi, par exemple, l' expressions comme opérandes de l'binaire + opérateur (6.5.6) sont les expressions définis dans 6.5.1 par 6.5.6. Les exceptions sont en fonte d'expressions (6.5.4) comme opérandes des opérateurs unaires (6.5.3), et un opérande contenues entre des paires suivantes des opérateurs: le regroupement entre parenthèses, () (6.5.1), subscripting crochets [] (6.5.2.1), la fonction d'appel parenthèses () (6.5.2.2), et l'opérateur conditionnel ?:(6.5.15).

C'est moi qui souligne.

En général, la priorité et l'associativité de ne pas affecter l'ordre d'évaluation ou l'ordre dans lequel les effets secondaires sont appliquées. Voici un exemple d'évaluation de la séquence:

 t0 = x + 1 
 t1 = x + t0
 x = t1
 x = t0

Oups. Pas ce que nous voulions.

Maintenant, d'autres langages tels que Java et C# (et je suppose que Javascript) ne spécifier que les opérandes sont toujours évaluées de gauche à droite, donc il y a toujours un ordre précis de l'évaluation.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X