Permettez-moi de le dire très clairement, car les gens comprennent mal cela tout le temps:
L'ordre d'évaluation des sous-expressions est indépendant à la fois de l'associativité et de la préséance . L'associativité et la priorité déterminent dans quel ordre les opérateurs sont exécutés mais ne déterminent pas dans quel ordre les sous - expressions sont évaluées. Votre question porte sur l'ordre dans lequel les sous - expressions sont évaluées.
Considérez A() + B() + C() * D()
. La multiplication a une priorité plus élevée que l'addition, et l'addition est associative à gauche, donc cela équivaut à (A() + B()) + (C() * D())
Mais sachant que cela vous indique seulement que la première addition se produira avant la deuxième addition, et que la multiplication se produira avant la deuxième addition. Il ne vous dit pas dans quel ordre A (), B (), C () et D () seront appelés! (Il ne vous dit pas non plus si la multiplication se produit avant ou après la première addition.) Il serait parfaitement possible d'obéir aux règles de préséance et d'associativité en compilant ceci comme:
d = D()
b = B()
c = C()
a = A()
sum = a + b
product = c * d
result = sum + product
Toutes les règles de préséance et d'associativité y sont suivies - la première addition se produit avant la deuxième addition, et la multiplication se produit avant la deuxième addition. Clairement, nous pouvons faire les appels à A (), B (), C () et D () dans n'importe quel ordre tout en obéissant aux règles de préséance et d'associativité!
Nous avons besoin d'une règle sans rapport avec les règles de préséance et d'associativité pour expliquer l'ordre dans lequel les sous-expressions sont évaluées. La règle pertinente en Java (et C #) est "les sous-expressions sont évaluées de gauche à droite". Puisque A () apparaît à gauche de C (), A () est évalué en premier, indépendamment du fait que C () est impliqué dans une multiplication et A () n'est impliqué que dans une addition.
Alors maintenant, vous avez suffisamment d'informations pour répondre à votre question. Dans a[b] = b = 0
les règles d'associativité disons que c'est, a[b] = (b = 0);
mais cela ne veut pas dire que les b=0
courses en premier! Les règles de priorité indiquent que l'indexation a une priorité plus élevée que l'affectation, mais cela ne signifie pas que l'indexeur s'exécute avant l'affectation la plus à droite .
(MISE À JOUR: Une version précédente de cette réponse avait quelques petites omissions pratiquement sans importance dans la section qui suit que j'ai corrigée. J'ai également écrit un article de blog décrivant pourquoi ces règles sont sensées en Java et C # ici: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
La préséance et l'associativité nous indiquent seulement que l'affectation de zéro à b
doit se produire avant l'affectation à a[b]
, car l'affectation de zéro calcule la valeur qui est affectée dans l'opération d'indexation. La préséance et l'associativité seules ne disent rien sur le fait de savoir si le a[b]
est évalué avant ou après le b=0
.
Encore une fois, c'est exactement la même chose que: A()[B()] = C()
- Tout ce que nous savons, c'est que l'indexation doit avoir lieu avant l'affectation. Nous ne savons pas si A (), B () ou C () s'exécute en premier en fonction de la priorité et de l'associativité . Nous avons besoin d'une autre règle pour nous le dire.
La règle est, encore une fois, "lorsque vous avez le choix de ce que vous devez faire en premier, allez toujours de gauche à droite". Cependant, il y a une ride intéressante dans ce scénario spécifique. L'effet secondaire d'une exception levée est-il causé par une collection nulle ou un index hors plage considéré comme faisant partie du calcul du côté gauche de l'affectation, ou une partie du calcul de l'affectation elle-même? Java choisit ce dernier. (Bien sûr, c'est une distinction qui n'a d'importance que si le code est déjà erroné , car le code correct ne déréférencera pas null ou ne passera pas un mauvais index en premier lieu.)
Alors que se passe-t-il?
- Le
a[b]
est à gauche du b=0
, donc les a[b]
exécutions en premier , résultant en a[1]
. Cependant, la vérification de la validité de cette opération d'indexation est retardée.
- Puis le
b=0
se produit.
- Ensuite, la vérification qui
a
est valide et a[1]
est à portée se produit
- L'affectation de la valeur se
a[1]
produit en dernier.
Donc, bien que dans ce cas spécifique il y ait quelques subtilités à considérer pour ces rares cas d'erreur qui ne devraient pas se produire dans un code correct en premier lieu, en général, vous pouvez raisonner: les choses à gauche se produisent avant les choses à droite . C'est la règle que vous recherchez. Parler de priorité et d'associativité est à la fois déroutant et hors de propos.
Les gens se trompent tout le temps , même ceux qui devraient être mieux informés. J'ai édité beaucoup trop de livres de programmation qui énonçaient les règles de manière incorrecte, il n'est donc pas surprenant que beaucoup de gens aient des croyances complètement incorrectes sur la relation entre la préséance / associativité et l'ordre d'évaluation - à savoir qu'en réalité, il n'y a pas de telle relation. ; ils sont indépendants.
Si ce sujet vous intéresse, consultez mes articles sur le sujet pour en savoir plus:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Ils concernent C #, mais la plupart de ces éléments s'appliquent également à Java.