Was ist das Ergebnis von + = in C und C ++?

92

Ich habe folgenden Code:

#include <stdio.h>
int main(int argc, char **argv) {
    int i = 0;
    (i+=10)+=10;
    printf("i = %d\n", i);
    return 0;
}

Wenn ich versuche, es mit gcc als C-Quelle zu kompilieren, wird folgende Fehlermeldung angezeigt:

error: lvalue required as left operand of assignment

Aber wenn ich es als C ++ - Quelle mit g ++ kompiliere, erhalte ich keine Fehlermeldung und wenn ich die ausführbare Datei ausführe:

i = 20

Warum das unterschiedliche Verhalten?

Svetlin Mladenov
quelle
85
Unterschiedliche Sprache, unterschiedliche Syntaxregeln?. Persönlich würde ich diesen Code bei der Codeüberprüfung ablehnen.
Max
7
Vermeiden Sie Code wie diesen imo ... Unklar für alle.
Allaire
1
Zweifellos ist der Code nicht sauber und sollte in der "echten" Entwicklung vermieden werden. Trotzdem beobachte ich das gleiche Verhalten und würde gerne Gründe dafür wissen.
Ulidtko
9
Dies ist KEIN Code-Auszug aus einer echten Software. Dies ist nur ein Knick, auf den ich versehentlich gestoßen bin.
Svetlin Mladenov
3
@JohnDibling Ich denke, das Upvote ist speziell das (i + = 10) + = 10 Ich weiß nicht, in welcher Sprache legitimer Code ist und die Tatsache, dass er sagt, dass C ++ ihn tatsächlich kompiliert, fasziniert mich.
Tony318

Antworten:

132

Die Semantik der zusammengesetzten Zuweisungsoperatoren unterscheidet sich in C und C ++:

C99 Standard, 6.5.16, Teil 3:

Ein Zuweisungsoperator speichert einen Wert in dem vom linken Operanden angegebenen Objekt. Ein Zuweisungsausdruck hat den Wert des linken Operanden nach der Zuweisung, ist jedoch kein l-Wert.

In C ++ 5.17.1:

Der Zuweisungsoperator (=) und die zusammengesetzten Zuweisungsoperatoren gruppieren sich alle von rechts nach links. Alle benötigen einen modifizierbaren l-Wert als linken Operanden und geben nach erfolgter Zuweisung einen l-Wert mit dem Typ und Wert des linken Operanden zurück.

BEARBEITEN: Das Verhalten von (i+=10)+=10in C ++ ist in C ++ 98 undefiniert, in C ++ 11 jedoch gut definiert. In dieser Antwort auf die Frage von NPE finden Sie die relevanten Teile der Standards.

dasblinkenlight
quelle
Richtig. Man gibt den Ergebniswert zurück und man gibt die Variable (Adresse) zurück
texasbruce
7
Wichtig : Beachten Sie, dass (i+=10)+=10es sich in C ++ um ein undefiniertes Verhalten handelt, siehe @ aix-Antwort.
David Rodríguez - Dribeas
@ DavidRodríguez-dribeas Du meintest nicht spezifiziert , nicht undefiniert , oder?
Dasblinkenlight
4
@dasblinkenlight: Nein, er meinte undefiniert . In C ++ 03 und früheren Versionen verhält sich das Ändern des l-Wertergebnisses eines Ausdrucks in allen Compilern unvorhersehbar , da kein dazwischenliegender Sequenzpunkt vorhanden ist. Wenn es nicht spezifiziert wäre , würde es sich auf verschiedenen Compilern vorhersehbar, aber unterschiedlich verhalten .
Justin 18
2
Dies wäre in einer Einstellung wie dem int f(int &y); f(x += 10);Übergeben eines Verweises auf die geänderte Variable an eine Funktion nützlich gewesen .
Phil Miller
51

Zusätzlich zum ungültigen C-Code ist die Zeile

(i+=10)+=10;

würde sowohl in C als auch in C ++ 03 zu einem undefinierten Verhalten führen, da es izwischen Sequenzpunkten zweimal geändert würde .

Warum es in C ++ kompiliert werden darf:

[C ++ N3242 5.17.1] Der Zuweisungsoperator (=) und die zusammengesetzten Zuweisungsoperatoren gruppieren sich alle von rechts nach links. Alle benötigen einen modifizierbaren l-Wert als linken Operanden und geben einen l-Wert zurück, der sich auf den linken Operanden bezieht.

Im selben Absatz heißt es weiter

In allen Fällen wird die Zuweisung nach der Wertberechnung des rechten und linken Operanden und vor der Wertberechnung des Zuweisungsausdrucks sequenziert.

Dies deutet darauf hin, dass der Ausdruck in C ++ 11 kein undefiniertes Verhalten mehr aufweist.

NPE
quelle
3
Nun, es ist sicherlich UB wegen der Sequenzpunkte. Es ist auch ungültiger Code in C (aber nicht in C ++), aber das hat nichts mit Sequenzpunkten zu tun und sollte vom Compiler abgefangen werden.
Konrad Rudolph
2
@KonradRudolph: Nein, der Compiler ist nicht verpflichtet, undefiniertes Verhalten zu erfassen, im Gegensatz zu schlecht geformtem Code. Im Teil "sollte vom Compiler abgefangen werden" sind wir uns nicht einig.
2
Sequenzpunkte sind in C ++ 11 nicht vorhanden. Der eigentliche Grund für die UB besteht darin, dass zwei Änderungen nicht isequenziert wurden.
Mankarse
4
Es ist falsch, dass dies undefiniertes Verhalten ist. Wenn die Zuweisung vor der Wertberechnung des Zuweisungsausdrucks keine Sequenz i = j+=1wäre, würde dies zu einem unbestimmten Wert führen. Aus demselben Absatz zitieren Sie "In allen Fällen wird die Zuweisung nach der Wertberechnung des rechten und linken Operanden und vor der Wertberechnung des Zuweisungsausdrucks sequenziert." Daher (i+=10)+=10ist gut definiert zu tun i += 10; i += 10;. Auf der anderen Seite (i+=10)+=(i+=10)ist UB.
Bames53
2
Da ich sah, dass es zwischen den Kommentatoren einige Meinungsverschiedenheiten gab, habe ich dies als separate Frage gepostet: stackoverflow.com/questions/10655290/…
NPE