Frage

    

Diese Frage bereits eine Antwort hier:

    
            
  •              Was der Komma-Operator ist, zu tun?                                      9 Antworten                          
  •     
    

Sie sehen es für Loop-Anweisungen verwendet wird, aber es ist legal überall Syntax. Welche Nutzen haben Sie es an anderer Stelle gefunden, wenn überhaupt?

War es hilfreich?

Lösung

C-Sprache (wie auch C ++) ist historisch eine Mischung aus zwei völlig unterschiedlichen Programmierstilen, die man als „Anweisung Programmierung“ und „Ausdruck Programmierung“ verweisen. Wie Sie wissen, unterstützt jede prozedurale Programmiersprache normalerweise solche grundlegenden Konstrukte wie Sequenzierung und Verzweigung (siehe Strukturierte Programmierung ). Diese grundlegenden Konstrukte sind in C / C ++ Sprachen in zwei Formen: eine für die Anweisung Programmierung, ein anderer für die Expression Programmierung

.

Zum Beispiel, wenn Sie Ihr Programm in Bezug auf den Aussagen zu schreiben, haben Sie vielleicht eine Folge von Anweisungen durch ; getrennt verwenden. Wenn Sie eine Verzweigung tun möchten, verwenden Sie if Aussagen. Sie können auch Zyklen und andere Arten von Steuerübertragungs Anweisungen verwenden.

Im Ausdruck der Programmierung der gleichen Konstrukte wie auch Sie zur Verfügung stehen. Dies ist eigentlich, wo , Betreiber ins Spiel kommt. Operator , in nichts anderes als ein Separator sequentiellen Ausdrücke in C, das heißt Operator , in Ausdruck Programmierung dient die gleiche Rolle wie ; in Anweisung Programmierung des Fall ist. Verzweigen in Expressions Programmierung durch ?: Operator gemacht und alternativ durch Kurzschlussauswertungseigenschaften && und || Operatoren. (Expression Programmierung hat jedoch keine Zyklen. Und sie mit Rekursion ersetzen Sie haben würden Anweisung Programmierung anzuwenden.)

Zum Beispiel den folgenden Code

a = rand();
++a;
b = rand();
c = a + b / 2;
if (a < c - 5)
  d = a;
else
  d = b;

, die ein Beispiel für die traditionelle Erklärung Programmierung ist, kann in Bezug auf die Expression Programmierung als

neu geschrieben werden
a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? d = a : d = b;

oder als

a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;

oder

d = (a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? a : b);

oder

a = rand(), ++a, b = rand(), c = a + b / 2, (a < c - 5 && (d = a, 1)) || (d = b);

Unnötig zu sagen, in der Praxis Anweisung Programmierung erzeugt in der Regel viel besser lesbar C / C ++ Code, so dass wir normalerweise Ausdruck Programmierung in sehr gut gemessen und eingeschränkten Mengen verwenden. Aber in vielen Fällen kommt es praktisch. Und die Linie zwischen dem, was akzeptabel ist und was nicht zu einem großen Teil eine Frage des persönlichen Geschmacks und der Fähigkeit etabliert Idiome zu erkennen und zu lesen.

Als zusätzliche Anmerkung: das sehr Design der Sprache offensichtlich auf Aussagen zugeschnitten ist. Aussagen können Ausdrücke frei aufrufen, aber Ausdrücke können keine Aussagen aufrufen (abgesehen vordefinierte Funktionen aus aufrufen). Diese Situation ist in eine ziemlich interessanten Art und Weise in GCC Compiler geändert, die so genannten " statement Ausdrücke " als Erweiterung (symmetrisch "expression Aussagen" in Standard-C). „Statement Ausdrücke“ erlaubt dem Anwender, direkt anweisungsbasierte Code in Ausdrücke einzufügen, wie können sie Ausdruck-basierten Code in Anweisungen in Standard C einzufügen.

