Was ist die richtige Antwort für cout

Lesezeit: 1 Minute

Was ist die richtige Antwort fur cout
Pravs

Kürzlich in einem Interview gab es eine folgende objektive Artfrage.

int a = 0;
cout << a++ << a;

Antworten:

A. 10
B. 01
C. undefiniertes Verhalten

Ich habe Wahl b beantwortet, dh Ausgabe wäre “01”.

Aber zu meiner Überraschung wurde mir später von einem Interviewer gesagt, dass die richtige Antwort Option c ist: undefiniert.

Nun, ich kenne das Konzept der Sequenzpunkte in C++. Das Verhalten ist für die folgende Anweisung nicht definiert:

int i = 0;
i += i++ + i++;

aber nach meinem Verständnis für die Aussage cout << a++ << a der ostream.operator<<() würde zweimal angerufen werden, zuerst mit ostream.operator<<(a++) und später ostream.operator<<(a).

Ich habe auch das Ergebnis auf dem VS2010-Compiler überprüft und seine Ausgabe ist ebenfalls ’01’.

  • Hast du nach einer Erklärung gefragt? Ich interviewe oft potenzielle Kandidaten und bin sehr daran interessiert, Fragen zu erhalten, das zeigt Interesse.

    – Brady

    28. Mai 2012 um 10:14 Uhr

  • @jrok Es ist undefiniertes Verhalten. Alles, was die Implementierung tut (einschließlich des Versendens einer beleidigenden E-Mail in Ihrem Namen an Ihren Chef), ist konform.

    – James Kanze

    28. Mai 2012 um 11:12 Uhr

  • Diese Frage schreit nach einem C++11 (the Strom Version von C++) Antwort, die keine Sequenzpunkte erwähnt. Leider kenne ich mich mit dem Ersatz von Sequenzpunkten in C++11 nicht aus.

    – CB Bailey

    28. Mai 2012 um 11:34 Uhr

  • Wenn es nicht undefiniert wäre, könnte es definitiv nicht sein 10es wäre entweder 01 oder 00. (c++ wird immer den Wert auswerten c hatte Vor inkrementiert wird). Und selbst wenn es nicht undefiniert wäre, wäre es immer noch schrecklich verwirrend.

    – linksherum

    28. Mai 2012 um 11:38 Uhr


  • Weißt du, als ich den Titel „cout << c++ << c“ las, dachte ich kurz daran als eine Aussage über die Beziehung zwischen den Sprachen C und C++ und einer anderen namens „cout“. Weißt du, wie jemand gesagt hat, dass er dachte, dass „cout“ C++ viel unterlegen sei und dass C++ C viel unterlegen sei – und wahrscheinlich aufgrund der Transitivität, dass „cout“ sehr, sehr viel schlechter als C. 🙂

    – tchrist

    28. Mai 2012 um 19:36 Uhr


Was ist die richtige Antwort fur cout
Maxim Egorushkin

Sie können sich vorstellen:

cout << a++ << a;

Als:

std::operator<<(std::operator<<(std::cout, a++), a);

C++ garantiert, dass alle Seiteneffekte früherer Auswertungen durchgeführt wurden Sequenzpunkte. Es gibt keine Sequenzpunkte zwischen der Auswertung der Funktionsargumente, was dieses Argument bedeutet a kann vor dem Argument ausgewertet werden std::operator<<(std::cout, a++) oder danach. Das Ergebnis des Obigen ist also undefiniert.


C++17-Update

In C++17 wurden die Regeln aktualisiert. Bestimmtes:

In einem Verschiebungsoperatorausdruck E1<<E2 und E1>>E2jede Wertberechnung und Nebenwirkung von E1 wird vor jeder Wertberechnung und Nebenwirkung von sequenziert E2.

Das bedeutet, dass der Code erforderlich ist, um ein Ergebnis zu erzeugen bdie ausgibt 01.

