Warum funktioniert a++++b nicht?

Lesezeit: 8 Minuten

Benutzeravatar von Barshan Das
Barschan Das

int main ()
{
   int a = 5,b = 2;
   printf("%d",a+++++b);
   return 0;
}

Dieser Code gibt den folgenden Fehler:

Fehler: lvalue als Inkrement-Operand erforderlich

Aber wenn ich überall Leerzeichen setze a++ + und ++bdann funktioniert es einwandfrei.

int main ()
{
   int a = 5,b = 2;
   printf("%d",a++ + ++b);
   return 0;
}

Was bedeutet der Fehler im ersten Beispiel?

  • Es ist überraschend, dass nach all dieser Zeit niemand entdeckt hatte, dass der genaue Ausdruck, nach dem Sie fragen, als Beispiel im C99- und C11-Standard verwendet wird. Es gibt auch eine gute Erklärung. Ich habe das in meine Antwort aufgenommen.

    – Shafik Yaghmour

    25. Juli 2014 um 15:01 Uhr

  • @ShafikYaghmour – Das ist „Beispiel 2“ in C11 §6.4 Lexikalische Elemente ¶6. Es sagt “Das Programmfragment x+++++y wird analysiert als x ++ ++ + ywodurch eine Einschränkung für Inkrementoperatoren verletzt wird, obwohl die Analyse x ++ + ++ y könnte einen korrekten Ausdruck ergeben.”

    – Jonathan Leffler

    16. Oktober 2019 um 4:12 Uhr


Benutzeravatar von Lou Franco
Lou Franco

Compiler werden stufenweise geschrieben. Die erste Stufe wird Lexer genannt und verwandelt Zeichen in eine symbolische Struktur. Aus “++” wird also so etwas wie ein enum SYMBOL_PLUSPLUS. Später verwandelt die Parser-Stufe dies in einen abstrakten Syntaxbaum, aber es kann die Symbole nicht ändern. Sie können den Lexer beeinflussen, indem Sie Leerzeichen einfügen (die Symbole beenden, sofern sie nicht in Anführungszeichen stehen).

Normale Lexer sind gierig (mit einigen Ausnahmen), daher wird Ihr Code als interpretiert

a++ ++ +b

Die Eingabe für den Parser ist ein Strom von Symbolen, Ihr Code würde also etwa so aussehen:

[ SYMBOL_NAME(name = "a"), 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS_PLUS, 
  SYMBOL_PLUS, 
  SYMBOL_NAME(name = "b") 
]

Was der Parser für syntaktisch falsch hält. (BEARBEITEN basierend auf Kommentaren: Semantisch falsch, da Sie ++ nicht auf einen r-Wert anwenden können, was zu a++ führt)

a+++b 

ist

a++ +b

Was in Ordnung ist. So sind Ihre anderen Beispiele.

  • +1 Gute Erklärung. Ich muss allerdings pingelig sein: Es ist syntaktisch korrekt, es hat nur einen semantischen Fehler (Versuch, den lvalue zu erhöhen, der aus a++).

    Benutzer395760

    15. April 2011 um 13:22 Uhr

  • a++ ergibt einen rvalue.

    – Frauref

    15. April 2011 um 13:40 Uhr

  • Im Zusammenhang mit Lexern wird der “gierige” Algorithmus normalerweise als Maximal Munch bezeichnet (en.wikipedia.org/wiki/Maximal_munch).

    – JoG

    15. April 2011 um 14:18 Uhr

  • Nett. Viele Sprachen haben dank gierigem Lexing ähnliche bizarre Eckfälle. Hier ist ein wirklich seltsamer Ausdruck, bei dem es besser wird, den Ausdruck zu verlängern: In VBScript x = 10&987&&654&&321 ist illegal, aber bizarr genug x = 10&987&&654&&&321 ist legal.

    – Eric Lippert

    15. April 2011 um 18:25 Uhr

  • Es hat nichts mit Gier zu tun, sondern mit Ordnung und Vorrang. ++ ist höher als +, also werden zuerst zwei ++ ausgeführt. +++++b wird auch + ++ ++ b sein und nicht ++ ++ + b. Gutschrift an @MByD für den Link.

    Benutzer34537

    15. April 2011 um 19:38 Uhr

Benutzeravatar von Prasoon Saurav
Prasun Saurav

printf("%d",a+++++b); wird interpretiert als (a++)++ + b nach der Maximal-Munch-Regel!.

