Semikolon nach Funktion (Klausurfrage-Fehler finden im Code)



  • rüdiger schrieb:

    Nein, es ist nicht erlaubt. Zumindest sagt der GCC: "error: ISO C does not allow extra ‘;’ outside of a function" (mit -std=c99 -Wall -W -pedantic-errors ).

    Ich weiß nicht, ich hab's mit VS2003 und einem aktuellen LCC probiert, -pedantic-errors kennen die nicht, (über C99 müssen wir eh keine Worte verlieren), aber -Wall ergibt gar nichts. Bei der normativen Kraft des Faktischen haben wir jetzt 2:1, was sagt denn der Standard dazu? Der GCC ist ja wohl eher Sekundärquelle.

    Der OP möchte ja seinem Prof eine hinreiben, aber eine Compilerparade ist kein echtes Argument. Und irgenwie finde ich keinen zitierfähigen Beleg, wär' vielleicht OK, wenn sich unsere Standard- Reiter in den Sattel schmeißen.





  • pointercrash() schrieb:

    rüdiger schrieb:

    Nein, es ist nicht erlaubt. Zumindest sagt der GCC: "error: ISO C does not allow extra ‘;’ outside of a function" (mit -std=c99 -Wall -W -pedantic-errors ).

    Ich weiß nicht, ich hab's mit VS2003 und einem aktuellen LCC probiert, -pedantic-errors kennen die nicht, (über C99 müssen wir eh keine Worte verlieren), aber -Wall ergibt gar nichts. Bei der normativen Kraft des Faktischen haben wir jetzt 2:1, was sagt denn der Standard dazu? Der GCC ist ja wohl eher Sekundärquelle.

    Ne ne ne, die Argumentation geht so nicht. Wenn ein Compiler es akzeptiert, dann ist es keine Aussage darüber, ob es der Standard erlaubt. Compiler schlucken allen möglichen misst. Gerade der MSVC. Der GCC akzeptiert es ja auch, wenn man das -pedantic-errors weglässt. Wenn ein Compiler es dagegen im strengen Standardmodus mit explizitem Hinweis auf den Standard ablehnt, dann ist das ein sehr starkes Indiz dafür, dass es wohl im Standard so festgelegt ist. Natürlich müsste man es im Standard genau nachlesen. Also wenn du Lust dazu hast, dann bitte ... Ich vertraue einfach mal den GCC-Leuten.


  • Mod

    pointercrash() schrieb:

    vielleicht OK, wenn sich unsere Standard- Reiter in den Sattel schmeißen.

    Ich höre den Ruf und antworte 😃

    Das Semikolon kommt im C Standard genau an diesen Stellen vor:

    • Am Ende einer Deklaration.
    • Innerhalb von structs und unions zum Abschluss einer "struct-declaration" (ich mag das gerade nicht übersetzen, ihr wisst ohnehin, was gemeint ist).
    • Zum Abschluss eines statements, wobei dies auch leer sein darf.
    • Nach do ... while
    • Innerhalb von for
    • Nach goto, continue, break und return.

    Die letzten drei Ausdrücke sind natürlich auch allesamt statements, aber sie werden im Standard nochmal explizit genannt, deswegen tue ich dies auch. Auffällig ist, dass nach while und for selber kein Semikolon verlangt wird. Dies liegt wohl daran, dass while und for selber wieder auf einem statement enden, wodurch man dann ein doppeltes Semikolon hätte.

    Nach einer Funktion wird kein Semikolon verlangt. Ein einzelnes Semikolon im Filescope (welches nicht auf eine Deklaration folgt), kann somit nur ein leeres Statement sein. Jetzt ist dann natürlich die Frage, ob Statements auch im Filescope stehen dürfen. Der Standard erwähnt Statements nur auf Block-Scope. Und Blocks dürfen nur auf eine Funktionsdefinition folgen und dann habe ich aufgehört mich weiter zu hangeln. Es geht jedenfalls hervor, dass ein Statement im Filescope nicht direkt erlaubt ist. Andererseits wird ein Statement dort auch nicht explizit verboten, soweit ich das sehe. Und ein leeres Statement macht ja ohnehin nichts. Also wenn man pedantisch ist, ist es ein Fehler, weil nicht explizit erlaubt. Aber es ist den Compilern auch erlaubt, dies als Feature anzubieten und wie wir sehen, tun dies auch viele (was ja auch sehr sinnvoll ist, wie ich finde).

    Aber ja: Es ist ganz streng genommen ein Fehler und braucht auf einem Compiler, der sonst 100% standardkonform ist, nicht zu kompilieren.



  • rüdiger schrieb:

    Ne ne ne, die Argumentation geht so nicht. Wenn ein Compiler es akzeptiert, dann ist es keine Aussage darüber, ob es der Standard erlaubt.

    Das war ja mein Ausgangspunkt.

    rüdiger schrieb:

    Wenn ein Compiler es dagegen im strengen Standardmodus mit explizitem Hinweis auf den Standard ablehnt, dann ist das ein sehr starkes Indiz ... Ich vertraue einfach mal den GCC-Leuten.

    Wie es schon in der Werbung heißt, "Die Geschichte des Semikolons ist eine Geschichte voller Mißverständnisse", 😉 und was die GCC- Leute tun, ist auch nicht durchgängig frei von Kritik.
    Ich hätte mich auch gerne in das Grauen verklausulierten Englischs gestürzt, aber wo find' ich den Standard im Internet?
    Nun hat sich SeppJ dankenswerter Weise den Ritt schon angetan:

    SeppJ schrieb:

    Es geht jedenfalls hervor, dass ein Statement im Filescope nicht direkt erlaubt ist. Andererseits wird ein Statement dort auch nicht explizit verboten, soweit ich das sehe.

    Das wundert mich ein wenig, daß es nicht schärfer formuliert ist, aber zumindest andere Statements außer einer Leeranweisung machen außerhalb von Blocks ja keinen Sinn. Daß "pedantic" auch Leeranweisungen bemängelt, naja, wär' eher was für'n switch -paranoid. 🙂

    SeppJ schrieb:

    Aber es ist den Compilern auch erlaubt, dies als Feature anzubieten ...

    Vermute mal, das ist eher Faulheit als Feature. Wenn Du einen Parser baust, wartet der nur auf die "scharfen" Zeichen und überliest alles, was ihn gerade nicht interessiert (also Whitespace- Eliminierung). Um eine Leeranweisung für "pedantic" da rauszufischen braucht es extra- Fleiß.

    Gut, wär' die Frage also geklärt 😃 ... leider zu Ungunsten des OP. 😞



  • SeppJ schrieb:

    Ein einzelnes Semikolon im Filescope (welches nicht auf eine Deklaration folgt), kann somit nur ein leeres Statement sein.[schnipp]

    Es könnte auch eine Deklaration sein. In C89 und C++ wäre es sogar syntaktisch eine, da alle Teile einer Deklaration optional sind (z.B. haben main(){...} oder ~Foo(); jeweils keinen Typ, während struct Bla { ... }; keinen Deklarator hat). Es gibt dann Regeln, die besagen, dass die Deklaration im Ganzen nicht leer sein darf. In C99 ist soweit ich das sehe der Typ immer zwingend.



  • pointercrash() schrieb:

    Um eine Leeranweisung für "pedantic" da rauszufischen braucht es extra- Fleiß.

    Nicht wirklich. Wenn die Grammatik so gebaut ist, dass das leere Semikolon auf eine Deklaration matcht (siehe Vorposting), dann wirst du als nächstes diese Deklaration verarbeiten. Dabei merkst du es auf jeden Fall, wenn alle Teile leer sind. Den "Fleiß" zum Formulieren der Fehlermeldung kann man wohl gerade noch aufbringen 😉



  • Ich habe den Standard mal geritten (bzw. den Committee Draft für TC3). Die relevanten Teile der Grammatik sind:

    ISO/IEC 9899:1999 6.8.2 und 6.9.1 schrieb:

    compound-statement:
          { block-item-list[t]opt[/t] }
    
    (...)
    
    function-definition:
           declaration-specifiers declarator declaration-list[t]opt[/t] compound-statement
    declaration-list:
           declaration
           declaration-list declaration
    

    ...also: kein Semikolon.



  • Bashar schrieb:

    pointercrash() schrieb:

    Um eine Leeranweisung für "pedantic" da rauszufischen braucht es extra- Fleiß.

    Nicht wirklich. Wenn die Grammatik so gebaut ist, dass das leere Semikolon auf eine Deklaration matcht (siehe Vorposting), dann wirst du als nächstes diese Deklaration verarbeiten. Dabei merkst du es auf jeden Fall, wenn alle Teile leer sind. Den "Fleiß" zum Formulieren der Fehlermeldung kann man wohl gerade noch aufbringen 😉

    Bin ich nicht ganz Deiner Meinung, wenn ich auf die "scharfen" Bestandteile einer (möglichen) Deklaration warte, ist das üblicherweise kein Semikolon und damit der Parser in einem Zustand, in dem er das Semikolon genausogut überlesen kann. Erst wenn anderer Kram eingelesen wurde, muß er sich beim Semikolon Gedanken machen, ob das stimmen kann. Ist aber ziemlich egal, ob ein Funktionstyp gefordert wird oder nicht, nach einer abgeschlossenen Funktion darf er es einfach überlesen oder auch nicht, scharf wird das Semikolon erst, wenn das "~" oder das "m" (Deine foo und main- Bsp's.) auf dem Tisch landen. Im Filescope untergebracht macht das keinen Unterschied.

    Wer will ein Semikolon auswerten, wenn es nichts auszuwerten gibt? Klar kann man das so bauen, aber wozu? Natürlich ist mir klar, daß es keinen echten Aufwand bedeutet, da auf pedantic zu prüfen und 'ne warning rauszuhauen. Ist halt nur eine Sinnlos- Prüfung, die Rechenzeit kostet. Ich kenne nur leider keinen Compilerbauer, der mir diese lebenswichtige Frage beantwortet, wie sowas wirklich gebaut wird. 😉



  • Ich weiß ja nicht, was du für Erfahrungen mit Compilerbau hast, aber normalerweise warte ich nicht auf "scharfe Bestandteile", sondern nehme die Reduktionen so, wie sie kommen. Die Produktion für "declaration" würde also genau dann reduziert werden, wenn das Semikolon gerade gesehen wird.
    [Übrigens nur in C++. In C89 ist das mit der main() anders gelöst, als ich dachte, dort kann man also aus "declaration" nicht ";" ableiten.]



  • Bashar schrieb:

    Ich weiß ja nicht, was du für Erfahrungen mit Compilerbau hast,

    Hab' schon jede Menge Parser und ein paar Interpreter gebaut, aber Null Ahnung, wie man "echte" Compiler designed.

    Bashar schrieb:

    ... sondern nehme die Reduktionen so, wie sie kommen. Die Produktion für "declaration" würde also genau dann reduziert werden, wenn das Semikolon gerade gesehen wird.
    [Übrigens nur in C++. In C89 ist das mit der main() anders gelöst, als ich dachte, dort kann man also aus "declaration" nicht ";" ableiten.]

    Das kapier' ich momentan nicht, hast Du mir einen Link, wo ich ein bißchen Lesen kann und dann ggf. nachfragen?



  • pointercrash() schrieb:

    Ich hätte mich auch gerne in das Grauen verklausulierten Englischs gestürzt, aber wo find' ich den Standard im Internet?

    TC3-Draft:
    www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

    pointercrash() schrieb:

    In C89 und C++ wäre es sogar syntaktisch eine, da alle Teile einer Deklaration optional sind

    Dort ist der leere String auch eine Deklaration?
    🙂
    Edit:
    Und noch ein Blödheitspunkt.



  • mngbd schrieb:

    TC3-Draft: www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf

    Danke, in Bibliothek verewigt!

    mngbd schrieb:

    pointercrash() schrieb:

    In C89 und C++ wäre es sogar syntaktisch eine, da alle Teile einer Deklaration optional sind

    Ne, das war ich nicht, sondern Bashar.



  • pointercrash() schrieb:

    Bashar schrieb:

    ... sondern nehme die Reduktionen so, wie sie kommen. Die Produktion für "declaration" würde also genau dann reduziert werden, wenn das Semikolon gerade gesehen wird.
    [Übrigens nur in C++. In C89 ist das mit der main() anders gelöst, als ich dachte, dort kann man also aus "declaration" nicht ";" ableiten.]

    Das kapier' ich momentan nicht, hast Du mir einen Link, wo ich ein bißchen Lesen kann und dann ggf. nachfragen?

    Uff, mit Links wird das schwierig, wie wärs mit Büchern? Das Drachenbuch ist sehr beliebt ... aber Spaß beiseite. In der C++-Grammatik steht ungefähr sowas drin:

    simple-declaration: decl-specifier-seq_opt init-declarator-list_opt ";"
    

    In Yacc umgesetzt sieht das etwa so aus:

    simple_declaration: 
      decl_specifier_seq_opt init_declarator_list_opt SEMICOLON {
      $$ = handle_simple_declaration($1, $2);
    }
    ;
    
    decl_specifier_seq_opt:
      { $$ = 0; /* empty */ }
    | decl_specifier_seq { $$ = $1; }
    ;
    
    init_declarator_list_opt:
      { $$ = 0; /* empty */ }
    | init_declarator_list { $$ = $1; }
    ;
    

    (Könnte sein, dass das einen Konflikt enthält, dass man das also noch ein bisschen umbauen muss, aber das ist für die Diskussion glaub ich irrelevant.)

    In dem Moment, in dem der Parser in diesem syntaktischen Zusammenhang das Semikolon gelesen hat, führt er den Code $$ = handle_simple_declaration($1, $2); aus. Wenn die vorherigen Produktionen den leer-Zweig durchlaufen haben, ist das eben so, die semantischen Werte der Werte der Nichtterminale decl_specifier_seq_opt und init_declarator_list_opt sind dann halt 0. Die Funktion handle_simple_declaration muss darauf vorbereitet sein. Falls beide gleichzeitig 0 sind, kann man entweder keine Deklaration erzeugen oder eine Fehlermeldung ausgeben.

    In C ist das, was hier decl_specifier_seq heißt, nicht optional. Es muss eine Speicherklasse oder einen Typ beinhalten. Es wäre aber trotzdem denkbar, dass man das ähnlich wie ich oben löst, und beim Bauen der Deklaration nachguckt, ob man mindestens eine Speicherklasse oder einen Typ dabeihat.



  • Bashar schrieb:

    In dem Moment, in dem der Parser in diesem syntaktischen Zusammenhang das Semikolon gelesen hat, führt er den Code $$ = handle_simple_declaration($1, $2); aus. Wenn die vorherigen Produktionen den leer-Zweig durchlaufen haben, ist das eben so, die semantischen Werte der Werte der Nichtterminale decl_specifier_seq_opt und init_declarator_list_opt sind dann halt 0. Die Funktion handle_simple_declaration muss darauf vorbereitet sein. Falls beide gleichzeitig 0 sind, kann man entweder keine Deklaration erzeugen oder eine Fehlermeldung ausgeben.

    In C ist das, was hier decl_specifier_seq heißt, nicht optional. Es muss eine Speicherklasse oder einen Typ beinhalten. Es wäre aber trotzdem denkbar, dass man das ähnlich wie ich oben löst, und beim Bauen der Deklaration nachguckt, ob man mindestens eine Speicherklasse oder einen Typ dabeihat.

    Darf ich das als Dummie mal so subsummieren: Der Parser pfeift sich alles rein, bis er auf das Semikolon trifft und erst dann guckt er nach, ob er aus dem Kram davor eine declaration basteln kann?



  • Nicht ganz. Er baut nicht aus Müll + Semikolon auf einmal eine Deklaration, sondern er baut zwischendurch immer wieder kleine Teil-Syntaxbäume für die ganzen Bestandteile einer Deklaration und packt das auf den Stack. Wenn er jetzt beim Antreffen des Semikolons im richtigen Zustand ist (eine Deklaration kann ja z.B. nicht innerhalb eines Ausdrucks stehen, d.h. ein Semikolon in einem Ausdruck bedeutet auch etwas anderes) und auf dem Stack ein Teilbaum für init-declarator-list_opt und declaration-specifier_seq_opt liegen (was eigentlich auch Teil des Parserzustandes ist), dann baut er eine Deklaration (und führt meine semantische Aktion aus, d.h. ruft handle_simple_declaration auf).

    Das ist jetzt das Prinzip des Bottom-Up-Parsers gewesen, wie es z.B. yacc umsetzt. Top-Down-Parser funktionieren ein bisschen anders.



  • Bashar schrieb:

    Nicht ganz. Er ... baut zwischendurch immer wieder kleine Teil-Syntaxbäume für die ganzen Bestandteile einer Deklaration und packt das auf den Stack. Wenn er jetzt beim Antreffen des Semikolons im richtigen Zustand ist (eine Deklaration kann ja z.B. nicht innerhalb eines Ausdrucks stehen, d.h. ein Semikolon in einem Ausdruck bedeutet auch etwas anderes) und auf dem Stack ein Teilbaum für init-declarator-list_opt und declaration-specifier_seq_opt liegen (was eigentlich auch Teil des Parserzustandes ist), dann baut er eine Deklaration (und führt meine semantische Aktion aus, d.h. ruft handle_simple_declaration auf).

    OK, Danke! Den Rest hab' ich mir bei LEXandYACC zusammengelesen. Wow, ziemlich aufwendig - Du befasst Dich wohl nicht nur zum Spaß damit, was? 😉
    Glücklicherweise mußte ich noch nie auf solche Tools zurückgreifen, meine Problemstellungen waren dafür einfach zu schlicht.



  • pointercrash() schrieb:

    Du befasst Dich wohl nicht nur zum Spaß damit, was? 😉

    Der Spaß war zuerst, dann kam das "nicht nur zum Spaß" dazu 🙂


Anmelden zum Antworten