Was macht der ,
Operator in C?
Was macht der ,
Operator in C?
Antworten:
Der Ausdruck:
(expression1, expression2)
Zuerst wird Ausdruck1 ausgewertet, dann wird Ausdruck2 ausgewertet und der Wert von Ausdruck2 wird für den gesamten Ausdruck zurückgegeben.
i
die Werte 5, 4, 3, 2 oder 1 angegeben. Es ist einfach 0. Es ist praktisch nutzlos, es sei denn, die Ausdrücke haben Nebenwirkungen.
i = b, c;
ist äquivalent zu (i = b), c
weil, weil die Zuweisung =
eine höhere Priorität hat als der Kommaoperator ,
. Der Kommaoperator hat die niedrigste Priorität von allen.
expression1, expression2;
zuerst expression1
ausgewertet, vermutlich auf seine Nebenwirkungen (wie das Aufrufen einer Funktion), dann gibt es einen Sequenzpunkt, dann expression2
wird ausgewertet und der Wert zurückgegeben…
Ich habe am häufigsten in while
Schleifen verwendet gesehen:
string s;
while(read_string(s), s.len() > 5)
{
//do something
}
Es wird die Operation ausführen und dann einen Test basierend auf einer Nebenwirkung durchführen. Der andere Weg wäre, es so zu machen:
string s;
read_string(s);
while(s.len() > 5)
{
//do something
read_string(s);
}
while (read_string(s) && s.len() > 5)
. Offensichtlich würde das nicht funktionieren, wenn read_string
es keinen Rückgabewert gibt (oder keinen aussagekräftigen). (Edit: Sorry, habe nicht bemerkt, wie alt dieser Beitrag war.)
while (1)
mit einer break;
Aussage im Körper zu verwenden. Der Versuch, den ausbrechenden Teil des Codes in den while-Test oder in den do-while-Test zu zwingen, ist oft eine Energieverschwendung und erschwert das Verständnis des Codes.
while(1)
und break
;
Der Kommaoperator wertet den linken Operanden aus, verwirft das Ergebnis und wertet dann den rechten Operanden aus, und das ist das Ergebnis. Die im Link angegebene idiomatische Verwendung erfolgt beim Initialisieren der in einer for
Schleife verwendeten Variablen und gibt das folgende Beispiel:
void rev(char *s, size_t len)
{
char *first;
for ( first = s, s += len - 1; s >= first; --s)
/*^^^^^^^^^^^^^^^^^^^^^^^*/
putchar(*s);
}
Ansonsten gibt es nicht viele großartige Verwendungsmöglichkeiten für den Kommaoperator , obwohl es leicht zu missbrauchen ist, Code zu generieren, der schwer zu lesen und zu warten ist.
Nach dem Entwurf des C99-Standards lautet die Grammatik wie folgt:
expression:
assignment-expression
expression , assignment-expression
und Absatz 2 sagt:
Der linke Operand eines Kommaoperators wird als void-Ausdruck ausgewertet. Nach seiner Auswertung gibt es einen Sequenzpunkt. Dann wird der richtige Operand ausgewertet; Das Ergebnis hat seinen Typ und Wert. 97) Wenn versucht wird, das Ergebnis eines Kommaoperators zu ändern oder nach dem nächsten Sequenzpunkt darauf zuzugreifen, ist das Verhalten undefiniert.
In Fußnote 97 heißt es:
Ein Kommaoperator liefert keinen l-Wert .
Dies bedeutet, dass Sie dem Ergebnis des Kommaoperators keine Zuordnung zuweisen können .
Es ist wichtig zu beachten, dass der Kommaoperator die niedrigste Priorität hat. Daher gibt es Fälle, in denen die Verwendung ()
einen großen Unterschied machen kann, zum Beispiel:
#include <stdio.h>
int main()
{
int x, y ;
x = 1, 2 ;
y = (3,4) ;
printf( "%d %d\n", x, y ) ;
}
wird die folgende Ausgabe haben:
1 4
Der Kommaoperator kombiniert die beiden Ausdrücke auf beiden Seiten zu einem und wertet beide in der Reihenfolge von links nach rechts aus. Der Wert der rechten Seite wird als Wert des gesamten Ausdrucks zurückgegeben.
(expr1, expr2)
ist wie, { expr1; expr2; }
aber Sie können das Ergebnis von expr2
in einem Funktionsaufruf oder einer Zuweisung verwenden.
In for
Schleifen wird häufig festgestellt, dass mehrere Variablen wie folgt initialisiert oder verwaltet werden:
for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
/* do something with low and high and put new values
in newlow and newhigh */
}
Abgesehen davon habe ich es nur "in Wut" in einem anderen Fall verwendet, wenn zwei Operationen zusammengefasst wurden, die immer in einem Makro zusammenpassen sollten. Wir hatten Code, der verschiedene Binärwerte in einen Bytepuffer zum Senden in ein Netzwerk kopierte, und einen Zeiger, der dort beibehalten wurde, wo wir waren:
unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;
*ptr++ = first_byte_value;
*ptr++ = second_byte_value;
send_buff(outbuff, (int)(ptr - outbuff));
Wo die Werte short
s oder int
s waren, haben wir dies getan:
*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;
Später lesen wir, dass dies kein wirklich gültiges C war, da (short *)ptr
es kein l-Wert mehr ist und nicht inkrementiert werden kann, obwohl es unserem damaligen Compiler nichts ausmachte. Um dies zu beheben, teilen wir den Ausdruck in zwei Teile:
*(short *)ptr = short_value;
ptr += sizeof(short);
Dieser Ansatz beruhte jedoch darauf, dass alle Entwickler daran dachten, beide Anweisungen ständig einzugeben. Wir wollten eine Funktion, mit der Sie den Ausgabezeiger, den Wert und den Werttyp übergeben können. Da dies C und nicht C ++ mit Vorlagen ist, konnte keine Funktion einen beliebigen Typ annehmen, daher haben wir uns für ein Makro entschieden:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Mit dem Komma-Operator konnten wir dies in Ausdrücken oder als Anweisungen verwenden, wie wir wollten:
if (need_to_output_short)
ASSIGN_INCR(ptr, short_value, short);
latest_pos = ASSIGN_INCR(ptr, int_value, int);
send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));
Ich schlage nicht vor, dass eines dieser Beispiele einen guten Stil hat! In der Tat erinnere ich mich an Steve McConnells Code Complete, der davon abgeraten hat, Kommaoperatoren in einer for
Schleife zu verwenden: Aus Gründen der Lesbarkeit und Wartbarkeit sollte die Schleife nur von einer Variablen gesteuert werden, und die Ausdrücke in der for
Zeile selbst sollten nur Code für die Schleifensteuerung enthalten. keine anderen zusätzlichen Teile der Initialisierung oder Schleifenwartung.
Es bewirkt die Auswertung mehrerer Anweisungen, verwendet jedoch nur die letzte als resultierenden Wert (rvalue, glaube ich).
So...
int f() { return 7; }
int g() { return 8; }
int x = (printf("assigning x"), f(), g() );
sollte dazu führen, dass x auf 8 gesetzt wird.
Der einzige Ort, an dem ich gesehen habe, dass es nützlich ist, ist, wenn Sie eine funky Schleife schreiben, in der Sie mehrere Dinge in einem der Ausdrücke ausführen möchten (wahrscheinlich den Init-Ausdruck oder den Schleifenausdruck. So etwas wie:
bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
size_t i1, i2;
for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
{
if(a1[i1] != a2[i2])
{
return false;
}
}
return true;
}
Verzeihen Sie mir, wenn es Syntaxfehler gibt oder wenn ich etwas eingemischt habe, das nicht streng C ist. Ich behaupte nicht, dass der Operator in guter Form ist, aber dafür könnten Sie ihn verwenden. Im obigen Fall würde ich wahrscheinlich while
stattdessen eine Schleife verwenden, damit die mehrfachen Ausdrücke auf init und loop offensichtlicher sind. (Und ich würde i1 und i2 inline initialisieren, anstatt zu deklarieren und dann zu initialisieren ... bla bla bla.)
Ich belebe dies einfach, um Fragen von @Rajesh und @JeffMercado zu beantworten, die ich für sehr wichtig halte, da dies einer der Top-Suchmaschinen-Hits ist.
Nehmen Sie zum Beispiel den folgenden Codeausschnitt
int i = (5,4,3,2,1);
int j;
j = 5,4,3,2,1;
printf("%d %d\n", i , j);
Es wird gedruckt
1 5
Der i
Fall wird wie in den meisten Antworten erläutert behandelt. Alle Ausdrücke werden in der Reihenfolge von links nach rechts ausgewertet, aber nur der letzte wird zugewiesen i
. Das Ergebnis des (
Ausdrucks ) is
1`.
Der j
Fall folgt unterschiedlichen Prioritätsregeln, da er ,
die niedrigste Operatorpriorität hat. Aufgrund dieser Regeln sieht der Compiler Zuweisungsausdruck, konstant, konstant ... . Die Ausdrücke werden wieder in von links nach rechts ausgewertet und ihre Nebenwirkungen bleiben sichtbar, daher j
ist 5
als Ergebnis j = 5
.
Interessanterweise int j = 5,4,3,2,1;
ist die Sprachspezifikation nicht zulässig. Ein Initialisierer erwartet einen Zuweisungsausdruck, sodass ein direkter ,
Operator nicht zulässig ist.
Hoffe das hilft.