Die Kompilierung eines C++-Programms umfasst drei Schritte:
-
Vorverarbeitung: Der Präprozessor nimmt eine C++-Quellcodedatei und bearbeitet die #include
s, #define
s und andere Präprozessoranweisungen. Die Ausgabe dieses Schrittes ist eine "reine" C++-Datei ohne Präprozessoranweisungen.
-
Kompilierung: Der Compiler nimmt die Ausgabe des Präprozessors und erstellt daraus eine Objektdatei.
-
Linken: Der Linker nimmt die vom Compiler erzeugten Objektdateien und erzeugt entweder eine Bibliothek oder eine ausführbare Datei.
Vorverarbeitung
Der Präprozessor behandelt die Präprozessor-Direktiven , wie #include
y #define
. Es ist unabhängig von der Syntax von C++, weshalb es mit Vorsicht verwendet werden muss.
Er arbeitet mit jeweils einer C++-Quelldatei, indem er die #include
Direktiven mit dem Inhalt der jeweiligen Dateien (der in der Regel nur aus Deklarationen besteht), die Ersetzung von Makros ( #define
), und die Auswahl verschiedener Textabschnitte in Abhängigkeit von #if
, #ifdef
y #ifndef
Richtlinien.
Der Präprozessor arbeitet mit einem Strom von Vorverarbeitungs-Token. Makrosubstitution ist definiert als das Ersetzen von Token durch andere Token (der Operator ##
ermöglicht das Zusammenführen zweier Token, wenn dies sinnvoll ist).
Nach all dem erzeugt der Präprozessor eine einzige Ausgabe, die ein Strom von Token ist, die aus den oben beschriebenen Transformationen resultieren. Er fügt auch einige spezielle Markierungen hinzu, die dem Compiler mitteilen, woher jede Zeile stammt, so dass er diese verwenden kann, um sinnvolle Fehlermeldungen zu erzeugen.
Einige Fehler können in diesem Stadium durch geschickte Verwendung der #if
y #error
Richtlinien.
Zusammenstellung
Der Kompilierungsschritt wird für jede Ausgabe des Präprozessors durchgeführt. Der Compiler analysiert den reinen C++-Quellcode (jetzt ohne Präprozessoranweisungen) und wandelt ihn in Assemblercode um. Dann ruft er das zugrundeliegende Back-End auf (Assembler in der Toolchain), das diesen Code in Maschinencode umwandelt und eine Binärdatei in einem bestimmten Format (ELF, COFF, a.out, ...) erzeugt. Diese Objektdatei enthält den kompilierten Code (in binärer Form) für die in der Eingabe definierten Symbole. Auf Symbole in Objektdateien wird mit Namen verwiesen.
Objektdateien können auf Symbole verweisen, die nicht definiert sind. Dies ist der Fall, wenn Sie eine Deklaration verwenden und keine Definition dafür angeben. Den Compiler stört dies nicht und er erzeugt die Objektdatei gerne, solange der Quellcode wohlgeformt ist.
Compiler lassen Sie die Kompilierung in der Regel an dieser Stelle abbrechen. Dies ist sehr nützlich, weil Sie damit jede Quellcodedatei einzeln kompilieren können. Dies hat den Vorteil, dass Sie nicht neu kompilieren müssen alles wenn Sie nur eine einzige Datei ändern.
Die erzeugten Objektdateien können in speziellen Archiven, so genannten statischen Bibliotheken, abgelegt werden, um sie später leichter wiederverwenden zu können.
In diesem Stadium werden "normale" Compilerfehler wie Syntaxfehler oder Fehler bei der Auflösung von Überladungen gemeldet.
Verlinkung
Der Linker erzeugt die endgültige Kompilierungsausgabe aus den vom Compiler erzeugten Objektdateien. Diese Ausgabe kann entweder eine gemeinsam genutzte (oder dynamische) Bibliothek sein (und obwohl der Name ähnlich ist, haben sie nicht viel mit den zuvor erwähnten statischen Bibliotheken gemeinsam) oder eine ausführbare Datei.
Es verknüpft alle Objektdateien, indem es die Verweise auf undefinierte Symbole durch die richtigen Adressen ersetzt. Jedes dieser Symbole kann in anderen Objektdateien oder in Bibliotheken definiert sein. Wenn sie in anderen Bibliotheken als der Standardbibliothek definiert sind, müssen Sie sie dem Linker mitteilen.
In diesem Stadium sind die häufigsten Fehler fehlende oder doppelte Definitionen. Ersteres bedeutet, dass die Definitionen entweder nicht existieren (d.h. nicht geschrieben sind) oder dass die Objektdateien oder Bibliotheken, in denen sie sich befinden, dem Linker nicht übergeben wurden. Letzteres ist offensichtlich: Das gleiche Symbol wurde in zwei verschiedenen Objektdateien oder Bibliotheken definiert.