Warum meldet der Compiler kein fehlendes Semikolon?

Lesezeit: 8 Minuten

Der Benutzer-Avatar eines Programmierer-Typen
Irgendein Programmierer

Ich habe dieses einfache Programm:

#include <stdio.h>

struct S
{
    int i;
};

void swap(struct S *a, struct S *b)
{
    struct S temp;
    temp = *a    /* Oops, missing a semicolon here... */
    *a = *b;
    *b = temp;
}

int main(void)
{
    struct S a = { 1 };
    struct S b = { 2 };

    swap(&a, &b);
}

Wie gesehen auf zB ideone.com das gibt einen Fehler:

prog.c: In function 'swap':
prog.c:12:5: error: invalid operands to binary * (have 'struct S' and 'struct S *')
     *a = *b;
     ^

Warum erkennt der Compiler das fehlende Semikolon nicht?


Hinweis: Diese Frage und ihre Antwort sind durch diese Frage motiviert. Während es andere ähnliche Fragen gibt, habe ich nichts gefunden, was die Freiformkapazität der C-Sprache erwähnt, die diese und verwandte Fehler verursacht.

  • Was hat diesen Beitrag motiviert?

    – R Sahu

    19. Oktober 2016 um 15:17 Uhr

  • @TavianBarnes Auffindbarkeit. Die andere Frage ist bei der Suche nach dieser Art von Problem nicht auffindbar. Es könnte auf diese Weise bearbeitet werden, aber das würde ein wenig zu viel Änderung erfordern, was es meiner Meinung nach zu einer ganz anderen Frage macht.

    – Irgendein Programmierer-Typ

    19. Oktober 2016 um 17:36 Uhr


  • @TavianBarnes: Die ursprüngliche Frage war die Frage nach dem Fehler. Diese Frage fragt, warum der Compiler (zumindest für das OP) den Ort des Fehlers falsch zu melden scheint.

    – TonyK

    19. Oktober 2016 um 18:29 Uhr

  • Zeigen Sie zum Nachdenken: Wenn ein Compiler systematisch fehlende Semikolons erkennen könnte, wäre die Sprache bräuchte zunächst keine Semikolons.

    – Euro Mizellen

    20. Oktober 2016 um 2:15 Uhr


  • Die Aufgabe des Compilers besteht darin, den Fehler zu melden. Es ist Ihre Aufgabe, herauszufinden, was geändert werden muss, um den Fehler zu beheben.

    – David Schwartz

    20. Oktober 2016 um 10:55 Uhr

C ist ein Freiform Sprache. Das heißt, Sie können es auf viele Arten formatieren und es wird immer noch ein legales Programm sein.

Zum Beispiel eine Aussage wie

a = b * c;

könnte geschrieben werden wie

a=b*c;

oder ähnliches

a
=
b
*
c
;

Also, wenn der Compiler die Zeilen sieht

temp = *a
*a = *b;

es denkt, es bedeutet

temp = *a * a = *b;

Das ist natürlich kein gültiger Ausdruck und der Compiler wird das statt des fehlenden Semikolons bemängeln. Der Grund, warum es nicht gültig ist, ist, weil a ist ein Zeiger auf eine Struktur, also *a * a versucht eine Strukturinstanz zu multiplizieren (*a) mit einem Zeiger auf eine Struktur (a).

Während der Compiler das fehlende Semikolon nicht erkennen kann, meldet er auch den völlig unabhängigen Fehler in der falschen Zeile. Dies ist wichtig zu beachten, denn egal, wie oft Sie sich die Zeile ansehen, in der der Fehler gemeldet wird, es gibt dort keinen Fehler. Manchmal müssen Sie sich mit solchen Problemen befassen früher Linien, um zu sehen, ob sie in Ordnung und fehlerfrei sind.

Manchmal müssen Sie sogar in einer anderen Datei suchen, um den Fehler zu finden. Wenn zum Beispiel eine Header-Datei eine Struktur als letzte in der Header-Datei definiert und das Semikolon am Ende der Struktur fehlt, dann wird der Fehler nicht in der Header-Datei, sondern in der Datei sein, die die Header-Datei enthält.

Und manchmal wird es noch schlimmer: Wenn Sie zwei (oder mehr) Header-Dateien einbinden und die erste eine unvollständige Deklaration enthält, wird höchstwahrscheinlich der Syntaxfehler in der zweiten Header-Datei angezeigt.


Damit verbunden ist der Begriff der nachverfolgen Fehler. Einige Fehler, die normalerweise auf fehlende Semikolons zurückzuführen sind, werden als gemeldet mehrere Fehler. Aus diesem Grund ist es wichtig, beim Beheben von Fehlern von oben zu beginnen, da das Beheben des ersten Fehlers dazu führen kann, dass mehrere Fehler verschwinden.

Dies kann natürlich dazu führen, dass jeweils ein Fehler behoben und häufig neu kompiliert wird, was bei großen Projekten umständlich sein kann. Das Erkennen solcher Folgefehler ist jedoch etwas, das mit der Erfahrung einhergeht, und nachdem man sie ein paar Mal gesehen hat, ist es einfacher, die wahren Fehler auszugraben und mehr als einen Fehler pro Neukompilierung zu beheben.

  • In C++, temp = *a * a = *b könnte ein gültiger Ausdruck sein, wenn operator* waren überlastet. (Die Frage ist jedoch mit „C“ gekennzeichnet.)

    – dan04

    19. Oktober 2016 um 21:42 Uhr

  • @dan04: Wenn das wirklich jemand getan hat… NEIN!

    – Kevin

    19. Oktober 2016 um 22:52 Uhr

  • +1 für den Hinweis zu (a) beginnend mit dem ersten gemeldeten Fehler; und (b) rückwärts schauen von wo der Fehler gemeldet wird. Du weißt, dass du ein bist real Programmierer, wenn Sie automatisch auf die Zeile davor schauen, wo ein Fehler gemeldet wird 🙂

    – TripeHound

    20. Oktober 2016 um 11:51 Uhr

  • @TripeHound BESONDERS, wenn es eine sehr große Anzahl von Fehlern gibt oder Zeilen, die zuvor kompiliert wurden, Fehler auslösen …

    – Zinn-Zauberer

    20. Oktober 2016 um 23:18 Uhr

  • Wie es bei Meta üblich ist, hat bereits jemand gefragt – meta.stackoverflow.com/questions/266663/…

    – StoryTeller – Unslander Monica

    3. September 2018 um 9:08 Uhr

Benutzeravatar von Plugwash
Plugwash

Warum erkennt der Compiler das fehlende Semikolon nicht?

Es gibt drei Dinge, an die man sich erinnern sollte.

  1. Zeilenenden in C sind nur gewöhnliche Leerzeichen.
  2. * in C kann sowohl ein unärer als auch ein binärer Operator sein. Als unärer Operator bedeutet es “Dereferenzieren”, als binärer Operator bedeutet es “Multiplizieren”.
  3. Der Unterschied zwischen unären und binären Operatoren wird durch den Kontext bestimmt, in dem sie gesehen werden.

Das Ergebnis dieser beiden Tatsachen ist, wenn wir parsen.

 temp = *a    /* Oops, missing a semicolon here... */
 *a = *b;

Der erste und der letzte * werden als unär interpretiert, aber die zweite * wird als binär interpretiert. Aus syntaktischer Sicht sieht das OK aus.

Erst nach dem Parsen, wenn der Compiler versucht, die Operatoren im Kontext ihrer Operandentypen zu interpretieren, wird ein Fehler angezeigt.

Einige gute Antworten oben, aber ich werde näher darauf eingehen.

temp = *a *a = *b;

Dies ist tatsächlich ein Fall von x = y = z; wo beides x und y wird der Wert von zugewiesen z.

Was Sie sagen, ist the contents of address (a times a) become equal to the contents of b, as does temp.

Zusamenfassend, *a *a = <any integer value> ist eine gültige Aussage. Wie bereits erwähnt, die erste * dereferenziert einen Zeiger, während die zweite zwei Werte multipliziert.

  • Die Dereferenzierung hat Vorrang, also (Inhalt von Adresse a) mal (Zeiger auf a). Sie können es erkennen, weil der Kompilierungsfehler “ungültige Operanden für Binär * (have ‘struct S’ and ‘struct S *’)” sagt, die diese beiden Typen sind.

    – dascandy

    20. Oktober 2016 um 8:56 Uhr


  • Ich codiere vor C99, also keine Bools 🙂 Aber Sie machen einen guten Punkt (+1), obwohl die Reihenfolge der Zuweisung nicht wirklich der Punkt meiner Antwort war

    – Mawg sagt, Monica wieder einzusetzen

    21. Oktober 2016 um 8:51 Uhr

  • Aber in diesem Fall y ist nicht einmal eine Variable, sondern der Ausdruck *a *aund Sie können das Ergebnis einer Multiplikation nicht zuweisen.

    – Barmar

    25. Oktober 2016 um 18:42 Uhr

  • @Barmar in der Tat, aber der Compiler kommt nicht so weit, er hat bereits entschieden, dass die Operanden für “binary *” ungültig sind, bevor er sich den Zuweisungsoperator ansieht.

    – Plugwash

    1. September 2017 um 12:32 Uhr

Die meisten Compiler analysieren die Quelldateien der Reihe nach und melden die Zeile, in der sie feststellen, dass etwas nicht stimmt. Die ersten 12 Zeilen Ihres C-Programms könnten der Beginn eines gültigen (fehlerfreien) C-Programms sein. Die ersten 13 Zeilen Ihres Programms können dies nicht. Einige Compiler notieren die Position von Dingen, auf die sie stoßen, die an und für sich keine Fehler sind, und lösen in den meisten Fällen später im Code keine Fehler aus, sind aber in Kombination mit etwas anderem möglicherweise nicht gültig. Zum Beispiel:

int foo;
...
float foo;

Die Erklärung int foo; an sich wäre vollkommen in Ordnung. Ebenso die Deklaration float foo;. Einige Compiler können die Zeilennummer aufzeichnen, in der die erste Deklaration erschien, und dieser Zeile eine Informationsnachricht zuordnen, um dem Programmierer zu helfen, Fälle zu identifizieren, in denen die frühere Definition tatsächlich die fehlerhafte ist. Compiler behalten möglicherweise auch die Zeilennummern bei, die mit etwas wie a verknüpft sind dodie gemeldet werden können, wenn die zugehörigen while erscheint nicht an der richtigen Stelle. In Fällen, in denen der wahrscheinliche Ort des Problems unmittelbar vor der Zeile liegt, in der der Fehler entdeckt wird, machen sich Compiler jedoch im Allgemeinen nicht die Mühe, einen zusätzlichen Bericht für die Position hinzuzufügen.

Kuba hat Monicas Benutzeravatar nicht vergessen
Kuba hat Monica nicht vergessen

Es gibt einen polnischen Film mit dem Titel „Nic Śmiesznego“ („Nichts Lustiges“). Hier ist ein Auszug aus relevanten Dialogen aus einer Szene das zeigt genau warum Die Compiler-Entwickler sind vielleicht etwas schüchtern, solche fehlenden Semikolons mit rücksichtsloser Hingabe zu proklamieren.

Direktor: Was meinst du mit “diesem”?! Wollen Sie damit sagen, dass sich dieses Objekt in meinem Sichtfeld befindet? Zeige mit deinem Finger darauf, denn ich möchte glauben, dass ich träume.

Adam: Das hier (Punkte).

Direktor: Dies? Was ist das?!

Adam: Wie meinst du das? Es ist ein Wald.

Direktor: Können Sie mir sagen, warum zum Teufel ich einen Wald brauche?

Adam: Wie kommt es zur “verdammten Hölle”? Hier, im Drehbuch, steht ein Wald, da steht…

Direktor: Im Drehbuch? Finden Sie es in diesem Drehbuch für mich.

Adam: Hier: (liest) “Als sie auf den Kamm der Straße kamen, erschien vor ihnen ein Wald”

Direktor: Blättere die Seite um.

Adam: Oh Mist…

Direktor: Lies es für mich.

Adam: vor ihnen erschien ein Wald… von Grabsteinen.

Sehen Sie, es ist im Allgemeinen nicht möglich, im Voraus zu sagen, dass Sie wirklich einen Wald und nicht einen Wald von Grabsteinen gemeint haben.

1422230cookie-checkWarum meldet der Compiler kein fehlendes Semikolon?

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

Privacy policy