Grundbegriffe zu Programmiersprachen von: Ansgar Schiffler, 2005 zurück zu 'Die Programmiersprache C++' Was ist eine höhere Programmiersprache? Wir wird der Quellcode in Maschinencode übersetzt? Hier erhalten Sie Antworten auf diese Fragen. Stichwortübersicht:
Bei Programmiersprachen handelt es sich um künstliche Sprachen, die der Kommunikation mit einem Computer dienen. Ihre Syntax ist wesentlich restriktiver als die natürlicher Sprachen. Die Programmiersprachen werden in sogenannten höhere Programmiersprachen und maschinennahe Sprachen unterteilt. Die Syntax (Sprachregeln) einer höheren Programmiersprache ist unabhängig von der Hardware, auf der das Programm ausgeführt werden soll. Die Syntax ist an die Bedürfnisse des Menschen angepasst, damit ihm das Programmieren möglichst leicht fällt. Die Übersetzung in eine Syntax, die zum Prozessor passt, wird von speziellen Programmen übernommen. Als Maschinensprache wird die für den Prozessor
erforderliche Darstellung von Befehlen im binären Zahlenformat
bezeichnet. Maschinensprache ist schwer zu programmieren, da sie an der
Hardware ausgerichtet ist und nicht wie die höheren Programmiersprachen
am Benutzer.
Die maschinennahen Sprachen werden auch
als Assembler-Sprachen bezeichnet. Diese Sprachen sind im Gegensatz zu den
höheren Programmiersprachen sehr stark an der Hardware; dem Prozessor;
ausgerichtet. In
Maschinensprache übersetzt wird der Quellcode von einem Assembler, der
genaugenommen ein Compiler ist, der den Code eines
"Assemblerprogramms" in Maschinensprache, d. h. Nullen und
Einsen übersetzt. Anders als ein C-Compiler hat es der Assembler jedoch
relativ einfach, da (fast immer) einer Assembleranweisung genau eine
Maschinensprachenanweisung entspricht. Das Assemblerprogramm ist also nur
eine für Menschen (etwas) komfortablere Darstellung des
Maschinenprogramms. Statt 000001011110100000000011
schreiben zu müssen, kann der Programmierer die Assembleranweisung
add ax,1000 verwenden. Eine Programmierung in 'Assembler' kann
dann sinnvoll sein, wenn es sich um besonders zeitkritische Anwendungen
handelt. Der Quellcode einer höheren Programmiersprache ist nicht direkt ausführbar, sondern muss in Maschinensprache übersetzt werden. Diese Aufgabe übernimmt ein Programm, das als Compiler bezeichnet wird. Dabei muss bekannt sein, auf welchem Prozessor bzw. auf welcher Prozessorfamilie das Programm ausgeführt werden soll. Daher gibt es für jede höhere Programmiersprache verschiedene Compiler. Das von einem Computer ausführbare Programm wird im
Englischen als 'executable' bezeichnet und erhält daher üblicherweise
die 'Extension' .exe (an den Dateinamen wird die Endung .exe angefügt).
Ein für einen Pentium IV-Prozessor übersetzes Programm kann
durchaus auch von einem Pentium III-Prozessor ausgeführt werden, da beide
zur gleichen Prozessorfamilie gehören und die Befehle kompatibel sind.
Falls ein neuer Pentium-Prozessor gegenüber seinem Vorgängermodell neue
Befehle kennt und das vom Compiler übersetzte Programm auf beiden
Versionen funktionieren soll, müssen die Software-Entwickler beim
Schreiben des Compilers darauf achten, dass diese neuen Maschinen-Befehle
nicht verwendet werden. Ein Interpreter ist genauso wie ein Compiler ein
Programm, das Quellcode einer höheren Programmiersprache in
Maschinensprache übersetzt. Jedoch ist dies beim Compiler ein einmaliger
Vorgang: Das Programm wird komplett übersetzt und es wird eine ausführbare
Datei erzeugt. Der Interpreter hingegen übersetzt kontinuierlich während
der Ausführung eines Anwendungsprogramms. Der Interpreter arbeitet dabei
den Quellcode zeilenweise ab, übersetzt die aktuelle Zeile in
Maschinensprache und führt die daraus entstandenden Anweisungen aus. Höhere
Programmiersprachen, die ausschließlich von einem Interpreter übersetzt
werden, sind heute bedeutungslos. Jedoch gibt es einen Trend die Vorzüge
von Compiler und Interpreter zu vereinen. Zum Beispiel wird bei der
Programmiersprache Java der Quellcode zunächst von einem Compiler in
einen sogenannten Byte-Code übersetzt, der dann zur Laufzeit des
Programms von einem Interpreter, bei Java 'Virtual Machine' genannt, in
Maschinensprache übersetzt wird. Ein Software-Produkt besteht oft aus mehrere Übersetzungseinheiten, die getrennt (also jeweils einzeln) kompiliert (übersetzt) werden. Der Übersetzungsvorgang einer einzelnen Übersetzungseinheit erzeugt dann noch nicht das Endprodukt, sondern ein Zwischenprodukt (eine Zwischendatei), die manchmal auch als Objektdatei bezeichnet wird. Diese Zwischenprodukte werden dann schließlich durch ein weiteres Programm, den Linker (Verbinder) zu einem ausführbaren Programm verbunden. Eine Zwischendatei ist schon weitgehend in die ausführbare Zielsprache übersetzt, ist aber alleine noch nicht ausführbar, da ihr noch Programmteile fehlen, die an anderen Stellen definiert wurden, und auch ihr Format noch nicht den Vorgaben des Zielsystems für ausführbare Dateien entsprechen muß. Als Header-Datei bezeichnet man eine Datei, die allgemeinen Deklarationen zur Nutzung einer Bibliothek enthält. Zum Beispiel sind die Funktionen 'cin' und 'cout' nicht Teil der Programmiersprache C++, wie z.B. die Schlüsselwörter 'if' und 'else'. Mit '#include stdio.h' wird die Header-Datei 'stdio.h' eingebunden. Das bedeutet, dass vor der Übersetzung des Quellcodes durch den Compiler ein anderes Programm - der Praeprozessor - den gesamten Quellcode nach Zeilen durchsucht, die mit '#' beginnen. Der Praeprozessor ersetzt dann diese Zeile durch den gesamten Inhalt der entsprechenden Header-Datei. In der Header-Datei 'stdio.h' werden dann die Funktionen 'cin' und 'cout' bekannt gemacht. In der vom Compiler erzeugten Objekt-Datei sind diese Funktionen aber noch nicht enthalten. Erst der Linker sucht nach diesen Funktionen in den entsprechenden Bibliotheksdateien und fügt sie dem ausführbaren Programm hinzu. Hinweis: dies gilt für statische Bibliotheken. Als 'Library' (deutsch: Bibliothek) bezeichnet man eine Sammlung von Programmfunktionalitäten für zusammengehörende Aufgaben. Bibliotheken sind im Unterschied zu Programmen keine eigenständige Einheiten sondern Hilfsmodule, die Programmen zur Verfügung stehen. Beispielsweise enthält die C++-Bibliothek 'stdio.lib' die Funktionen 'cin' und 'cout' zur Ein- und Ausgabe von Text und Daten über den Bildschirm. Der Linker sucht aus den Bibliotheksdateien
Funktionen heraus, für die es im Quellcode keine Implementierungen gibt.
Diese werden dann an den Programmcode angefügt.
Dadurch werden Umfang und Speicherbedarf der ausführbaren Datei
vergrößert. In diesem Fall spricht man von statische Bibliotheken. Angenommen in einem Multitasking-System laufen dutzende Prozesse gleichzeit, die alle die gleiche Funktion verwenden. Im Falle einer statische Bibliothek ist der Code dieser Funktion ein paar dutzend mal im Arbeitsspeicher vorhanden, für jeden Prozess einmal, obwohl der Code immer identisch ist. Hier schaffen dynamische Bibliotheken Abhilfe. Jetzt ist der Code dieser Funktion nicht mehr ein Teil der ausführbaren exe-Datei. Wenn bei der Ausführung des Programms - also zur Laufzeit - diese Funktion benötigt wird, sucht der 'Loader' nach dieser Funktion im Arbeitsspeicher. Findet er sie dort nicht, lädt er sie in den Arbeitsspeicher. Wenn dann ein weiterer Prozess diese Funktion benötigt, ist sie bereits im Arbeitsspeicher und das Programm verzweigt an die entsprechende Stelle. Auch wenn nun viele Prozesse diese Funktion benötigen, ist ihr Code nur einmal im Arbeitsspeicher vorhanden. Die Ausführungszeit, insbesondere die Startzeit eines Programms, ist hier geringfügig erhöht. Dies wird in Kauf genommen, da der Programmcode der Bibliotheksfunktionen von allen Prozessen geteilt wird. Der gesamte Speicherbedarf aller Programme zusammen ist daher in der Regel kleiner als beim statischen Linken. Unter Windows wird eine dynamische Bibliothek als 'dynamic link library' ( extension: 'dll') bezeichnet und unter Unix /Linux wird sie als 'shared library' (extension: 'so' ; shared object) bezeichnet. |