++ (postfix) wird nicht zu einem ausgewertet lvalue aber es erfordert, dass sein Operand an ist lvalue.

! 6.4/4 sagt, dass das nächste Vorverarbeitungstoken die längste Folge von Zeichen ist, die ein Vorverarbeitungstoken darstellen könnte.

Der Lexer verwendet einen sogenannten “Maximum-Munch”-Algorithmus, um Token zu erstellen. Das heißt, während es Zeichen einliest, liest es so lange Zeichen, bis es auf etwas trifft, das nicht Teil desselben Tokens sein kann wie das, was es bereits hat (z ein A, es weiß, dass das nicht Teil der Nummer sein kann. so hält es an und verlässt die A im Eingabepuffer, der als Beginn des nächsten Tokens verwendet werden soll). Anschließend gibt es dieses Token an den Parser zurück.

In diesem Fall bedeutet das +++++ wird als lexed a ++ ++ + b. Da das erste Post-Inkrement einen rvalue ergibt, kann das zweite nicht darauf angewendet werden, und der Compiler gibt einen Fehler aus.

Nur FWIW, in C++ kann man überladen operator++ um einen lvalue zu erhalten, der es ermöglicht, dass dies funktioniert. Zum Beispiel:

struct bad_code { 
    bad_code &operator++(int) { 
        return *this;
    }
    int operator+(bad_code const &other) { 
        return 1;
    }
};

int main() { 
    bad_code a, b;

    int c = a+++++b;
    return 0;
}

Das kompiliert und läuft (obwohl es nichts tut) mit den C++-Compilern, die ich zur Hand habe (VC++, g++, Comeau).

  • “zB wenn es Ziffern gelesen hat, also was es hat, ist eine Zahl, wenn es auf ein A trifft, weiß es, dass es nicht Teil der Zahl sein kann.” 16FA ist eine vollkommen feine Hexadezimalzahl Nummer der ein A enthält.

    – orlp

    15. April 2011 um 19:55 Uhr

  • @nightcracker: ja, aber ohne a 0x Am Anfang wird es noch so behandelt 16 gefolgt von FAkeine einzelne Hexadezimalzahl.

    – Jerry Sarg

    15. April 2011 um 19:58 Uhr

  • @Jerry Coffin: Das hast du nicht gesagt 0x war nicht Teil der Nummer.

    – orlp

    15. April 2011 um 19:59 Uhr

  • @nightcracker: Nein, habe ich nicht – angesichts der Tatsache, dass die meisten Leute nicht darüber nachdenken x eine Ziffer, schien es ziemlich unnötig.

    – Jerry Sarg

    15. April 2011 um 20:02 Uhr


Benutzeravatar von Shafik Yaghmour
Shafik Yaghmur

Dieses genaue Beispiel wird in behandelt Entwurf des C99-Standards(gleiche Details in C11) Sektion 6.4 Lexikalische Elemente Absatz 4 was darin sagt:

Wenn der Eingabestrom bis zu einem gegebenen Zeichen in Vorverarbeitungstoken zerlegt wurde, ist das nächste Vorverarbeitungstoken die längste Folge von Zeichen, die ein Vorverarbeitungstoken bilden könnte. […]

die auch als bekannt ist maximale munch regel Dies wird in der lexikalischen Analyse verwendet, um Mehrdeutigkeiten zu vermeiden, und funktioniert, indem so viele Elemente wie möglich verwendet werden, um ein gültiges Token zu bilden.

Der Absatz enthält auch zwei Beispiele. Das zweite entspricht genau Ihrer Frage und lautet wie folgt:

BEISPIEL 2 Das Programmfragment x+++++y wird als x ++ ++ + y geparst, was eine Einschränkung für Inkrementoperatoren verletzt, obwohl die Syntaxanalyse x ++ + ++ y einen korrekten Ausdruck ergeben könnte.

was uns sagt:

a+++++b

wird geparst als:

a ++ ++ + b

Dies verstößt gegen die Einschränkungen für das Post-Inkrement, da das Ergebnis des ersten Post-Inkrements ein R-Wert ist und das Post-Inkrement einen L-Wert erfordert. Dies wird im Abschnitt behandelt 6.5.2.4 Postfix Inkrement- und Dekrementoperatoren was sagt (Betonung von mir):

