Lassen Sie mich das ganz klar sagen, weil die Leute das die ganze Zeit falsch verstehen:
Die Reihenfolge der Auswertung von Unterausdrücken ist unabhängig von Assoziativität und Vorrang . Assoziativität und Priorität bestimmen, in welcher Reihenfolge die Operatoren ausgeführt werden, bestimmen jedoch nicht , in welcher Reihenfolge die Unterausdrücke ausgewertet werden. Ihre Frage bezieht sich auf die Reihenfolge, in der Unterausdrücke ausgewertet werden.
Überlegen Sie A() + B() + C() * D()
. Die Multiplikation hat eine höhere Priorität als die Addition, und die Addition ist linksassoziativ. Dies entspricht jedoch dem (A() + B()) + (C() * D())
Wissen, dass die erste Addition vor der zweiten Addition und die Multiplikation vor der zweiten Addition erfolgt. Es sagt Ihnen nicht, in welcher Reihenfolge A (), B (), C () und D () aufgerufen werden! (Es sagt Ihnen auch nicht, ob die Multiplikation vor oder nach der ersten Addition erfolgt.) Es wäre durchaus möglich, die Vorrang- und Assoziativitätsregeln zu befolgen, indem Sie Folgendes kompilieren:
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
Dort werden alle Vorrang- und Assoziativitätsregeln befolgt - die erste Addition erfolgt vor der zweiten Addition und die Multiplikation erfolgt vor der zweiten Addition. Natürlich können wir die Aufrufe von A (), B (), C () und D () in beliebiger Reihenfolge ausführen und trotzdem die Vorrang- und Assoziativitätsregeln einhalten!
Wir brauchen eine Regel, die nicht mit den Vorrang- und Assoziativitätsregeln zusammenhängt, um die Reihenfolge zu erklären, in der die Unterausdrücke ausgewertet werden. Die relevante Regel in Java (und C #) lautet "Unterausdrücke werden von links nach rechts ausgewertet". Da A () links von C () erscheint, wird A () zuerst ausgewertet, unabhängig davon, dass C () an einer Multiplikation beteiligt ist und A () nur an einer Addition beteiligt ist.
Jetzt haben Sie genug Informationen, um Ihre Frage zu beantworten. In a[b] = b = 0
den Regeln der Assoziativität heißt es, dass dies a[b] = (b = 0);
aber nicht bedeutet, dass das b=0
zuerst läuft! Die Vorrangregeln besagen, dass die Indizierung eine höhere Vorrangstellung als die Zuweisung hat. Dies bedeutet jedoch nicht, dass der Indexer vor der Zuweisung ganz rechts ausgeführt wird .
(UPDATE: Eine frühere Version dieser Antwort hatte einige kleine und praktisch unwichtige Auslassungen im folgenden Abschnitt, die ich korrigiert habe. Ich habe auch einen Blog-Artikel geschrieben, in dem beschrieben wird, warum diese Regeln in Java und C # sinnvoll sind: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )
Rangfolge und Assoziativität uns nur sagen , dass die Zuordnung von Nullb
passieren muss , bevor die Zuordnung zu a[b]
, weil die Zuordnung von Null berechnet der Wert, der in dem Indexierungsvorgang zugeordnet ist. Vorrang und Assoziativität allein sagen nichts darüber aus, ob das vor oder nach dem a[b]
bewertet wird .b=0
Dies ist wiederum genau das Gleiche wie: A()[B()] = C()
- Wir wissen nur, dass die Indizierung vor der Zuweisung erfolgen muss. Wir wissen nicht, ob A (), B () oder C () zuerst ausgeführt werden, basierend auf Vorrang und Assoziativität . Wir brauchen eine andere Regel, um uns das zu sagen.
Die Regel lautet wiederum: "Wenn Sie die Wahl haben, was zuerst zu tun ist, gehen Sie immer von links nach rechts." In diesem speziellen Szenario gibt es jedoch eine interessante Falte. Wird der Nebeneffekt einer ausgelösten Ausnahme, der durch eine Nullsammlung oder einen Index außerhalb des Bereichs verursacht wird, als Teil der Berechnung der linken Seite der Zuweisung oder als Teil der Berechnung der Zuweisung selbst betrachtet? Java wählt letzteres. (Dies ist natürlich eine Unterscheidung, die nur dann von Bedeutung ist, wenn der Code bereits falsch ist , da der richtige Code nicht null dereferenziert oder überhaupt einen schlechten Index übergibt.)
Was passiert also?
- Das
a[b]
ist links von b=0
, also a[b]
läuft das zuerst , was dazu führt a[1]
. Die Überprüfung der Gültigkeit dieses Indizierungsvorgangs wird jedoch verzögert.
- Dann
b=0
passiert das.
- Dann erfolgt die Überprüfung,
a
die gültig ist und a[1]
sich in Reichweite befindet
- Die Zuordnung des Wertes
a[1]
erfolgt zuletzt.
Obwohl in diesem speziellen Fall einige Feinheiten für die seltenen Fehlerfälle zu berücksichtigen sind, die überhaupt nicht im richtigen Code auftreten sollten, können Sie im Allgemeinen argumentieren: Dinge auf der linken Seite passieren vor Dingen auf der rechten Seite . Das ist die Regel, nach der Sie suchen. Die Rede von Vorrang und Assoziativität ist sowohl verwirrend als auch irrelevant.
Die Leute verstehen dieses Zeug die ganze Zeit falsch , sogar Leute, die es besser wissen sollten. Ich habe viel zu viele Programmierbücher bearbeitet , in denen die Regeln falsch angegeben wurden. Daher ist es nicht verwunderlich, dass viele Menschen völlig falsche Vorstellungen über die Beziehung zwischen Vorrang / Assoziativität und Bewertungsreihenfolge haben - nämlich, dass es in Wirklichkeit keine solche Beziehung gibt ;; Sie sind unabhängig.
Wenn Sie dieses Thema interessiert, lesen Sie meine Artikel zum Thema zur weiteren Lektüre:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
Es geht um C #, aber das meiste davon gilt auch für Java.