Frage zur Auswertungsreihenfolge von Operatoren in einem konkreten Fall



  • Hallo zusammen,

    ich bin C-Neuling und arbeite mich gerade fleißig in die Materie ein. Jetzt bin ich aber in meinem Lehrbuch an einer Stelle ins Stocken gekommen und hoffe hier freundlicherweise Hilfe zu bekommen, nachdem mich meine Internetrecherche bisher nicht weitergebracht hat. Und zwar geht es um folgenden Absatz:

    "Während beispielsweise bei a + b nicht festgelegt ist, ob erst a oder erst b ausgewertet wird, muss der Compiler bei A && B sowie bei A || B erst den Ausdruck A auswerten. Nebeneffekte des linken Operanden werden damit auch ausgeführt, bevor die weiteren Operanden ausgewertet werden. Die Auswertung wird abgebrochen, wenn das Ergebnis schon feststeht. Das kann dazu führen, dass Nebeneffekte der weiter rechts stehenden Ausdrücke nicht mehr ausgeführt werden: 1 < 0 && 2 < a++ / a++ wird nie ausgefuehrt, da vorher die Auswertung abgebrochen wird. */*"

    Mir ist klar, dass beim &&-Operator beide Ausdrücke wahr sein müssen, und dass somit, wenn der linke Ausdruck 1 < 0 bereits falsch ist, es keine Rolle mehr spielt, ob der rechte wahr oder falsch ist. Was ich aber nicht verstehe, ist, warum a++ in dem Fall nicht ausgeführt wird, denn schließlich hat - und das steht auch so in diesem Buch - der ++-Operator eine höhere Priorität als der &&-Operator. Demnach müsste doch eigentlich als Erstes a++ ausgewertet also a im Nebeneffekt um eins hochgezählt, dann als zweites die beiden Größer-Kleiner-Vergleiche durchgeführt und erst als Letztes die Verknüpfung über den &&-Operator ausgeführt werden.

    Meine Frage ist also: Warum wird der &&-Operator vor dem ++-Operator und den Größer-Kleiner-Vergleichen ausgewertet, obwohl er eine niedrigere Priorität hat?

    Ich bin irritiert. Danke für Eure Hilfe!



  • Es liegt an der Position des ++ operators. steht er vor a, dann wird a zu erst um 1 erhöht


  • Mod

    Bitmapper schrieb:

    Es liegt an der Position des ++ operators. steht er vor a, dann wird a zu erst um 1 erhöht

    Nein, das hat damit nichts zu tun.

    Laputzki schrieb:

    Meine Frage ist also: Warum wird der &&-Operator vor dem ++-Operator und den Größer-Kleiner-Vergleichen ausgewertet, obwohl er eine niedrigere Priorität hat?

    Wird er doch gar nicht. Du hast doch genau beschrieben, wie und warum er (hier) zuletzt ausgewertet wird. Wenn er zuerst ausgewertet würde, würde beim Gesamtausdruck 1 < 0 && 2 < a++ schließlich zuallererst 0 && 2 ausgewertet, was ganz klar nicht der Fall ist.



  • Die Operatorenrangfolge bestimmt lediglich, wo die Klammern sein würden, nicht die Auswertungsreihenfolge.



  • Generell schon, aber bei den logischen Operatoren && und || gilt die Kurzschlußauswertung (short circuit evaluation).



  • Ja, und? Was Nathan sagen will, ist folgendes:
    1 < 0 && 2 < a++
    sähe mit Klammern so aus:
    (1 < 0) && (2 < a++)
    Dass nun der links vom && stehende Teil zuerst ausgewertet wird, hat der TE ja schon richtig erkannt.
    Und danach ist dann wegen der short circuit evaluation Schluss, und deshalb wird a++ nicht ausgeführt.



  • Also zunächst mal ein Dankeschön an alle für die Antworten.

    Nathan schrieb:

    Die Operatorenrangfolge bestimmt lediglich, wo die Klammern sein würden, nicht die Auswertungsreihenfolge.

    Verstehe ich dich richtig, dass die allseits bekannte Prioritätenliste (erst Klammern, dann einstellige Operatoren, dann zweistellige, usw.) mir nur aufzeigt, wo ich Klammern zu setzen habe, aber keinerlei Aussage darüber trifft, welcher Operator als erstes ausgewertet wird?

    Gibt es denn für letzteres auch eine Liste, aus der ich hätte ersehen können, dass in meinem Beispiel der &&- vor dem ++-Operator ausgewertet wird?

    EDIT: @SeppJ: Du schreibst

    Wird er doch gar nicht. Du hast doch genau beschrieben, wie und warum er (hier) zuletzt ausgewertet wird. Wenn er zuerst ausgewertet würde, würde beim Gesamtausdruck 1 < 0 && 2 < a++ schließlich zuallererst 0 && 2 ausgewertet, was ganz klar nicht der Fall ist.

    Der &&-Operator wird offenbar als zweites nach dem linken <-Operator aber noch vor dem rechten <-Operator und dem ++-Operator ausgeführt, da er für einen Abbruch sorgt, der dazu führt, dass alles rechts von ihm (also sowohl der rechte <-Operator als auch der ++-Operator) nicht mehr ausgewertet wird. So versteh ich das.



  • Laputzki schrieb:

    Verstehe ich dich richtig, dass die allseits bekannte Prioritätenliste (erst Klammern, dann einstellige Operatoren, dann zweistellige, usw.) mir nur aufzeigt, wo ich Klammern zu setzen habe, ...

    Es zeigt eher, wo du die Klammern weglassen kannst.

    [quote="Laputzki"]Gibt es denn für letzteres auch eine Liste, aus der ich hätte ersehen können, dass in meinem Beispiel der &&- vor dem ++-Operator ausgewertet wird?
    Wenn du den Asudruck mit Klammern aufschreibst (als wenn alle Operatoren die gleiche Priorität hätten), bleibt am Ende ein Operator über.
    Mit dem fängst du an. Und daraus ergibt sich der Rest.

    Belli schrieb:

    sähe mit Klammern so aus:
    (1 < 0) && (2 < a++)

    (1 < 0) && (2 < (a++)) Da hast du das &&. Dafür musst du zuerst² den linken Klammer-Ausdruck auswerten. Danach wäre der rechte dran.

    ²sonst würde die Kurzschlußregel nicht funktionieren.



  • DirkB schrieb:

    Wenn du den Asudruck mit Klammern aufschreibst (als wenn alle Operatoren die gleiche Priorität hätten), bleibt am Ende ein Operator über.
    Mit dem fängst du an. Und daraus ergibt sich der Rest.

    Wenn alle die gleiche Priorität hätten, wäre es ja willkürlich, wo ich die Klammern setze. Also wäre z.B. auch das möglich: 1 < ((0 && 2) < a++).
    Deine Klammerung (1 < 0) && (2 < (a++)) fängt mit dem Klammern des Operators mit der höchsten Priorität (++) an, macht dann mit dem der zweithöchsten (<) weiter, und der mit der niedrigsten (&&) bleibt übrig.

    Hieße die korrekte Faustregel dann nicht eher:
    Alle Ausdrücke werden (zumindest im Kopf) geklammert, wobei mit dem Ausdruck, der den Operator mit der höchsten Priorität besitzt, begonnen und mit dem Ausdruck, der den Operator mit der niedrigsten Priorität besitzt, beendet wird. Anschließend werden die Ausdrücke in genau umgekehrter Reihenfolge ausgewertet.



  • Langsam glaube ich, Du willst uns verarschen ...
    Nochmal:
    Die Kurzschlussregel zum && - Operator kennst Du, das lässt sich Deinem Eingangsposting entnehmen. Damit die sinnvoll befolgt werden kann, wird zunächst die linke Seite des Operators ausgewertet. Als Folge der short circuit evaluation ist nun völlig egal, was rechts vom Operator steht, und aus diesem Grund wird das auch nicht mehr ausgewertet.



  • Belli schrieb:

    Langsam glaube ich, Du willst uns verarschen ...

    Ich sehe überhaupt keinen Grund unfreundlich zu werden.

    Nochmal:
    Die Kurzschlussregel zum && - Operator kennst Du, das lässt sich Deinem Eingangsposting entnehmen. Damit die sinnvoll befolgt werden kann, wird zunächst die linke Seite des Operators ausgewertet. Als Folge der short circuit evaluation ist nun völlig egal, was rechts vom Operator steht, und aus diesem Grund wird das auch nicht mehr ausgewertet.

    Richtig. Das sagte ich ja auch selbst bereits. Ich sehe nicht, wo meine aufgestellte Faustregel dem widerspricht. Sie impliziert nicht den Abbruch, aber sie ist ja auch nicht auf das konkrete Beispiel bezogen, sondern soll allgemeingültig formuliert sein - eine Faustregel eben. Um auch Abbruchsfälle wie den im vorliegenden Beispiel explizit zu erwähnen, kannst du sie Faustregel gerne noch mit dem Zusatz ", bis entweder alle Ausdrücke ausgewertet wurden oder ein Abbruch erfolgt ist." versehen.



  • Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun. Nichts. Nochmal: Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun.
    Die Auswertungsreihenfolge ist nicht definiert, der Compiler kann die Ausdrücke so auswerten, wie er möchte. Außer bei &&, || und , (Komma), da wird zuerst der linke, dann der rechte Ausdruck ausgewertet. Die Klammerung hat damit nichts zu tun.

    Laputzki schrieb:

    Anschließend werden die Ausdrücke in genau umgekehrter Reihenfolge ausgewertet.

    Demzufolge ist dieser Teil deiner Faustregel falsch.



  • Laputzki schrieb:

    Belli schrieb:

    Langsam glaube ich, Du willst uns verarschen ...

    Ich sehe überhaupt keinen Grund unfreundlich zu werden.

    Ich werde nicht unfreundlich, ich hab nur meine Meinung gesagt. Und im Weiteren habe noch einmal versucht, eine Erklärung zu geben.



  • Nathan schrieb:

    Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun. Nichts. Nochmal: Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun.

    Klammerung hat nichts mit Auswertungsreihenfolge zu tun? Häh? Es ist doch gerade der Sinn von Klammern die Auswertungsreihenfolge zu bestimmen. Dafür gibt es die. Oder folgt C hier nicht den Regeln der Mathematik?



  • Laputzki schrieb:

    Nathan schrieb:

    Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun. Nichts. Nochmal: Die Klammerung hat nichts mit der Auswertungsreihenfolge zu tun.

    Klammerung hat nichts mit Auswertungsreihenfolge zu tun? Häh? Es ist doch gerade der Sinn von Klammern die Auswertungsreihenfolge zu bestimmen. Dafür gibt es die. Oder folgt C hier nicht den Regeln der Mathematik?

    Klammern sagen auch in der Mathematik nicht, in welcher Reihenfolge du die Teilausdrücke zu berechnen hast.

    (35 + 50) × (32 - 11)

    Es ist dem Mathematiker völlig wurscht, ob du zuerst 35+50 oder 32-11 rechnest. Hauptsache beide Teilausdrücke werden vor der Multiplikation berechnet.

    Mfg Martin



  • Von geklammerten Teilausdrücken sollte man doch eigentlich davon ausgehen können, daß sie immer von innen nach aussen ausgewertet werden. Ist meiner Erfahrung nach auch so. Die Operatorpräzedenz muß natürlich beachtet werden.
    Eine Klammer als Klammer ist erstmal gar kein Operator - den zutreffenden Namen habe ich jetzt nicht dafür - es ist ein Strukturierungshilfsobjekt oder so.

    Ob der TE so langsam ins trolling gekippt ist traue ich mich nicht zu beurteilen. Eine Meinung dazu hätte ich aber schon. 😉



  • Hallo Martin,

    auch dir einen freundlichen Gruß.

    mgaeckler schrieb:

    Klammern sagen auch in der Mathematik nicht, in welcher Reihenfolge du die Teilausdrücke zu berechnen hast.

    (35 + 50) × (32 - 11)

    Es ist dem Mathematiker völlig wurscht, ob du zuerst 35+50 oder 32-11 rechnest. Hauptsache beide Teilausdrücke werden vor der Multiplikation berechnet.

    Sie werden aber nur vor der Multiplikation berechnet, weil sie in Klammern stehen. Ohne die Klammern würde als Erstes 50 × 32 berechnet. Also beeinflussen die Klammern sehr wohl die Auswertungsreihenfolge.

    Dass es in deinem Beispiel egal ist, ob zuerst der rechte oder der linke Teilausdruck berechnet wird, ist klar. Das ist aber auch nicht die Frage, die ich gestellt habe, denn ich weiß ja, dass in C beim &&-Operator zuerst die linke Seite ausgewertet wird.

    Meine Frage war ursprünglich eine ganz andere, nämlich warum in dem konkreten Beispiel 1 < 0 && 2 < a++ nicht der ++-Operator vor dem &&-Operator ausgeführt wird, obwohl er ja eine höhere Priorität besitzt.
    Nathan schrieb daraufhin, dass die Operatorenpriorität lediglich bestimmt, wo die Klammern sein würden, aber nicht die Auswertungsreihenfolge. DirkB ergänze dazu, dass die Klammerung in meinem Beispiel dann folgendermaßen aussehen würde: (1 < 0) && (2 < (a++)). Ok, dann betrachten wir diesen Fall mal:

    In der Mathematik ist es so, dass erst die in Klammern stehenden Ausdrücke ausgewertet werden. Demnach müsste bei DirkBs Klammerung a++ [in der Mathematik: a+1] vor (2 < (a++)) und letzteres vor der &&-Verknüpfung stattfinden. (1 < 0) müsste auch vor der &&-Verknüpfung stattfinden, stünde aber in keinem Prioritätenverhältnis zu dem was auf der rechten Seite der &&-Verknüpfung steht. Soweit die Mathematik.

    C wertet die &&-Verknüpfung aber offenbar vor dem a++ aus, obwohl a++ in Klammern steht, wogegen die &&-Verknüpfung nicht in Klammern steht. Damit würde C folglich entgegengesetzt der mathematischen Logik auswerten, und das ist das, was ich in meine (zu überprüfende!) Faustregel (+ ihrer späteren Ergänzung) geschrieben habe.

    Jetzt ist also aktuell die Frage: Ist Nathans und DirkBs Klammerung korrekt? Dann würde meine auf deren Annahmen beruhende Faustregel stimmen, was ich selbst bezweifle. Oder ist die Klammerung doch nicht die korrekte Antwort auf meine Ursprungsfrage, warum die Auswertung von a++ trotz höherer Operator-Priorität durch die &&-Verknüpfung verhindert wird, also warum nicht a++ als erstes ausgewertet wird? In letzterem Fall wäre ich für weitere Erklärungsversuche dankbar.

    Und um es in Anbetracht gegenteiliger Vermutungen von Belli und EOP ganz klar zu sagen: Nein, ich will hier niemanden verarschen, argumentiere hier nach bestem Wissen und Gewissen und bin für jeden eurer Versuche, mir zu helfen, dankbar, auch wenn sich die eine oder andere eurer Aussagen möglicherweise als nicht korrekt herusstellen sollte. Der gute Wille zählt, und den habe auch ich.



  • Laputzki schrieb:

    (1 < 0) && (2 < (a++)) [...]
    [...] Oder ist die Klammerung doch nicht die korrekte Antwort auf meine Ursprungsfrage, warum die Auswertung von a++ trotz höherer Operator-Priorität durch die &&-Verknüpfung verhindert wird, also warum nicht a++ als erstes ausgewertet wird?

    Short. Circuit. Evaluation.



  • Laputzki schrieb:

    Und um es in Anbetracht gegenteiliger Vermutungen von Belli und EOP ganz klar zu sagen: Nein, ich will hier niemanden verarschen, argumentiere hier nach bestem Wissen und Gewissen und bin für jeden eurer Versuche, mir zu helfen, dankbar, auch wenn sich die eine oder andere eurer Aussagen möglicherweise als nicht korrekt herusstellen sollte. Der gute Wille zählt, und den habe auch ich.

    Du machst hier den Eindruck, willst du mit allen Mitteln deine Faustregel zurechtbiegen. Das geht aber nicht, da sie einen falschen Ansatz hat. Auf uns wirkt das, als würdest du unsere Erklärungen ignorieren oder falsch deuten.

    Um nochmal auf das Beispiel vomgaeckler zurückzukommen:

    mgaeckler schrieb:

    (35 + 50) × (32 - 11)

    Es gibt 2 mögliche Berechnungsarten:

    int a = 35+50;
    int b = 32-11;
    int c = a*b;
    
    int a = 32-11;
    int b = 35+50;
    int c = a*b;
    

    Beide sind erlaubt. Deine Faustregel lässt aber nur eine Möglichkeit zu.

    Es gibt nur 4 Ausnahmen, bei denen die Reihenfolge eindeutig ist: ';', ',', '&&' und '||'.



  • Die Operatoren rangfolge sagt nur aus, wie die Operatoren zu Ihren Operanden in Bezug stehen.

    Also 5+2×3 heisst, das der Operator × zu den Operanden 2 und 3 gehört. Der Operator + hat die Operatoren 5 und 2×3. Welche der beiden zuerst in das Register geladen wird bleibt aber dem Compiler überlassen.

    move #5, d0
    move #3, d1
    move #2, d2
    mul d2, d1
    Add d1, d0

    Ist ok

    Move #2, d0
    Move #3, d1
    Mul, d1, d0
    Move #5, d1
    Add, d1, d0

    Ist auch ok

    Wie Du siehst, wurden die drei Operatoren in unterschiedlicher Reihenfolge ausgewertet.

    Diese 3 Operatoren könnten wieder Operationen mit eigenen Operatoren enthalten. Deren Reihenfolge ist dann auch entsprechend anders.

    Hoffe es ist jetzt klar.

    Mfg Martin


Anmelden zum Antworten