Als weitere zusätzliche Anmerkung: in C ++ Sprache Funktors-basierte Programmierung spielt eine wichtige Rolle, die als eine andere Form von „expression Programmierung“ zu sehen ist. Nach den aktuellen Trends in C ++ Design, könnte es über traditionelle Aussage Programmierung in vielen Situationen bevorzugt berücksichtigt werden.

Andere Tipps

Ich denke, im Allgemeinen C das Komma ist kein guter Stil einfach zu bedienen, weil es so sehr, sehr einfach zu verpassen - entweder von jemandem anderen zu lesen / verstehen / reparieren Ihren Code versuchen, oder Sie sich einen Monat auf der ganzen Linie. Außerhalb von Variablendeklarationen und für Schleifen, natürlich, wo es idiomatisch.

Sie können es verwenden, zum Beispiel mehr Anweisungen in einen ternären Operator zu packen (:), ala:

int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;

aber meine Götter, warum?!? (Ich habe es auf diese Weise gesehen in echten Code verwendet, aber keinen Zugriff auf sie leider zeigen)

Ich habe es in Makros gesehen verwendet, wo das Makro, eine Funktion zu sein vorgibt und will einen Wert zurückgeben, aber einige andere Arbeiten zuerst tun muss. Es ist immer hässlich und oft sieht aus wie ein gefährliches Hack though.

Vereinfachtes Beispiel:

#define SomeMacro(A) ( DoWork(A), Permute(A) )

Hier B=SomeMacro(A) "Erträge" das Ergebnis der Vertausche (A) und weist sie "B".

Zwei Killer Komma-Operator-Funktionen in C ++:

a) Lesen von Strom, bis bestimmte Zeichenfolge gefunden wird (hilft den Code trocken zu halten):

 while (cin >> str, str != "STOP") {
   //process str
 }

b) Schreiben komplexen Code in Konstruktor Initialisierungen:

class X : public A {
  X() : A( (global_function(), global_result) ) {};
};

Ich hatte ein Komma zu debuggen Mutex Sperren zu verwenden, um eine Nachricht zu setzen, bevor die Sperre zu warten beginnt.

Ich konnte nicht aber die Log-Nachricht in den Körper des abgeleiteten Schlossbauer, also musste ich es in den Argumenten der Basisklassenkonstruktor setzen mit: Basisklasse ((log ( „message“), actual_arg)) in der Initialisierungsliste. Beachten Sie die zusätzliche Klammer.

Hier ist ein Auszug aus den Klassen:

class NamedMutex : public boost::timed_mutex
{
public:
    ...

private:
    std::string name_ ;
};

void log( NamedMutex & ref__ , std::string const& name__ )
{
    LOG( name__ << " waits for " << ref__.name_ );
}

class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:

    NamedUniqueLock::NamedUniqueLock(
        NamedMutex & ref__ ,
        std::string const& name__ ,
        size_t const& nbmilliseconds )
    :
        boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
            boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
            ref_( ref__ ),
            name_( name__ )
    {
    }

  ....

};

Die Zuordnung Bibliothek Boost ist ein gutes Beispiel für Überlastung des Komma-Operator in einer nützlichen, gut lesbare Art und Weise. Zum Beispiel:

using namespace boost::assign;

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;

Von dem C-Standard:

  

Der linke Operand eines Komma-Operator wird als nichtig Ausdruck ausgewertet; gibt es eine Folge Punkt nach der Auswertung. Dann wird der rechte Operand ausgewertet; das Ergebnis hat seine Art und Wert. (Ein Komma-Operator keine L-Wert ergeben.)) Wenn versucht wird, das Ergebnis eines Komma-Operator zu ändern oder es nach der nächsten Sequenz Punkt zuzugreifen, ist das Verhalten nicht definiert ist.

Kurz gesagt es können Sie mehr als einen Ausdruck angeben, wo C nur eine erwartet. Aber in der Praxis ist es meist verwendet in for-Schleifen.

Beachten Sie, dass:

