Wie funktioniert der Comma Operator?

Wie funktioniert der Komma-Operator in C ++?

Zum Beispiel, wenn ich Folgendes mache:

a = b, c; 

Ist ein Ende gleich b oder c?

(Ja, ich weiß, dass das einfach zu testen ist – hier einfach dokumentieren, damit jemand die Antwort schnell findet.)

Update: Diese Frage hat bei Verwendung des Komma-Operators eine Nuance ergeben. Nur um das zu dokumentieren:

 a = b, c; // a is set to the value of b! a = (b, c); // a is set to the value of c! 

Diese Frage wurde tatsächlich von einem Tipperrors im Code inspiriert. Was sollte sein?

 a = b; c = d; 

Wurde zu

 a = b, // <- Note comma typo! c = d; 

    Es wäre gleich b .

    Der Kommaoperator hat eine niedrigere Priorität als die Zuweisung.

    Beachten Sie, dass der Komma-Operator möglicherweise in C ++ überladen wird. Das tatsächliche Verhalten kann sich daher sehr von dem erwarteten unterscheiden.

    Als Beispiel verwendet Boost.Spirit den Komma-Operator sehr geschickt, um Listeninitialisierer für Symboltabellen zu implementieren. Somit macht es die folgende Syntax möglich und sinnvoll:

     keywords = "and", "or", "not", "xor"; 

    Beachten Sie, dass aufgrund der Vorrangstellung des Operators der Code (absichtlich!) Identisch ist mit

     (((keywords = "and"), "or"), "not"), "xor"; 

    Das heißt, der erste aufgerufene Operator ist keywords.operator =("and") der ein Proxy-Objekt zurückgibt, auf dem die verbleibenden operator, s aufgerufen werden:

     keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor"); 

    Der Komma-Operator hat die niedrigste Priorität aller C / C ++ – Operatoren. Daher ist es immer der letzte, der an einen Ausdruck bindet, also:

     a = b, c; 

    ist äquivalent zu:

     (a = b), c; 

    Eine weitere interessante Tatsache ist, dass der Kommaoperator einen Sequenzpunkt einführt. Dies bedeutet, dass der Ausdruck:

     a+b, c(), d 

    wird garantiert, dass seine drei Unterausdrücke ( a + b , c () und d ) in der Reihenfolge ausgewertet werden. Dies ist signifikant, wenn sie Nebenwirkungen haben. Normalerweise dürfen Compiler Teilausdrücke in beliebiger Reihenfolge auswerten. Zum Beispiel in einem functionsaufruf:

     someFunc(arg1, arg2, arg3) 

    Argumente können in beliebiger Reihenfolge ausgewertet werden. Beachten Sie, dass die Kommas im functionsaufruf keine Operatoren sind. Sie sind Separatoren.

    Der Komma-Operator:

    • hat die niedrigste Priorität
    • ist linksassoziativ

    Eine Standardversion des Komma-Operators ist für alle Typen (integriert und benutzerdefiniert) definiert und funktioniert wie folgt – mit exprA , exprB :

    • exprA wird bewertet
    • Das Ergebnis von exprA wird ignoriert
    • exprB wird bewertet
    • Das Ergebnis von exprB wird als Ergebnis des gesamten Ausdrucks zurückgegeben

    Bei den meisten Operatoren kann der Compiler die Ausführungsreihenfolge wählen, und es ist sogar erforderlich, die Ausführung zu überspringen, wenn dies das Endergebnis nicht beeinflusst (zB false && foo() überspringt den Aufruf von foo ). Dies ist jedoch nicht der Fall für den Komma-Operator und die obigen Schritte werden immer passieren * .

    In der Praxis funktioniert der Standard-Komma-Operator fast genauso wie ein Semikolon. Der Unterschied besteht darin, dass zwei durch ein Semikolon getrennte Ausdrücke zwei separate statementen bilden, während die Kommatrennung alle als einen einzigen Ausdruck enthält. Aus diesem Grund wird der Komma-Operator manchmal in den folgenden Szenarien verwendet:

    • C-Syntax erfordert einen einzelnen Ausdruck , keine statement. zB in if( HERE )
    • C-Syntax benötigt eine einzelne statement, nicht mehr, zB bei der Initialisierung der for Schleife for ( HERE ; ; )
    • Wenn Sie geschweifte Klammern überspringen und eine einzelne statement if (foo) HERE ; möchten: if (foo) HERE ; (Bitte tu das nicht, es ist wirklich hässlich!)

    Wenn eine statement kein Ausdruck ist, kann das Semikolon nicht durch ein Komma ersetzt werden. Zum Beispiel sind diese nicht erlaubt:

    • (foo, if (foo) bar) ( if kein Ausdruck ist)
    • int x, int y (Variablendeklaration ist kein Ausdruck)

    In Ihrem Fall haben wir:

    • a=b, c; , äquivalent zu a=b; c; a=b; c; , vorausgesetzt, dass a vom Typ ist, der den Kommaoperator nicht überlädt.
    • a = b, c = d; äquivalent zu a=b; c=d; a=b; c=d; , vorausgesetzt, dass a vom Typ ist, der den Kommaoperator nicht überlädt.

    Beachten Sie, dass nicht jedes Komma ein Komma-Operator ist. Einige Kommas, die eine ganz andere Bedeutung haben:

    • int a, b; — Variablendeklarationsliste ist durch Kommas getrennt, aber dies sind keine Kommaoperatoren
    • int a=5, b=3; — Dies ist auch eine kommagetrennte Variablendeklarationsliste
    • foo(x,y) — Komma-getrennte Argumentliste. Tatsächlich können x und y in beliebiger Reihenfolge ausgewertet werden!
    • FOO(x,y) — durch Kommas getrennte Makroargumentliste
    • foo — durch Komma getrennte Template-Argumentliste
    • int foo(int a, int b) — durch Komma getrennte Parameterliste
    • Foo::Foo() : a(5), b(3) {} — durch Kommas getrennte Initialisierungsliste in einem classnkonstruktor

    * Dies trifft nicht ganz zu, wenn Sie Optimierungen anwenden. Wenn der Compiler erkennt, dass bestimmte Teile des Codes absolut keinen Einfluss auf den Rest haben, werden unnötige statementen entfernt.

    Weiterführende Literatur: http://en.wikipedia.org/wiki/Comma_operator

    Der Wert von a ist b , aber der Wert des Ausdrucks ist c . Das ist in

     d = (a = b, c); 

    a wäre gleich b und d wäre gleich c .

    Der Wert von b wird a zugewiesen. Nichts wird c passieren

    Der Wert von a wird gleich b sein, da der Komma-Operator eine niedrigere Priorität als der Zuweisungsoperator hat.

    Ja Der Komma-Operator hat eine niedrigere Priorität als der Zuweisungsoperator

     #include int main() { int i; i = (1,2,3); printf("i:%d\n",i); return 0; } 

    Ausgabe: i = 3
    Weil der Komma-Operator immer den am weitesten rechts liegenden Wert zurückgibt.
    Bei Kommaoperator mit Zuweisungsoperator:

      int main() { int i; i = 1,2,3; printf("i:%d\n",i); return 0; } 

    Ausgang: i = 1
    Wie wir wissen, hat der Komma-Operator eine niedrigere Priorität als die Zuweisung …..

    Das Wichtigste zuerst: Komma ist eigentlich kein Operator, für den Compiler ist es nur ein Token, das im Zusammenhang mit anderen Tokens eine Bedeutung bekommt.

    Was bedeutet das und warum?

    Beispiel 1:

    Um den Unterschied zwischen der Bedeutung desselben Tokens in einem anderen Kontext zu verstehen, betrachten wir dieses Beispiel:

     class Example { Foo ContentA; } 

    Normalerweise würde ein C ++ Anfänger denken, dass dieser Ausdruck Dinge vergleichen könnte, aber es ist absolut falsch, die Bedeutung der Symbole < , > und , abhängig vom Kontext der Verwendung.

    Die korrekte Interpretation des obigen Beispiels ist natürlich, dass es sich um eine Schablone handelt.

    Beispiel 2:

    Wenn wir eine typische for-Schleife mit mehr als einer Initialisierungsvariablen und / oder mehr als einem Ausdruck schreiben, der nach jeder Iteration der Schleife ausgeführt werden soll, verwenden wir auch Komma:

     for(a=5,b=0;a<42;a++,b--) ... 

    Die Bedeutung des Kommas hängt vom Nutzungskontext ab, hier ist es der Kontext der Konstruktion.

    Was bedeutet ein Komma im Zusammenhang eigentlich?

    Um es noch komplizierter zu machen (wie immer in C ++), kann der Komma-Operator selbst überlastet sein (Danke an Konrad Rudolph für das Hinzeigen).

    Um auf die Frage zurückzukommen, der Code

     a = b, c; 

    bedeutet für den Compiler sowas wie

     (a = b), c; 

    weil die Priorität von = token / operator höher ist als die Priorität von token.

    und dies wird im Kontext wie interpretiert

     a = b; c; 

    (Beachten Sie, dass die Interpretation vom Kontext abhängt, hier ist es weder ein functions- / Methodenaufruf noch eine Template-Initialisierung.)