CompilerDie Anweisungen, die man in den Sprachen C oder C++ schreibt, können von dem Prozessor eines Computers nicht direkt verstanden werden. Bevor die Anweisungen ausgeführt werden können, muss der gesamte Code (der sogenannte DetailsEin Prozessor versteht nur eine Sprache, die sogenannte Maschinensprache, welche für jeden Prozessor unterschiedlich sein kann. Die Programmiersprache Assembler liefert für jede Maschinensprache (welche grundsätzlich für den Menschen unverständlich ist) eine mehr oder weniger lesbare Umsetzung der einzelnen Befehle, doch für jeden Prozessortyp existiert somit ein eigener Assembler. Da ein Programmierer nicht für jeden neuen Prozessortyp sein Programm umschreiben möchte, wurden sogenannte Hochsprachen entwickelt, welche über allen Assemblern stehen und den Programmcode auf verschiedene Art und Weise automatisch in den gewünschten Assembler und schlussendlich in die Maschinensprache umwandeln. Man unterscheidet grob zwischen zwei Hochsprachentypen: Interpreter und Compiler. Ein in den letzten Jahren zusätzlich in Mode gekommener Typ ist die Bei einer Interpreter-Sprache werden die einzelnen Anweisungen eine nach der anderen während der Laufzeit interpretiert und direkt umgewandelt. Der Vorteil hiervon ist, dass das Programm direkt gestartet werden kann, ohne dass der original-Programmtext vom Anwender in irgendeiner Weise verändert oder umgewandelt werden muss. Der Nachteil jedoch ist eine verminderte Geschwindigkeit sowie das wiederholte Übersetzen des Programmcodes bei jedem Start des Programmes. Wichtige Beispiele für Interpretersprachen sind BASIC, Perl, PHP, Javascript sowie grundsätzlich sämtliche Skriptsprachen. Eine Compiler-Sprache übersetzt den kompletten Programmcode ein Mal und kann danach beliebig oft ohne erneute Compilierung gestartet werden. Der Vorteil hiervon ist, dass das Programm sehr schnell ist und zudem eigenständig arbeitet. Der Nachteil ist jedoch, dass die Compilierung viel Zeit kostet und das schlussendliche Resultat ohne neu-Compilierung grundsätzlich nur auf dem Prozessortyp läuft, für den compiliert wurde. Wichtige Beispiele sind C, C++, Objective-C und Pascal. Eine virtuelle Maschine schlussendlich ist eine Mischform der obengenannten Typen. Eine solche Maschine bezeichnet einen künstlichen Prozessor, der nicht als Hardware vorhanden, sondern nur softwaremässig ausprogrammiert ist. Der Vorteil hierbei ist, dass dieser künstliche Prozessor auf jedem Computer ausprogrammiert werden kann und somit sämtliche Programme dieser virtuellen Maschine auf jedem beliebigen Computer Die Sprachen C und C++ werden mit Compilern in Maschinencode umgewandelt. Während einige eingefleischte Programmierer auf ältere Compiler aus früheren Jahren schwören, sind heutzutags besonders zwei Compiler in der breiten Masse vertreten: Der C++ Compiler des Visual Studios von Microsoft und der GNU C Compiler Wenngleich alle existierenden Compiler grundsätzlich die Aufgabe haben, Sourcecode in ein lauffähiges Programm umzuwandeln, unterscheiden sie sich doch teils markant in den Details. Beim Umsteigen von dem einen auf den anderen Compiler können Fehler angezeigt werden, die ursprünglich nicht erkannt wurden. In einigen Fällen ist es sogar möglich, das ein Programm nicht mehr richtig läuft. Insbesondere, wenn ein Programm auf verschiedenen Systemen zum Laufen gebracht werden soll, wird man auf die eine oder andere Überraschung stossen. Die Fülle an Unterschieden kann hier nicht aufgezählt werden, doch jeder, der sich an ein sogenanntes Cross-Compiling heranwagt, wird einige Überraschungen erleben und vieles über die Sprachen C und C++ lernen. Die Übersetzung von C und C++Die Übersetzung des Sourcecodes in ein Binary geschieht grob über drei Schritte: Preprocessing, Compiling, Linking. Es ist zu beachten, dass ein Programm in C oder C++ häufig nicht nur aus einer einzelnen Datei besteht, sondern aus mehreren. Die Vorverarbeitung (das Preprocessing) und das eigentliche Übersetzen (Compilieren) wird für jede Implementations-Datei (.c .cpp) einzeln eingeleitet. Header-Dateien (.h) werden normalerweise nicht als Startpunkt verwendet, sondern erst beim Preprocessing in die Implementations-Dateien eingebunden. Schlussendlich wird durch das Zusammenfügen (Linken) der einzelnen compilierten Teile ein einziges lauffähiges Programm erstellt. Hier ein kurzer Durchgang durch eine vollständige Übersetzung eines Programmes: Eine Datei, die in C oder C++ geschrieben ist, beinhaltet normalerweise Angaben wie beispielsweise Kommentare oder Leerzeichen, die der Programmierer für sich in den Programmtext eingefügt hat, die jedoch der Compiler nicht zu wissen braucht. Solche rein unterstützenden Angaben werden vom Preprozessor aus dem Quellcode herausgelöscht. Des weiteren hat der Programmierer mittels des Preprozessors die Möglichkeit, andere Dateien einzubinden, Makros zu definieren, sowie bedingte Compilierung und Fehlermeldungen zu steuern. Eine umfassende Erklärung der Möglichkeiten der Steuerung des Preprozessors kann in der Sektion Preprozessor nachgelesen werden. Die daraus entstehende, vorverarbeitete (preprocessed) Datei wird sodann compiliert. Diese Übersetzung kann in mehrere Schritte aufgeteilt werden: Zuerst werden sämtliche Angaben über Typen, Variablen, Funktionen, Klassen... usw, sowie der gesamte Aufbau der Programmierung zusammengetragen und in einer internen Struktur des Compilers gespeichert. Diese Phase nennt man das Parsen des Programmcodes. Während dieser ersten Phase wird der Code auf korrekte Syntax, also korrekte Anordnung der einzelnen Teile geprüft. Danach erfolgt eine Konsistenzprüfung, welche überprüft, ob sämtliche angesprochenen Symbole definiert wurden, ob die Typen korrekt gesetzt sind, dass keine Schutzverletzungen auftreten... usw. Hat der Compiler bis hier keinen Fehler gefunden, so beginnt er mit der Übersetzung der einzelnen Anweisungen in Assemblercode, Stück für Stück. Danach folgt auf Wunsch eine Optimierung des Codes, womit sowohl Platzverbrauch und insbesondere auch Geschwindigkeit des schlussendlichen Programmes verbessert werden. Ganz zum Schluss übernimmt ein Assembler die Übersetzung in Maschinencode. Das Resultat der Übersetzung einer einzelnen Datei wird in eine sogenannte Es sei hier bemerkt, dass diese Auflistung je nach Compiler mehr oder weniger ausgeprägt sein kann. Des weiteren sei bemerkt, dass eine Mischung von C und C++ Objektdateien zu Linker-Problemen führen kann, die jedoch mit dem extern-Keyword gelöst werden können. SchlussbemerkungDer konkrete Ablauf der Kompilierung spielt beim Programmieren selten eine Rolle, kann jedoch bis ins kleinste Detail konfiguriert werden. Spezifische Einstellungen für die einzelnen Teile der Übersetzung sind jedoch für die eigentliche Programmierarbeit normalerweise sekundär. Im alltäglichen Programmieren wird man höchstens einige Preprozessor-Anweisungen schreiben, ein paar (viele) Compilerfehler ausbügeln müssen und ein paar wenige Linker-Fehler erwischen.
|