Der Operand des Postfix-Inkrement- oder Dekrement-Operators muss eine qualifizierte oder nicht qualifizierte Real- oder Zeigerart haben und soll ein modifizierbarer lvalue sein.

und

Das Ergebnis des postfix ++ Operators ist der Wert des Operanden.

Das Buch C++ Fallstricke deckt auch diesen Fall ab Gotcha #17 Maximale Munch-Probleme es ist das gleiche Problem in C++ auch und es gibt auch einige Beispiele. Es erklärt dies, wenn es um den folgenden Satz von Zeichen geht:

->*

Der lexikalische Analysator kann eines von drei Dingen tun:

  • Behandeln Sie es als drei Zeichen: -, > und *
  • Behandeln Sie es als zwei Token: -> und *
  • Behandeln Sie es als ein Token: ->*

Das maximal kauen Regel ermöglicht es, diese Mehrdeutigkeiten zu vermeiden. Der Autor weist darauf hin, dass es (Im C++-Kontext):

löst viel mehr Probleme als es verursacht, aber in zwei häufigen Situationen ist es ein Ärgernis.

Das erste Beispiel wären Templates, deren Template-Argumente ebenfalls Templates sind (die in C++11 gelöst wurde), zum Beispiel:

list<vector<string>> lovos; // error!
                  ^^

Was die schließenden spitzen Klammern als interpretiert Schichtbetreiberund daher ist ein Leerzeichen erforderlich, um Folgendes zu klären:

list< vector<string> > lovos;
                    ^

Der zweite Fall betrifft Standardargumente für Zeiger, zum Beispiel:

void process( const char *= 0 ); // error!
                         ^^

würde so interpretiert werden *= Zuweisungsoperator, besteht die Lösung in diesem Fall darin, die Parameter in der Deklaration zu benennen.

Benutzeravatar von Péter Török
Peter Török

Ihr Compiler versucht verzweifelt zu parsen a+++++bund interpretiert es als (a++)++ +b. Nun ist das Ergebnis des Post-Inkrements (a++) ist kein Wertdh es kann nicht mehr nachinkrementiert werden.

Bitte schreiben Sie niemals solchen Code in Programmen mit Produktionsqualität. Denken Sie an den armen Kerl, der nach Ihnen kommt und Ihren Code interpretieren muss.

(a++)++ +b

a++ gibt den vorherigen Wert zurück, einen rvalue. Sie können dies nicht erhöhen.

Weil es undefiniertes Verhalten verursacht.

Welches ist es?

c = (a++)++ + b
c = (a) + ++(++b)
c = (a++) + (++b)

Ja, weder Sie noch der Compiler wissen es.

BEARBEITEN:

Der wahre Grund ist der, wie von den anderen gesagt:

Es wird interpretiert als (a++)++ + b.

aber post increment erfordert einen lvalue (das ist eine Variable mit einem Namen), aber (a++) gibt einen rvalue zurück, der nicht inkrementiert werden kann, was zu der Fehlermeldung führt, die Sie erhalten.

Danke an die anderen für den Hinweis.

  • Sie könnten dasselbe für a+++b sagen – (a++) + b und a + (++b) haben unterschiedliche Ergebnisse.

    – Michael Chinen

    15. April 2011 um 13:13 Uhr

  • Tatsächlich hat Postfix ++ eine höhere Priorität als Präfix ++, also a+++b ist immer a++ + b

    – MByD

    15. April 2011 um 13:16 Uhr

  • Ich glaube nicht, dass das die richtige Antwort ist, aber ich könnte mich irren. Ich denke, der Lexer definiert es als a++ ++ +b die nicht geparst werden können.

    – Lou Franco

    15. April 2011 um 13:17 Uhr

  • Ich bin mit dieser Antwort nicht einverstanden. „undefiniertes Verhalten“ unterscheidet sich stark von der Zweideutigkeit der Tokenisierung; und ich glaube auch nicht, dass das problem ist.

    – Jim Blackler

    15. April 2011 um 13:17 Uhr


  • “Andernfalls würde a+++++b zu ((a++)++)+b ausgewertet werden” … ist meine derzeitige Ansicht a+++++b tut auswerten zu (a++)++)+b. Sicherlich mit GCC, wenn Sie diese Klammern einfügen und neu erstellen, ändert sich die Fehlermeldung nicht.

    – Jim Blackler

    15. April 2011 um 13:21 Uhr

1423410cookie-checkWarum funktioniert a++++b nicht?

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

Privacy policy