Anfängerfrage: Vorrangigkeit von Operatoren



  • Den Link habe ich mir angesehen, was aber genau daran undefiniert ist, ist mir noch nicht klar. Kann da bitte jemand ein paar Zeilen zu schreiben?



  • ich denke freund camper hat da schon alles sinnvolle dazu gesagt. das verhalten ist undefiniert. dein compiler darf frei schnauze entscheiden, was er macht. das verhalten, das deiner zeigt ist höchst interessant, aber eben in ordnung.

    nun gut hier der erklärungsversuch: x=12 liefert eine referenz auf x (mit dem wert 12). das wertet der compiler zuerst aus. diese wird in x++ erhöht aber vorher als 12 ausgeben. danach ist die referenz um 1 erhöht und wird als 13 ausgegeben.



  • Ich habe mich vielleicht etwas undeutlich ausgedrückt. Was macht aus dem Ausdruck einen undefinierten: Ist es die Abfolge oder die Kombination der Operationen? Oder kommt der Compiler nicht damit klar, dass er "nicht weiß" ob nun erst das (x=blah) oder das Inkrementieren zu beachten ist? Denn das sollte doch durch die Reihenfolge "von links" festgelegt werden.
    Die einzelnen Operationen, Zuweisen, postfix-Inkrement und das Verschieben sind ja alle definiert, also muss doch "irgendwas" daraus ein undefiniert machen und genau das "was" ist mir noch nicht klar.

    Ja und sind eigentlich beide Ausdrücke undefiniert oder nur einer (welcher)?


  • Mod

    Anfänger fragt schrieb:

    Ja und sind eigentlich beide Ausdrücke undefiniert oder nur einer (welcher)?

    Beide, aus dem gleichen Grund. Ein Objekt (x) wird darin zweimal modifiziert, ohne dass sich dazwischen ein Sequenzpunkt befindet.
    Ein zweites Objekt (cout) wird ebenfalls mehrfach modifiziert, allerdings nur innerhalb der Operatorfunktionen; da sich am Anfang und Ende von Funktionen Sequenzpunkte befinden, ist dies allerdings kein Grund für undefiniertes Verhalten (andernfalls könnte man diese Aufrufe generell nicht verketten).



  • es liegt wohl eher daran, dass ich nicht genau lesen kann. 😉
    falls es camper zu kompliziert ausgedrückt: dem compiler steht tatsächlich frei, ob er erst x++ oder (x=12) auswertet. beides ist möglich und in diesem fall (nicht generell, sondern eben nur, weil << zu beiden ein untergeordneter operator ist) gleichrangig. das ist ein allgemeines problem bei funktionsaufrufen. es ist nicht definiert, in welcher reihenfolge ausdrücke, die als parameter genutzt werden, ausgewertet werden (ein operatoraufruf ist auch nur ein funktionsaufruf). daher sollte man es vermeiden modifizierende ausdrücke als parameter zu verwenden.



  • Dann muss ich jetzt noch etwas nachschieben, denn laut meinem C++ Buch ist das hier lösbar:

    int i(1);
    cout << i << “,“ << ++i <<endl;
    

    Ist das nun nur deshalb gültig, bzw. nicht undefiniert, da das erste i nicht geändert wird? Laut meinem Buch müsste da "2,2" ausgegeben werden. Mein Compiler spuckt das auch aus.

    Und um sicherzugehen: Sobald ich bei dem ersten i eine Änderung vornehme -egal welche- wird der Ausdruck dann undefiniert?

    Was die Sequenzpunkte angeht, das sind das doch:
    [] Strichpunkt am Zeilenende
    [
    ] Aufruf und Ende einer Funktion
    [] Deklaration einer Variable
    [
    ] das Komma nur bedingt (falls z.B. nicht als Aufzählungszeichen bei den Parametern einer Funktion)
    [*] logische Parameter && und ||

    Stimmt das, bzw. habe ich etwas vergessen?

    Danke nochmals!



  • das stimmt so mit ausnahme der logischen operatoren. (x++)||(++x) ist auch ein undefiniertes verhalten.

    bei der auswertung geht es um ausdrücke. "x" ist vollständig ausgewertet.



  • Vielen Dank an euch, dann ist mir das jetzt klar!


  • Mod

    Anfänger fragt schrieb:

    Dann muss ich jetzt noch etwas nachschieben, denn laut meinem C++ Buch ist das hier lösbar:

    int i(1);
    cout << i << “,“ << ++i <<endl;
    

    Ist das nun nur deshalb gültig, bzw. nicht undefiniert, da das erste i nicht geändert wird? Laut meinem Buch müsste da "2,2" ausgegeben werden. Mein Compiler spuckt das auch aus.

    Auch das ist undefiniert. Der Wert eines Objektes, dass zwischen zwei Sequenzpunkten verändert wird, darf zwischen diesen Punkten nur gelesen werden, um den neuen Wert zu bestimmen. Bestimmung des Wertes eines Ausdrucks (vereinfacht: die Berechnung) und Ausführung von Seiteneffekten (=verändern des Zustands von Objekten, Schreibvorgänge) erfolgen grundsätzlich unabhängig und in keiner bestimmten Reihenfolge. Es sei denn, es bestehen bestimmte zusätzliche semantische Einschränken wie oben (bei ++i): einen neuen Wert kann man in ein Objekt eben erst schreiben, wenn man diesen neuen Wert kennt.
    Der Teilausdruck i ist völlig unabhängig von ++i; da sich zwischen beiden kein Sequenzpunkt befindet, ++i den Wert des Objektes verändert und der Ausdruck i (durch implizite Konvertierung in ein rvalue) zu einem Lesevorgang führt, ist das undefiniert.

    Was die Sequenzpunkte angeht, das sind das doch:
    [] Strichpunkt am Zeilenende
    [
    ] Aufruf und Ende einer Funktion
    [] Deklaration einer Variable
    [
    ] das Komma nur bedingt (falls z.B. nicht als Aufzählungszeichen bei den Parametern einer Funktion)
    [*] logische Parameter && und ||

    im Grunde schon, wenn ich auch nichts vergessen habe.

    P.S. offenbar ist diese Regel in C++ etwas einzuschränken:
    in C++ ist das Ergebnis einer Zuweisung ein lvalue (im Gegensatz zu C), andererseits sollen einfache Mehrfachzuweisungen natürlich weiterhin möglich sein:

    int foo,bar;
    foo=bar=0;
    

    Hier soll die zweite Zuweisung offenbar zu keinem Lesevorgang führen, da der zu schreibende Wert ja im Prinzip bekannt ist.

    foo=*&(bar=0);
    

    dürfte dagegen undefiniert sein.
    Bei volatile verändern sich die Regeln nach einmal, aber darauf muss ich hier nicht eingehen.



  • Danke!


Anmelden zum Antworten