Zuweisungen



  • out schrieb:

    camper schrieb:

    out schrieb:

    Merk dir einfach die Faustregel: Innerhalb eine Anweisung darf eine Variable max. 1x modifiziert werden. Hälst du dich daran, bist du stets auf der sicheren Seite.

    Das vermeidet eine Stolperfalle, hinreichend für wohldefiniertes Verhalten ist das noch lange nicht.

    Hast du ein Beispiel parat, in dem eine Variable innerhalb einer Anweisung nur 1x modifiziert wird und es trotzdem zu UB kommt?

    int y = x++ + x;
    

    Sone schrieb:

    i = j++;
    

    Wo genau versteckt sich das UB?



  • dot schrieb:

    out schrieb:

    camper schrieb:

    out schrieb:

    Merk dir einfach die Faustregel: Innerhalb eine Anweisung darf eine Variable max. 1x modifiziert werden. Hälst du dich daran, bist du stets auf der sicheren Seite.

    Das vermeidet eine Stolperfalle, hinreichend für wohldefiniertes Verhalten ist das noch lange nicht.

    Hast du ein Beispiel parat, in dem eine Variable innerhalb einer Anweisung nur 1x modifiziert wird und es trotzdem zu UB kommt?

    int y = x++ + x;
    

    Stimmt. Dann muss man die Faustregel etwas strengen auslegen: Zwischen zwei sequence-points darf eine modifiziere Variable höchstens 1x auftauchen. Dann ist man wirklich safe, oder?



  • Ich glaube, + ist kein Sequence Point, auch wenn der Begriff veraltet ist.

    Und schlagt mich, aber: camper, die Tabelle sieht echt super aus, aber was genau drückt eine Zelle aus, wie werden Zeile und Spalte genau miteinander vermengt? Sorry, wenn das da irgendwie draus hervorgeht. Wenn Dir die Frage zu doof ist, vergiss sie.



  • Eisflamme schrieb:

    Ich glaube, + ist kein Sequence Point, auch wenn der Begriff veraltet ist.

    Eben, ist es auch nicht.
    Ist der Begriff für C++98/03 auch veraltet? Dachte das gilt nur für de neuen Standard.


  • Mod

    Allgemein:
    Im Allgemeinen (sofern nichts anderes gesagt wird) ist die Auswertung von Ausdrücken ungeordnet (unsequenced) im Verhältnis zur Auswertung irgendwelcher anderer Ausdrücke (1.9/15 Satz 1)
    Sofern zwei Ausdrücke das gleiche Objekt modifizieren oder einer das Objekt modifiziert und der andere vom gespeicherten Wert des Objektes abhängt, ist das Verhalten undefiniert, wenn die Auswertung der beiden Ausdrücke nicht geordnet (sequenced) ist (1.9/15 Satz 3)

    Zum Glück gibt es nicht sehr viele (allgemeine) Regeln, die die Reihenfolge der Auswertung festlegen:

    1. die Operanden eines Operators werden ausgewertet, bevor das Ergebnis des Operators berechnet wird (1.9/15 Satz 2), also in

    (1+2)+(3+4)
    

    werden erst 1+2 und 3+4 ausgewertet, bevor die Gesamtsumme gebildet wird. Das ist im Prinzip intuitiv klar.
    Ausnahmen sind bei bestimmten Operatoren (&& || ?: ,) zu beachten.

    2. Ausdrücke, die zu verschiedenen Anweisungen gehören, werden in der Reihenfolge ausgewertet, in der die Anweisungen stehen (a.k.a ";" ist ein Sequenzpunkt) 1.9/14.

    3. Argumente für Funktionsaufrufe werden ausgewertet, bevor die Funktion aufgerufen wird (die Auswertung der Argumente untereinander ist aber - bis auf eine Ausnahme - unbestimmt) 1.9/15 Satz 4.

    4. (Teil-)Ausdrücke (einschließlich anderer Funktionsaufrufe), die zum gleichen vollständigen Ausdruck wie ein Funktionsaufruf gehören, werden in unbestimmter Weise entweder vor oder nach dem Funktionsaufruf ausgeführt (indeterminately sequenced) 1.9/15 Satz 5. (das bedeutet auch, das Funktionsaufrufe nicht überlappen).

    (Eine Regel mit kleinerer Nummer hat jeweils Vorrang). Das sind ungefähr die Regeln, die man sich als Anfänger aneignen sollte, um den häufigsten Stolperfallen aus dem Weg zu gehen.

    Ein paar Besonderheiten sind bei , && || ?: zu beachten - das ist bekannt.
    Die Zuweisung macht ein paar zusätzliche Garantien:
    - der Wert der Zuweisung wird erst bestimmt, nachdem das Objekt modifiziert wurde. Damit werden Mehrfachzuweisungen der Form

    a = b = 0;
    

    möglich.
    - Es ist garantiert, dass der Wert der Zuweisung mit dem in der Zuweisung gespeicherten Wert übereinstimmt im Verhältnis zu einem Funktionsaufruf im gleichen vollständigen Ausdruck:

    int foo(int& i) { return ++i; }
    int i = 0;
    int j = (i=0) + foo(i);
    

    j ist garantiert 1; nicht etwa evtl. 2.

    Bei List-Initialisierung werden die Initialisierer der einzelnen Elemente nacheinander ausgewertet. Das ist sogar dann der Fall (siehe Ausnahme zu 3.), wenn die List-Initialisierung zu einem Funktionsaufruf führt:

    struct foo { foo(int, int) };
    int x = 0;
    foo{ x++, x++ };  // ruft den Konstruktor mit den Argumenten 0, 1 auf
    foo( x++, x++ );  // ist undefiniert
    

    Wahrscheinlich habe noch ein oder 2 Spezialsfälle vergessen - die sind dann aber wahrscheinlich auch praktisch ohne große Bedeutung.


  • Mod

    out schrieb:

    Stimmt. Dann muss man die Faustregel etwas strengen auslegen: Zwischen zwei sequence-points darf eine modifiziere Variable höchstens 1x auftauchen. Dann ist man wirklich safe, oder?

    Wenn ein Ausdruck vom Wert einer Variablen abhängt und diese Variable im gleichen Ausdruck auch modifiziert wird, muss die Leseoperation entweder dazu dienen, den zu speicherenden neuen Wert zu bestimmen, oder aber direkt vom modifizierenden Ausruck abhängen (=benutze die Zuweisung direkt als Operand). (Das ist ziemlich genau die Regel, wie sie in C++03 formuliert wurde).

    int x = 1;
    x = x + x + x + x; // ist ok
    


  • camper schrieb:

    out schrieb:

    Stimmt. Dann muss man die Faustregel etwas strengen auslegen: Zwischen zwei sequence-points darf eine modifiziere Variable höchstens 1x auftauchen. Dann ist man wirklich safe, oder?

    Wenn ein Ausdruck vom Wert einer Variablen abhängt und diese Variable im gleichen Ausdruck auch modifiziert wird, muss die Leseoperation entweder dazu dienen, den zu speicherenden neuen Wert zu bestimmen, oder aber direkt vom modifizierenden Ausruck abhängen (=benutze die Zuweisung direkt als Operand). (Das ist ziemlich genau die Regel, wie sie in C++03 formuliert wurde).

    int x = 1;
    x = x + x + x + x; // ist ok
    

    Hätte nicht gedacht, dass es so schwierig ist eine gute Faustregel aufzustellen :D, darum lass ich es nun lieber. 😃



  • dot schrieb:

    Wo genau versteckt sich das UB?

    Nirgendswo, da hab ich mich vertan. 🙂

    Ich glaube, ich werde erstmal ein Stündchen brauchen bis ich Campers Post verstanden habe.


Anmelden zum Antworten