Ein Satz ohne Sinn
Eine Hausverwaltung rechnet Heizkosten mit einem kleinen Skript ab. Die letzte Zeile lautet zeige gesamt + anteil. Die Grammatik hat nichts dagegen, jedes Token passt.
Aber anteil gab es nur in einer Schleife, die längst vorbei ist. Ein Parser prüft die Form eines Satzes, nicht seinen Sinn. Den Sinn prüft der nächste Teil des Compilers.
Am Ende dieser Lektion
Notiz
Ich kann die Fehler finden, die ein Parser nicht findet.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Semantische Analyse | die Prüfung, ob ein Programm Sinn ergibt | |
| Deklaration | die Zeile, die einen Namen einführt | int step |
| Gültigkeitsbereich | der Teil eines Programms, in dem ein Name existiert | |
| Symboltabelle | die Liste des Compilers mit den gültigen Namen | |
| Typ | welche Art Wert etwas enthält | int |
| Warnung | eine Meldung, die den Build nicht stoppt |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Der Compiler führt eine Liste der Namen
Während der Compiler den Syntaxbaum durchläuft, führt er eine Symboltabelle. Jedes { legt einen neuen Gültigkeitsbereich oben auf. Jede Deklaration trägt einen Namen in den obersten Bereich ein. Jedes } wirft den obersten Bereich weg.
Für einen Namen sucht der Compiler zuerst oben, dann im Bereich darunter, und so weiter. Ein Name, der nirgends steht, ist nicht deklariert. Die Befehle zeigen, wo step in scope.c lebt.
In den Ordner compiler wechseln
cd ~/compilerHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Jede Zeile, in der step vorkommt
grep -n "step" scope.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Beispiel: die Symboltabelle von scope.c
| Zeile | Was passiert | Gültige Namen |
|---|---|---|
| 4 und 5 | main deklariert unused und total | unused, total |
| 6 | Die Schleife öffnet einen Bereich und deklariert i | unused, total | i |
| 7 | Der Schleifenrumpf deklariert step | unused, total | i | step |
| 9 | Die Schleife endet, ihre Bereiche fallen weg | unused, total |
| 10 | printf benutzt step: nicht gefunden | unused, total |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ohne zusätzliche Prüfungen kompilieren
gcc -c scope.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Jetzt die üblichen Warnungen verlangen
gcc -Wall -c scope.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Aufrufe müssen zur Deklaration passen
Der Compiler kennt auch die Deklaration jeder Funktion. square nimmt genau ein Argument vom Typ int. Bei jedem Aufruf vergleicht er Anzahl und Typ.
Die Befehle erzeugen zwei Kopien von hello.c, das Original bleibt unberührt. zuviel.c ruft square(7, 8) auf. typfehler.c ruft square("7") auf: Ein Text in Anführungszeichen ist in C kein int, sondern ein char *.
Eine Kopie mit zwei Argumenten
sed 's/square(7)/square(7, 8)/' hello.c > zuviel.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Kopie kompilieren
gcc -Wall -c zuviel.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Eine Kopie mit falschem Typ
sed 's/square(7)/square("7")/' hello.c > typfehler.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Auch diese Kopie kompilieren
gcc -Wall -c typfehler.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Fehler stoppen den Build, Warnungen nicht
Die nächste Kopie, format.c, verlangt von printf zwei Zahlen und gibt ihm nur eine. gcc weiß, wie printf sein Format liest, und kann deshalb zählen.
Diesmal kommt nur eine Warnung. Der Build läuft weiter, und ls zeigt, dass format.o geschrieben wurde. Nach einem Fehler entsteht dagegen gar keine Objektdatei.
Eine Kopie, der eine Zahl fehlt
sed 's/is %d/is %d %d/' hello.c > format.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Diese Kopie kompilieren
gcc -Wall -c format.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Wurde eine Objektdatei geschrieben?
ls format.oHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ein häufiger Fehler
Warnung
Viele glauben, eine Warnung lässt sich übergehen, weil das Programm trotzdem gebaut wird. Eine Warnung heißt: Das ist erlaubt, aber sehr wahrscheinlich falsch. format.c lässt sich bauen und gibt dann eine Zahl aus, die niemand übergeben hat. Lesen Sie jede Warnung und behandeln Sie sie als Fehler, bis Sie wissen, warum sie harmlos ist.
Python prüft erst während des Laufs
C prüft Namen und Typen, bevor das Programm läuft. Das heißt statische Prüfung. Python prüft sie erst, wenn die Zeile ausgeführt wird. Der Fehler erscheint also genau in diesem Moment.
Beide Befehle unten starten das Programm. Keiner der Fehler fällt auf, bevor Python die Stelle erreicht.
Ein Name, den Python nie gesehen hat
python3 -c "print(summe)"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Länge einer Zahl
python3 -c "print(len(5))"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Weiterführend
Ergänzen Sie Ihre Prüfung um eine zweite Liste, unbenutzt. Sie meldet jeden Namen, der deklariert und nie benutzt wurde, mit der Zeile seiner Deklaration.
Prüfen Sie sie an PROGRAMM mit einer zusätzlichen ersten Zeile sei rest = 1. Erklären Sie dann, warum gcc unused nur mit -Wall meldet, step aber immer.