Warum erst Funktionsaufruf und dann Ausgabe ? cout<<" 32 * 5 = "<<multiply(32,5);



  • ist das nicht das gleiche problem wie in diesem thread: http://c-plusplus.net/forum/viewtopic-var-t-is-254913.html



  • Hallo,

    da ist keine "Textausgabe". Zumindest nicht für den Compiler, der sieht nur die Operatoren, und ein Funktionsaufruf hat in deiner "Anweisungs-Kette" höhere Priorität als der "Linksverschiebungsoperator" (den sieht der Compiler, und nichts anderes)

    MfG,

    Probe-Nutzer



  • ... aber demnach müsste ja folgende Ausgabe erscheinen:

    Ergebnis: 160 32 * 5 =


  • Mod

    La Grave schrieb:

    ... aber demnach müsste ja folgende Ausgabe erscheinen:

    Ergebnis: 160 32 * 5 =

    Nö.

    Erst der Funktionsaufruf, Ausgabe: "Ergebnis: "

    Dann werden die << Operatoren in main abgearbeitet. Nach dem Funktionsaufruf sieht der Ausdruck so aus: cout<<" 32 * 5 = "<< 160 <<endl; Dessen Ausgabe ist von links nach rechts (wie man es von cout auch korrekterweise erwartet): 32 * 5 = 160

    Zusammen:
    Ergebnis: 32 * 5 = 160



  • Vielleicht mal etwas weg von den Shift-Operatoren und der Ausgabe.
    Schau dir nochmal die Priotitätenliste vom Probe-Nutzer an und mach dir klar, was z.B. da rauskommt:

    int dec(int& x)
    {
      x--;
      return x;
    }
    int x = 5;
    int result = dec(x) + dec(x) * dec(x);
    


  • Jockelx schrieb:

    Vielleicht mal etwas weg von den Shift-Operatoren und der Ausgabe.
    Schau dir nochmal die Priotitätenliste vom Probe-Nutzer an und mach dir klar, was z.B. da rauskommt:

    int dec(int& x)
    {
      x--;
      return x;
    }
    int x = 5;
    int result = dec(x) + dec(x) * dec(x);
    

    Wieviele Ergebnisse sind erlaubt?









  • Mmmhh, glaube ich dir natürlich gerne, dass das nicht definiert ist.
    Sehe ich aber ehrlich gesagt nicht, warum das so ist. 😞

    Ich kann das Ergebnis klar definieren. 🙂



  • Ach nee, ich darf nicht davon ausgehen, dass von 'links nach rechts' abgearbeitet wird, oder?



  • Mit der Operator-Priorität hat es nichts zu tun. Die entscheidet nur dazwischen, ob es als cout << (multiply(...)) oder (cout << multiply)(...) interpretiert wird.

    Der << Operator ist ja hier überladen, d.h. das ist eigentlich ein verschachtelter Funktionsaufruf, etwa so:

    operator<<(operator<<(operator<<(cout, " 32 * 5 = "), multiply(32,5)), endl);
    

    Darin gibt es diesen Teilausdruck:

    operator<<(operator<<(cout, " 32 * 5 = "), multiply(32,5))
    

    d.h. ein Funktionsaufruf mit 2 Argumenten. In welcher Reihenfolge Argumente ausgewertet werden ist nicht spezifiziert. Der Compiler wird das entweder in einer willkürlich vorgegebenen Reihenfolge tun oder die Auswertung so ordnen, dass möglichst optimaler Code herauskommt. Bei dir wird zuerst das zweite Argument ausgewertet, innerhalb der multiply-Funktion eine Ausgabe gemacht, dann wird erst operator<<(cout, " 32 * 5 = ") ausgefürt.



  • also ohne Adressoperator würde ja logischerweise 20 herauskommen.

    Aber mit ist das Ergebnis 10 - nur da kann ich noch nicht folgen....



  • ...In welcher Reihenfolge Argumente ausgewertet werden ist nicht spezifiziert.

    ... also gibt es keine Regel, die angewendet wird ?



  • LaGrave schrieb:

    ...In welcher Reihenfolge Argumente ausgewertet werden ist nicht spezifiziert.

    ... also gibt es keine Regel, die angewendet wird ?

    Doch. Mach deinen Code unabhängig von der Reihenfolge.



  • Bei dir wird zuerst das zweite Argument ausgewertet, innerhalb der multiply-Funktion eine Ausgabe gemacht, dann wird erst operator<<(cout, " 32 * 5 = ") ausgefürt.

    er geht aber in die Funktion rein, gibt dann den Text des ersten Argumentes aus und dann erst schließt er die Funktion mit dem Rückgabewert.
    Gibt es eine Erklärung mit Hilfe des Stacks ?



  • Nein. Der Stack ist so aufgebaut, wie es der Compiler entscheidet. da der Compiler aber alle Argumente in beliebiger Reihenfolge auswerten kann, wird auchd er Stack "beliebig" sein.

    Vielleicht noch um das klar zu machen:

    der Compiler hat prinzipiell folgende Möglichkeiten der Auswertung:

    //operator<<(operator<<(operator<<(cout, " 32 * 5 = "), multiply(32,5)),endl);
    
    //1. Möglichkeit
    operator<<(cout, " 32 * 5 = ");
    temp=multiply(32,5);
    operator<<(cout,temp);
    operator<<(cout,endl);
    
    //2. Möglichkeit
    temp=multiply(32,5);
    operator<<(cout, " 32 * 5 = ");
    operator<<(cout,temp);
    operator<<(cout,endl);
    

    Er wertet also immer zuerst beide Argumente aus. Die Reihenfolge ist ihm prinzipiell egal (dir nicht, aber ihm ist das erlaubt).

    Im zweiten Fall, in dem zuerst multiply aufgerufen wird, wird zuerst das Ergebnis von multiply berechnet und "Ergebnis:" in cout rein geschoben. Danach wertet der compiler dann den ersten "operator<<" aufruf aus und schreibt dann "32*5=" hinter dein "Ergebnis:". Danach wird der letzte "operator<<" aufgerufen und das Ergebnis deiner Funktion ausgegeben.

    Wie das im ersten Fall läuft, kannst du dir ja selbst anschaulich machen.



  • erstmal danke für die ausführlichen Antworten.
    Und bevor wir jetzt den Compiler auseinander nehmen sag ich einfach
    mal "Der Compiler ist ja auch nur ein Mensch"
    Kann mich zwar nur schwer mit der Lust und Laune eines Compilers
    anfreunden. Aber nehme in Zukunft zur Berechnung einfach zwei Zeilen und lass das Problem gar nicht erst aufkommen. Aber trotzdem interessant mal nachzuforschen...



  • ... aber wenn der Compiler verschiedene Möglichkeiten hat, warum nimmt er immer diese eine Möglichkeit ? Oder könnte das Programm auf verschiedenen Compilern anders aussehen?


  • Mod

    La Grave schrieb:

    ... aber wenn der Compiler verschiedene Möglichkeiten hat, warum nimmt er immer diese eine Möglichkeit ?

    Weil dein spezieller Compiler natürlich irgendwelche Regeln eingebaut hat, nach denen er dies entscheidet. Und die können durchaus besagen, dass er immer von rechts nach links gehen soll. Wie diese Regeln konkret lauten bleibt aber dem Programmierer des Compilers überlassen.

    Oder könnte das Programm auf verschiedenen Compilern anders aussehen?

    Ja. Sogar mit dem gleichen Compiler, wenn du Compilereinstellungen änderst.



  • Der Compiler nimmt die 2. Möglichkeit (also erst die Funktionen berechnen und danach dann die cout-Ausgaben), da im Assemblercode das "cout"-Objekt im ersten Register bleiben kann und nicht jedesmal neu geladen werden muß - spart also einen mov-Befehl je Operation).


Anmelden zum Antworten