Compiler liegt mal wieder falsch!



  • int x = 1;
    x += 3 + x++;
    

    Der Compiler meint es komme 4 raus. Die Auswertung von x++ wird von der Zuweisung überschrieben? Also für kurze Zeit steht da 2 und dann wird die 4 rein geschoben?



  • 5 kommt dabei raus, was auch richtig ist.

    int x = 1;
    x += 3 + ++x;
    

    Hier ist das Ergebnis 6



  • Vielleicht mal als Bug melden. Aber mal davon abgesehen: Solchen Code schreibt man auch nicht. :p Trotzdem mal als Bug senden. Hab auch schon einen Bug im Scala-Compiler gefunden und hab mich drüber gewundert, warum dem niemanden aufgefallen ist bzw. niemand reported hat. War ein ziemlich simpler Bug der dann nach meinem Report gefixt wurde.



  • Was sagt denn die Spec dazu?



  • Bashar schrieb:

    Was sagt denn die Spec dazu?

    Die Spec sagt:
    Wenn sie halbwegs bei Verstand sind, schreiben sie nicht so einen Code.



  • abcd schrieb:

    int x = 1;
    x += 3 + x++;
    

    Der Compiler meint es komme 4 raus.

    Welcher Compiler?



  • EOP schrieb:

    Bashar schrieb:

    Was sagt denn die Spec dazu?

    Die Spec sagt:
    Wenn sie halbwegs bei Verstand sind, schreiben sie nicht so einen Code.

    Das ist nicht witzig. Leute die Spezifikationen mit Anleitungen für Idioten verwechseln sind für Verbrechen wie Basic und Matlab verantwortlich.



  • Bashar schrieb:

    EOP schrieb:

    Bashar schrieb:

    Was sagt denn die Spec dazu?

    Die Spec sagt:
    Wenn sie halbwegs bei Verstand sind, schreiben sie nicht so einen Code.

    Das ist nicht witzig. Leute die Spezifikationen mit Anleitungen für Idioten verwechseln sind für Verbrechen wie Basic und Matlab verantwortlich.

    Ich fand's einigermaßen witzig und richtig.
    Wenn du nicht, musst du dich eben in den Schlaf weinen.

    Ich kann natürlich auch code wie

    int x = 1;
    x += 3 + x++ | x << 4 + --x;
    

    schreiben und mich dann wundern wieso das Ergebnis nicht meinen Erwartungen entspricht.



  • Bashar schrieb:

    Was sagt denn die Spec dazu?

    Edit: Siehe die eingearbeiteten Korrekturen.

    • " x++ ": steht in §7.7.5, ist aber eh intuitiv klar: x wird erst ausgewertet, dann inkrementiert, dann zurückgeschrieben, das Ergebnis ist in der Postfixnotation der alte Wert.
    • " 3 + x++ ": hier ist die Auswertungsreihenfolge egal.
    • " x += 3 + x++ ":

    7.17.2 Compound assignment
    [...]
    An operation of the form x op= y is processed by applying binary operator overload resolution (§7.3.4) as if the operation was written x op y. Then,
    • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

    Also wird das ausgewertet als " x = x + 3 + x++ ".
    Edit: Falsch, es wird ausgewert als " x = x + (3 + x++) ", was bei genauerem Nachdenken auch klar ist, siehe den Post von hustbaer weiter unten.

    • " x = x + 3 + x++ ":

    7.3.1 Operator precedence and associativity
    When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than the binary + operator.[...]
    The following table summarizes all operators in order of precedence from highest to lowest:

    Section	Category        Operators
    [...]
    7.7     Unary	        +  -  !  ~  ++x  --x  (T)x
    7.8     Multiplicative	*  /  %
    7.8     Additive	+  -
    

    When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed:
    • Except for the assignment operators and the null coalescing operator, all binary operators are left-associative, meaning that operations are performed from left to right. For example, x + y + z is evaluated as (x + y) + z.

    Also wird das ausgewertet als " x = (x + 3) + (x++) ".

    • Bleibt noch die Frage, ob der linke oder der rechte Summand zuerst ausgewertet wird.

    5.3.3.21 General rules for expressions with embedded expressions
    The following rules apply to these kinds of expressions: [...], binary +, -, *, /, %, <<, >>, <, <=, >, >=, ==, !=, is, as, &, |, ^ expressions (§7.8, §7.9, §7.10, §7.11), compound assignment expressions (§7.17.2), [...].
    Each of these expressions has one or more sub-expressions that are unconditionally evaluated in a fixed order. For example, the binary % operator evaluates the left hand side of the operator, then the right hand side.

    Und wie ist es wohl für " + "?

    7.8.4 Addition operator
    For an operation of the form x + y, binary operator overload resolution (§7.3.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.
    The predefined addition operators are listed below. For numeric and enumeration types, the predefined addition operators compute the sum of the two operands. When one or both operands are of type string, the predefined addition operators concatenate the string representation of the operands.
    • Integer addition:
    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);

    Und in welcher Reihenfolge werden Argumentlisten ausgewertet?

    7.5.1.2 Run-time evaluation of argument lists
    During the run-time processing of a function member invocation (§7.5.4), the expressions or variable references of an argument list are evaluated in order, from left to right [...]

    Da haben wirs also:

    x           = 1
    x + 3       = 4
            x++ = 1 (und x ist jetzt 2)
    x + 3 + x++ = 5
    

    und dann die Zuweisung. Ergebnis ist also 5.

    **Edit: Aufgrund obiger Korrektur wird das Nachschlagen der Assoziativität von + hinfällig, und die tatsächliche Evaluationsreihenfolge ist:

    x             = 1
             x++  = 1 (und x ist jetzt 2)
         3 + x++  = 4
    x + (3 + x++) = 5
    

    **

    Schön an C# ist, daß es kaum UB gibt. Abweichungen zwischen den verschiedenen Compilern bleiben dadurch gering.



  • Was spricht dagegen

    int x = 1;
    x += 3;
    x += x++;
    

    zu schreiben? Da kann ich mir wenigstens sicher sein was passiert ohne mir Gedanken über Auswertungsreihenfolge und sowas machen zu müssen.
    Nur alles zusammenzupacken um ne richtig leet-Zeile rauszuhauen ist lächerlich. Macht ja manchmal Spaß, ist aber eher kontraproduktiv.
    oder

    x++ = x << 1; // Haut das hin?
    
    x++ <<= 1;
    

    Dann enden wir bei

    3 + (x++ <<= 1); // Ist aber spät und ich nicht mehr so 100% nüchtern
    


  • EOP schrieb:

    [...]

    Was spricht dagegen, sich mit dem Thema zu beschäftigen anstatt mit irgendwelchen Stilfragen?

    Der Threadersteller meinte, einen Compilerbug gefunden zu haben. Für den Compiler aus VS 2012 kann ich das nicht bestätigen, der verhält sich so, wie es die Spezifikation verlangt.



  • audacia schrieb:

    Der Threadersteller meinte, einen Compilerbug gefunden zu haben. Für den Compiler aus VS 2012 kann ich das nicht bestätigen, der verhält sich so, wie es die Spezifikation verlangt.

    Hab hier schön öfters Leute gesehen, die meinten schlauer als der Compiler zu sein. Lagen alle falsch.



  • EOP schrieb:

    audacia schrieb:

    Der Threadersteller meinte, einen Compilerbug gefunden zu haben. Für den Compiler aus VS 2012 kann ich das nicht bestätigen, der verhält sich so, wie es die Spezifikation verlangt.

    Hab hier schön öfters Leute gesehen, die meinten schlauer als der Compiler zu sein. Lagen alle falsch.

    Die Frage danach welchen Compiler er verwendet hat hat mir der OP ja nicht beantwortet.

    Davon abgesehen: bei C++ kommt das schon öfter mal vor dass der Programmierer schlauer als der Compiler ist. Speziell bei Compilern wie MSVC die noch nicht so ganz "up to date" sind, und die in vielen Projekten auch noch in sehr alten Versionen verwendet werden.
    Bei C# ist es aber denke ich wirklich recht selten.



  • Ich benutze Visual Studio Express.
    Freitag Abend war ich wohl auch nicht mehr so konzentriert. Der Compiler wirft bei mir jetzt auch 5 und nicht 4 raus. Ich hätte aber 6 erwartet.
    Wenn ich Audacia Ausführungen interpretiere, dann meint auch er das das Ergebniss der x++ Operation durch die Zuweisung überchrieben wird.



  • Visual Studio Express 2013 V12.0.31101.00 Update 4



  • audacia schrieb:

    [*]" x += 3 + x++ ":

    7.17.2 Compound assignment
    [...]
    An operation of the form x op= y is processed by applying binary operator overload resolution (§7.3.4) as if the operation was written x op y. Then,
    • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

    Also wird das ausgewertet als " x = x + 3 + x++ ".

    Nö, "x = x + (3 + x++)".
    Wobei das hier wohl keinen Unterschied macht.
    Wichtig ist ja nur dass er sich den Wert von "x" schnappt bevor er anfängt "3 + x++" auszuwerten.



  • hustbaer schrieb:

    audacia schrieb:

    7.17.2 Compound assignment
    [...]
    An operation of the form x op= y is processed by applying binary operator overload resolution (§7.3.4) as if the operation was written x op y. Then,
    • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.

    Also wird das ausgewertet als " x = x + 3 + x++ ".

    Nö, "x = x + (3 + x++)".

    Tatsächlich, du hast recht. Das ist subtil, weil durch die Zusammenfassung der RHS als y in x = x op y implizit gesagt wird, daß y zuerst ausgewertet werden muß (also x = x op (y) ) und nicht nachträglich noch Operatorpräzedenz und Linksassoziativität angewandt wird. Es ist klar, daß es sich so verhalten muß, denn wenn y einen Operator beinhaltet, der geringere Priorität hat als + , also etwa y = 2 << 3 , dann muß x += 2 << 3 zu x = x + (2 << 3) werden und nicht zu x = x + 2 << 3 , was x = (x + 2) << 3 entspräche.

    hustbaer schrieb:

    Wichtig ist ja nur dass er sich den Wert von "x" schnappt bevor er anfängt "3 + x++" auszuwerten.

    Ja, zum Verstehen der 5 reicht es, das zu wissen.



  • audacia schrieb:

    Es ist klar, daß es sich so verhalten muß, (...)

    Ja, klar. Deswegen sind bei deinem "x = (x + 3) + (x++)" auch gleich Alarmglocken bei mir im Kopf losgegangen. So subtil finde ich das nämlich nicht 😉

    Mit dem "wichtig ist ja nur (...)" meinte ich natürlich nur das hier diskutierte Beispiel, bei dem es eben keinen Unterschied macht. Dass es allgemein einen Unterschied macht (bzw. machen kann), und daher alles andere als egal ist, ist klar.



  • hustbaer schrieb:

    So subtil finde ich das nämlich nicht 😉

    Nachts um eins vielleicht schon 🙂



  • Da bin ich erst mal richtig wach geworden.


Log in to reply