int a, b, c;

ist nicht der Komma-Operator, es ist eine Liste von Deklaratoren.

Es wird manchmal in Makros, wie Debug-Makros wie folgt verwendet:

#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))

(Aber schauen Sie auf diese schreckliche Versagen , von meiner Wenigkeit, für das, was passieren kann, wenn Sie es übertreiben.)

Aber wenn Sie es wirklich brauchen, oder Sie sind sicher, dass es der Code besser lesbar und wartbar macht, würde ich empfehlen, gegen den Komma-Operator.

Sie können es überlasten (solange diese Frage einen „C ++“ Tag hat). Ich habe einige Codes zu sehen ist, wo überladenes Komma zur Erzeugung Matrizen verwendet wurde. Oder Vektoren, weiß ich nicht mehr genau. Ist es nicht schön (wenn auch ein wenig verwirrend):

myvector foo = 2, 3, 4, 5, 6;

Außerhalb eines for-Schleife, und auch dort hat sich ein Aroma von Codegeruch haben kann, der einzige Ort, den ich als eine gute Verwendung gesehen habe für den Komma-Operator ist als Teil eines Lösch:

 delete p, p = 0;

Der einzige Wert über die Alternative ist, dass Sie versehentlich / einfügen kopieren können nur die Hälfte dieser Operation, wenn es auf zwei Linien ist.

Ich mag es auch, denn wenn man es aus Gewohnheit tun, werden Sie nie die Null Zuordnung vergessen. (Natürlich, warum p nicht innerhalb somekind auto_ptr ist, smart_ptr, shared_ptr, etc Wrapper ist eine andere Frage.)

Da @Nicolas Goy des Zitat aus dem Standard, dann klingt es wie Sie Einzeiler für Schleifen wie schreiben könnte:

int a, b, c;
for(a = 0, b = 10; c += 2*a+b, a <= b; a++, b--);
printf("%d", c);

Aber guter Gott, die Menschen, wollen Sie wirklich Ihren C-Code machen mehr obskure auf diese Weise?

Generell Ich vermeide mit dem Komma-Operator, weil es nur Code macht weniger lesbar. In fast allen Fällen wäre es einfacher und klarer zu nur zwei Aussagen zu machen. Wie:

foo=bar*2, plugh=hoo+7;

bietet keinen klaren Vorteil gegenüber:

foo=bar*2;
plugh=hoo+7;

Der einzige Ort neben Schleifen, wo ich es es verwendet haben, in if / else-Konstrukte, wie:

if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...

Sie können die Funktion vor dem IF setzen, aber wenn die Funktion dauert eine lange Zeit zu laufen, könnten Sie tun, um es vermeiden wollen, wenn es nicht notwendig ist, und wenn die Funktion nicht, es sei denn ein getan werden soll! = 1, dann das ist keine Option. Die Alternative ist, zu nisten die IF ist eine zusätzliche Schicht. Das ist eigentlich das, was ich in der Regel tun, weil der obige Code ein wenig kryptisch. Aber ich habe es das Komma Weg jetzt getan und dann, weil Verschachtelung auch kryptisch ist.

Es ist sehr nützlich in einigen Kommentaren in ASSERT Makros hinzu:

ASSERT(("This value must be true.", x));

Da die meisten Stile Makros behaupten ausgibt den gesamten Text ihrer Argumentation, dies fügt ein zusätzliches Bit von nützlichen Informationen in die Behauptung.

Ich verwende es oft eine statische Initialisierer Funktion in einigen CPP-Dateien, laufen faul initialisiert Probleme mit klassischen Singletons zu vermeiden:

void* s_static_pointer = 0;

void init() {
    configureLib(); 
    s_static_pointer = calculateFancyStuff(x,y,z);
    regptr(s_static_pointer);
}

bool s_init = init(), true; // just run init() before anything else

Foo::Foo() {
  s_static_pointer->doStuff(); // works properly
}

Für mich ist das ein wirklich nützliche Fall mit Kommas in C wird mit ihnen etwas bedingt auszuführen.

  if (something) dothis(), dothat(), x++;

Dies ist äquivalent zu

  if (something) { dothis(); dothat(); x++; }

Es geht nicht um „die Eingabe weniger“, es ist nur manchmal sehr klar sieht.

Auch Schleifen sind einfach so:

while(true) x++, y += 5;

Natürlich können beide nur dann sinnvoll sein, wenn der Bedingungsteil oder ausführbarer Teil der Schleife ist recht klein, zwei bis drei Operationen.

Das einzige Mal, dass ich die , Operator außerhalb eines for Schleife verwendet gesehen haben war eine assingment in einer ternären Anweisung auszuführen. Es war vor langer Zeit, so kann ich nicht die genaue Aussage remeber aber es war so etwas wie:

int ans = isRunning() ? total += 10, newAnswer(total) : 0;

Offensichtlich kein vernünftiger Mensch würde Code so schreiben, aber der Autor war ein böser Genius, c Aussagen, die auf den Assembler Code, den sie erzeugt, nicht die Lesbarkeit konstruieren. Zum Beispiel verwendet er manchmal Schleifen statt if-Anweisungen, weil er den Assembler bevorzugt es erzeugt wird.

Sein Code war sehr schnell, aber wartbaren, ich bin froh, dass ich muss damit nicht funktionieren nicht mehr.

ich verwendet habe, es für einen Makro auf „einen Wert eines beliebigen Typs zu einem Ausgangspuffer von einer char * darauf zuweisen und dann den Zeiger um die erforderliche Anzahl von Bytes zu erhöhen“, wie folgt:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

den Komma-Operator bedeutet das Makro kann in Ausdrücken oder als Anweisungen verwendet werden, wie gewünscht:

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));

Es reduziert eine wiederholte Eingabe, aber Sie müssen vorsichtig sein, es nicht zu unleserlich bekommt.

Bitte beachten Sie meine allzu lange Version dieser Antwort hier .

Es kann für "Code Golf" nützlich sein:

Code Golf: Spielen der Würfel

Die , in if(i>0)t=i,i=0; speichert zwei Zeichen ein.

qemu einen Code hat, der den Komma-Operator innerhalb des Bedingungsteils einer for-Schleife verwendet (siehe QTAILQ_FOREACH_SAFE in qemu-Queue.h). Was sie taten, läuft darauf hinaus, auf die folgenden:

#include <stdio.h>

int main( int argc, char* argv[] ){
  int x = 0, y = 0;

  for( x = 0; x < 3 && (y = x+1,1); x = y ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n\n", x, y );

  for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
    printf( "%d, %d\n", x, y );
  }

  printf( "\n%d, %d\n", x, y );
  return 0;
}

... mit der folgenden Ausgabe:

0, 1
1, 2
2, 3

3, 3

0, 1
1, 2
2, 3

3, 4

Die erste Version dieser Schleife hat folgende Auswirkungen:

  • Sie vermeidet zwei Aufgaben zu tun, so dass die Chancen des Codes out of sync bekommen verringert
  • Da es && verwendet, wird die Zuordnung nicht nach der letzten Iteration ausgewertet
  • Da die Zuordnung nicht ausgewertet, so wird es nicht versuchen, das nächste Element in der Warteschlange, um zu de-Referenz, wenn es am Ende ist (in qemu des Code, nicht der Code oben).
  • Innerhalb der Schleife haben Sie Zugriff auf die aktuelle und nächste Element

Gefunden es in Feldinitialisierung:

In C, was genau passiert, wenn i () verwenden, um eine doppelte Dimension Array anstelle der {}?

zu initialisieren

Wenn ich initialisieren ein Array a[][]:

int a[2][5]={(8,9,7,67,11),(7,8,9,199,89)};

und zeigt dann die Array-Elemente.

ich:

11 89 0 0 0 
0 0 0 0 0
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top