Sehen P0145R3 Verfeinern der Ausdrucksauswertungsreihenfolge für idiomatisches C++ für mehr Details.

  • @Maxim: Danke für die Erklärung. Bei den von Ihnen erläuterten Aufrufen wäre es ein undefiniertes Verhalten. Aber jetzt habe ich noch eine Frage (möglicherweise noch eine, und ich vermisse etwas Grundlegendes und denke laut nach): Wie haben Sie abgeleitet, dass die globale Version von std::operator<<() anstelle von ostream::operator< aufgerufen wird <() Mitgliedsversion. Beim Debuggen lande ich eher in einer Member-Version des ostream::operator<<()-Aufrufs als in der globalen Version, und das ist der Grund, warum ich anfangs dachte, dass die Antwort 01 wäre

    – Pravs

    28. Mai 2012 um 11:26 Uhr


  • @Maxim Nicht das es einen anders macht, aber da c Typ hat intder operator<< Hier sind Member-Funktionen.

    – James Kanze

    28. Mai 2012 um 11:32 Uhr

  • @pravs: ob operator<< eine Mitgliedsfunktion oder eine freistehende Funktion ist, wirkt sich nicht auf Sequenzpunkte aus.

    – Maxim Egorushkin

    28. Mai 2012 um 11:39 Uhr

  • Der ‘sequence point’ wird im C++-Standard nicht mehr verwendet. Sie war unpräzise und wurde durch die Relation „sequenziert vor/sequenziert nach“ ersetzt.

    – Rafal Dowgird

    28. Mai 2012 um 14:01 Uhr

  • So the result of the above is undefined. Ihre Erklärung ist nur gut für nicht spezifiziertnicht für nicht definiert. JamesKanze erklärte, wie es umso vernichtender wird nicht definiert aber in seiner Antwort.

    – Deduplizierer

    31. Mai 2014 um 0:45 Uhr


Was ist die richtige Antwort fur cout
Alok Speichern

Technisch gesehen ist dies insgesamt Undefiniertes Verhalten.

Aber es gibt zwei wichtige Aspekte bei der Antwort.

Die Code-Anweisung:

std::cout << a++ << a;

wird bewertet als:

std::operator<<(std::operator<<(std::cout, a++), a);

Der Standard definiert nicht die Reihenfolge der Auswertung von Argumenten für eine Funktion.
Also entweder:

  • std::operator<<(std::cout, a++) wird zuerst ausgewertet bzw
  • awird zuerst ausgewertet bzw
  • es kann sich um eine beliebige implementierungsdefinierte Reihenfolge handeln.

Diese Reihenfolge ist Nicht spezifiziert[Ref 1] nach Norm.

[Ref 1]C++03 5.2.2 Funktionsaufruf
Abs. 8

Die Reihenfolge der Bewertung der Argumente ist nicht festgelegt. Alle Seiteneffekte der Auswertung von Argumentausdrücken werden wirksam, bevor die Funktion eingegeben wird. Die Reihenfolge der Auswertung des Postfix-Ausdrucks und der Argumentausdrucksliste ist nicht festgelegt.

Ferner gibt es keinen Sequenzpunkt zwischen der Auswertung von Argumenten für eine Funktion, sondern ein Sequenzpunkt existiert nur nach der Auswertung aller Argumente[Ref 2].

[Ref 2]C++03 1.9 Programmausführung [intro.execution]:
Abs. 17:

Beim Aufruf einer Funktion (unabhängig davon, ob es sich um eine Inline-Funktion handelt oder nicht) gibt es nach der Auswertung aller Funktionsargumente (falls vorhanden) einen Sequenzpunkt, der vor der Ausführung von Ausdrücken oder Anweisungen im Funktionsrumpf stattfindet.

Beachten Sie, dass hier der Wert von c mehr als einmal ohne dazwischenliegenden Sequenzpunkt zugegriffen wird, heißt es dazu in der Norm:

[Ref 3]C++03 5 Ausdrücke [expr]:
Absatz 4:

….
Zwischen dem vorherigen und dem nächsten Sequenzpunkt darf ein Skalarobjekt seinen gespeicherten Wert höchstens einmal durch die Auswertung eines Ausdrucks modifizieren. Außerdem soll auf den vorherigen Wert nur zugegriffen werden, um den zu speichernden Wert zu bestimmen. Die Anforderungen dieses Absatzes müssen für jede zulässige Reihenfolge der Teilausdrücke eines vollständigen Ausdrucks erfüllt werden; andernfalls ist das Verhalten undefiniert.

Der Code ändert sich c mehr als einmal ohne dazwischenliegenden Sequenzpunkt und es wird nicht darauf zugegriffen, um den Wert des gespeicherten Objekts zu bestimmen. Dies ist ein klarer Verstoß gegen die obige Klausel und daher das von der Norm vorgeschriebene Ergebnis Undefiniertes Verhalten[Ref 3].

  • Ich meine, c wird nur einmal geändert, sodass das Programm legal 01 oder 10 drucken kann, aber nichts Seltsames tun kann. Ist mein Verständnis richtig?

    – jrok

    28. Mai 2012 um 10:40 Uhr


  • Technisch gesehen ist das Verhalten undefiniert, da ein Objekt modifiziert wird und an anderer Stelle ohne einen dazwischenliegenden Sequenzpunkt darauf zugegriffen wird. Undefiniert ist nicht nicht spezifiziert; sie lässt der Umsetzung noch mehr Spielraum.

    – James Kanze

    28. Mai 2012 um 11:14 Uhr

  • @Als Ja. Ich hatte Ihre Bearbeitungen nicht gesehen (obwohl ich auf jroks Aussage reagiert hatte, dass das Programm etwas Seltsames nicht tun kann – es kann). Ihre bearbeitete Version ist soweit gut, aber meiner Meinung nach ist das Schlüsselwort Teilbestellung; Sequenzpunkte führen nur eine teilweise Ordnung ein.

    – James Kanze

    28. Mai 2012 um 11:30 Uhr

  • Der neue C++0x-Standard sagt im Wesentlichen das Gleiche, aber in anderen Abschnitten und in anderen Formulierungen 🙂 Zitat: (1.9 Program Execution [intro.execution]Abs. 15): „Wenn eine Nebenwirkung auf ein skalares Objekt relativ zu einer anderen Nebenwirkung auf dasselbe skalare Objekt oder einer Wertberechnung unter Verwendung des Werts desselben skalaren Objekts nicht sequenziert ist, ist das Verhalten undefiniert.“

    – Rafal Dowgird

    28. Mai 2012 um 14:41 Uhr

  • Ich glaube, diese Antwort enthält einen Fehler. “std::cout<ist angegebenen.

    – Christoph Schmidt

    21. September 2013 um 4:08 Uhr

1646911216 783 Was ist die richtige Antwort fur cout
James Kanze

Sequenzpunkte definieren nur a teilweise Bestellung. In Ihrem Fall haben Sie (sobald die Überladungsauflösung abgeschlossen ist):

std::cout.operator<<( a++ ).operator<<( a );

Es gibt einen Sequenzpunkt zwischen den a++ und der erste Anruf zu
std::ostream::operator<<und es gibt einen Sequenzpunkt zwischen dem zweiten a und der zweite Aufruf an std::ostream::operator<<aber es gibt keinen Sequenzpunkt dazwischen a++ und a; die einzigen Bestellbeschränkungen sind die a++ vor dem ersten Anruf vollständig ausgewertet werden (einschließlich Nebenwirkungen). operator<<und das die zweite a vor dem zweiten Aufruf vollständig ausgewertet werden operator<<. (Es gibt auch kausale Ordnungseinschränkungen: der zweite Aufruf von to operator<< kann dem ersten nicht vorangestellt werden, da es die Ergebnisse des ersten als Argument benötigt.) §5/4 (C++03) besagt:

