10. Die Dreifaltigkeit des C-Compilers

Datenflussdiagramm der Kompilation

Bei einem cc hello.c -o hello passiert folgendes:

C besteht gewissermaßen aus drei Sprachen:

Präprozessor: z.B. #include, #define, allgemein alles mit einem Hashmark davor. Textersetzungen, bevor der eigentliche Compiler etwas sieht.

Python hat nichts Entsprechendes, auch wenn prinzipiell nichts dagegen spricht, auch Python-Programme durch einen Präprozessor irgendeiner Art zu schicken – es ist aber weder üblich noch direkt sinnvoll, da Python viele und weitaus ausgefeiltere Möglichkeiten bietet, Probleme zu lösen, für die C den Präprozessor braucht. Wie auch immer: Der C-Präprozessor erhält nicht unbedingt whitespace und ist deshalb für Python ungeeignet.

Sprachkern: Reservierte Wörter wie int, void, return (und etwa 40 weitere), die der Compiler direkt versteht, dazu noch Operatoren (+, *, % usf.) und diverse syntaktische Symbole (Anführungszeichen, Klammern, Strichpunkt usf.).

Die reservierten Wörter von Python sind etwas unterschiedlich von denen in C – die meisten Operatoren funktionieren aber in den beiden Sprachen ganz ähnlich, viele (arithmetische) Python-Ausdrücke lassen sich direkt in ein C-Programm kopieren. Die vordefinierten Typen (int, float, list, dict) sind in Python keine reservierten Wörter (sie sind im builtin-Dictionary), in C schon.

Bibliotheksfunktionen: Im Beispiel wird nur printf benutzt. Für sie baut der Compiler nur eine Referenz in das Kompilat ein, die der Linker nachher auflöst. Zu unterscheiden sind Standardbibliothek und Zusatzbibliotheken.

Generell kommen alle Funktionen, die aufgerufen, aber in der Quelle nicht definiert werden, “von außen”. Vorläufig bedeutet das für uns “aus Bibliotheken”.

Die Bibliotheksfunktionen entsprechen etwa dem, was in Python per import geholt wird (plus den Builtin-Funktionen). Es gibt in C kein Äquivalent zu import, eine etwas ähnliche Funktionalität stellt der Linker zur Verfügung. Insbesondere gibt es in C keine Namespaces, das Dazulinken eine Bibliothek entspricht also quasi immer einem from lib import *.

Übungen zu diesem Abschnitt

Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen

(1)

Ihr könnt beim gcc sehr einfach den Präprozessor alleine laufen lassen, indem ihr die Option -E mit übergebt. Der Compiler gibt dann einfach das, was er eigentlich dem “wirklichen” Compiler übergeben würde, auf die Standardausgabe aus. Probiert also

gcc -E hello.c | more

(oder etwas ähnliches) und seht nach, was der Compiler aus eurem Code macht. Verstehen müsst ihr das nicht unbedingt

(2)

Schreibt einen naiven Präprozessor für Python. Er soll #include können und sein Ergebnis zunächst auf die Standardausgabe ausgeben. Überlegt euch, was der Unterschied zwischen so einem include und einem import ist, und was der Unterschied zu einem from x import *.


Markus Demleitner

Copyright Notice