Auswertungsreihenfolge


  • Mod

    Nein, warum sollte es? Flush ist eine Nachricht an den Stream und hat nichts mit dem internen Werkeln des Compilers zu tun.



  • Na die Idee war, dass flush das entleeren des Buffers an dieser Stelle erzwingt und damit ug vor der inkrementierung ausgegeben wird.



  • zachery_foxx schrieb:

    Na die Idee war, dass flush das entleeren des Buffers an dieser Stelle erzwingt und damit ug vor der inkrementierung ausgegeben wird.

    flush ist ein Ausdruck, wie przif und ug und ug++. Es ist nicht garantiert, dass der Aufruf des op<< vor dem flush ausgewertet wird, bevor przif oder ug++ ausgewertet wird.



  • SeppJ schrieb:

    cout << setfill('0') << setw(8) << ug << '.' << przif(ug++) << '\n';
    

    Es ist nicht garantiert, in welcher Reihenfolge die Unterausdrücke (also insbesondere ug und przif(ug++) ) dieses vollständigen Ausdrucks ausgewertet werden.

    Das würde mich jetzt auch mal interessieren: Sind die diversen << nicht Operatoren, also das, was intern eigentlich nur eine Funktion ist? Und ist die Funktionsaufrufsreihenfolge nicht eindeutig definiert, da ja der Rückgabewert von einer Funktion die nächste aufruft? Müsste nicht garantiert sein, dass zuerst setfill, dann setw, dann ug, dann '.', dann ug++ und dann przif (mit dem alten ug) ausgewertet wird? Denn ansonsten wäre ja nichtmal garantiert, dass er bei

    const string target = "Welt";
    cout << "Hallo " << target;
    

    tatsächlich "Hallo Welt" und nicht "WeltHallo " ausgibt.



  • Die einzelnen Ausdrücke können in beliebiger Reihenfolge ausgewertet werden. Das heisst aber nicht, dass sie nicht zwischengespeichert werden können, um die Reihenfolge dann zu garantieren.


  • Mod

    Operator << ist nichts besonderes. Denk dir, da stünde + statt << und du willst Strings zusammensetzen:

    string stringfunktion1();
    string stringfunktion2();
    
    // ...
    
    string result = "Hallo " + stringfunktion1() + " 123\n " + stringfunktion2() + " ende";
    

    Da steht auch nicht fest, in welcher Reihenfolge stringfunktion1 und stringfunktion2 abgearbeitet werden, trotzdem würdest du nie auf die Idee kommen, dass hinterher die Reihenfolge nicht stimmen würde.
    Oder zum Beispiel flush: Das wäre als ob das " 123\n " eine Änderung der Auswertungsreihenfolge erzwingen könnte, bloß weil es eine besondere Bedeutung für die Ausgabe eines Strings auf den Bildschirm hat.

    Oder auch Grundrechenarten:

    int a();
    int b();
    int c();
    
    // ...
    
    int ergebnis = a() + b() * c();
    

    Bloß weil die Reihenfolge von a,b und c undefiniert ist, vergisst der Compiler trotzdem nicht, dass Punktrechnung vor Strichrechnung kommt. Und diese Regel erzwingt nicht, dass b und c zuerst ausgewertet werden!



  • Ich habs gerade bei mir getestet.

    #include <iostream>
    #include <cmath>
    #include <cstdlib>
    
    int main(){
        using namespace std;
        //Auswertungsreihenfolge
        cout<< (!!(cout<<"hello"))+(!!(cout<<"world"))<<'\n';//eingebauter op+ richtig rum
        cout<< (!!(cout<<"hello"))<<(!!(cout<<"world"))<<'\n';//selbergebauter op<< falsch rum
        cout<< atan2((!!(cout<<"hello")),(!!(cout<<"world")))<<'\n';//Funktionsaufruf falsch rum
        cout<< (!!(cout<<"hel"))<<!!(cout<<"lo ")<<(!!(cout<<"world"))<<'\n';//sogar ganz falschrum
        cout<< atan2(!!(cout<<"hel lo "),!!(cout<<"wor ld"))<<'\n';//Uups, Leiche, übriggeblieben beim Baslt0ln der Folgezeile
        cout<< atan2(atan2(!!(cout<<"hel"),!!(cout<<"lo ")),atan2(!!(cout<<"wor"),!!(cout<<"ld!")));//auch komplett falsch rum
    }
    
    helloworld2
    worldhello11
    worldhello0.785398
    worldlo hel111
    wor ldhel lo 0.785398
    ld!worlo hel0.785398
    

  • Mod

    Schöner Test. Der g++ bei mir macht's genau so wie bei dir. Der Intercompiler schmeißt 14 Warnungen, von wegen "operands are evaluated in unspecified order" (na sowas 😉 ) und macht daraus:

    helloworld2
    hello1world1
    helloworld0.785398
    hel1lo 1world1
    hel lo wor ld0.785398
    hello world!0.785398
    


  • Ich hatte auch g++. Dann fehlt und noch ein MSVC-Lauf.


  • Mod

    volkard schrieb:

    Ich hatte auch g++. Dann fehlt und noch ein MSVC-Lauf.

    Falls da kompliziertere Berechnungen laufen, könnte ich mir auch gut vorstellen, dass der Optimierungsgrad eine Rolle spielt.



  • volkard schrieb:

    Ich hatte auch g++. Dann fehlt und noch ein MSVC-Lauf.

    helloworld2
    worldhello11
    worldhello0.785398
    worldlo hel111
    wor ldhel lo 0.785398
    ld!worlo hel0.785398
    

    Wie oben, musste allerdings noch casten wegen ambiguous call.



  • Bei mir (2005er) ebenfalls. Debug und Release sogar gleich.



  • zachery_foxx schrieb:

    Ich bin mir nicht ganz sicher, aber müsste ein "flush" an der richtigen Stelle nicht abhilfe schaffen?

    cout << setfill('0') << setw(8) << ug << flush << '.' << przif(ug++) << '\n';
    

    Bei meinen Tests mit dem fraglichen Programm hats das tatsächlich getan - aber das kann auch Zufall/Glück sein ...



  • Belli schrieb:

    zachery_foxx schrieb:

    Ich bin mir nicht ganz sicher, aber müsste ein "flush" an der richtigen Stelle nicht abhilfe schaffen?

    cout << setfill('0') << setw(8) << ug << flush << '.' << przif(ug++) << '\n';
    

    Bei meinen Tests mit dem fraglichen Programm hats das tatsächlich getan - aber das kann auch Zufall/Glück sein ...

    Ja das ist dann Glück. Die richtige Lösung ist aber denkbar simpel: das Ganze in 2 Anweisungen aufgliedern:

    cout << setfill('0') << setw(8) << ug << '.';
    cout << przif(ug++) << '\n';
    

Anmelden zum Antworten