Sofern nicht anders angegeben, ist die Reihenfolge der Auswertung von Operanden einzelner Operatoren und Unterausdrücke einzelner Ausdrücke sowie die Reihenfolge, in der Seiteneffekte auftreten, nicht festgelegt. Zwischen dem vorherigen und dem nächsten Sequenzpunkt darf ein Skalarobjekt seinen gespeicherten Wert höchstens einmal durch die Auswertung eines Ausdrucks modifizieren. Außerdem soll auf den vorherigen Wert nur zugegriffen werden, um den zu speichernden Wert zu bestimmen. Die Anforderungen dieses Absatzes müssen für jede zulässige Reihenfolge der Teilausdrücke eines vollständigen Ausdrucks erfüllt werden; andernfalls ist das Verhalten undefiniert.

Eine der zulässigen Reihenfolgen Ihres Ausdrucks ist a++, aerster Aufruf an operator<<zweiter Aufruf an operator<<; dies modifiziert den gespeicherten Wert von a (a++) und greift darauf zu, außer um den neuen Wert zu bestimmen (die zweite a), ist das Verhalten undefiniert.

  • Ein Fang von Ihrem Zitat des Standards. Das IIRC „außer wo angegeben“ enthält eine Ausnahme beim Umgang mit einem überladenen Operator, die den Operator als Funktion behandelt und daher einen Sequenzpunkt zwischen dem ersten und zweiten Aufruf von std::ostream::operator<<(int ). Bitte korrigieren Sie mich, wenn ich falsch liege.

    – Christoph Schmidt

    21. September 2013 um 3:14 Uhr

  • @ChristopherSmith Ein überladener Operator verhält sich wie ein Funktionsaufruf. Wenn c waren ein Benutzertyp mit einem Benutzerdefiniert ++anstatt intwären die Ergebnisse nicht spezifiziert, aber es gäbe kein undefiniertes Verhalten.

    – James Kanze

    21. September 2013 um 10:28 Uhr

  • @ChristopherSmith Wo siehst du einen Sequenzpunkt zwischen den beiden c in foo(foo(bar(c)), c)? Es gibt einen Sequenzpunkt, wenn Funktionen aufgerufen werden und wenn sie zurückkehren, aber zwischen den Auswertungen der beiden ist kein Funktionsaufruf erforderlich c.

    – James Kanze

    2. Mai 2014 um 8:21 Uhr

  • @ChristopherSmith Wenn c war ein UDT, der überladene Operatoren möchten Funktionsaufrufe sein und einen Sequenzpunkt einführen würden, damit das Verhalten nicht undefiniert wäre. Aber es wäre noch nicht spezifiziert, ob der Unterausdruck c vorher oder nachher ausgewertet wurde c++ob Sie also die inkrementierte Version erhalten haben oder nicht, würde nicht angegeben (und müsste theoretisch nicht jedes Mal gleich sein).

    – James Kanze

    30. Dezember 2014 um 20:18 Uhr

  • @ChristopherSmith Alles vor dem Sequenzpunkt wird vor allem nach dem Sequenzpunkt passieren. Aber Sequenzpunkte definieren nur eine teilweise Ordnung. In dem betreffenden Ausdruck gibt es beispielsweise keinen Sequenzpunkt zwischen den Teilausdrücken c und c++, also können die beiden in beliebiger Reihenfolge auftreten. Was Semikolons angeht… Sie bewirken nur insofern einen Sequenzpunkt, als es sich um Vollausdrücke handelt. Weitere wichtige Ablaufpunkte sind der Funktionsaufruf: f(c++) wird die Erhöhung sehen c in fund der Kommaoperator, &&, || und ?: verursachen auch Sequenzpunkte.

    – James Kanze

    4. Januar 2015 um 17:40 Uhr

Die richtige Antwort ist, die Frage zu hinterfragen. Die Aussage ist nicht akzeptabel, weil ein Leser keine klare Antwort sehen kann. Eine andere Betrachtungsweise ist, dass wir Nebeneffekte (c++) eingeführt haben, die die Interpretation der Anweisung erheblich erschweren. Kurzer Code ist großartig, vorausgesetzt, seine Bedeutung ist klar.

987710cookie-checkWas ist die richtige Antwort für cout

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy