StartseiteSitemapDownloadsHilfeImpressumLastenheft Chat


Startseite

Einführung

Was ist eine Programmiersprache?

Eine Programmiersprache ist eine Sprache zur Formulierung von Rechenvorschriften wie Algorithmen, die von einem Computer ausgeführt werden können.
Programmiersprachen bilden die wichtigste Schnittstelle zwischen Benutzern und Computern. Jeder Benutzer muss die Bearbeitung von Problemen, die einem Computer übergeben werden sollen, in einer Programmiersprache formulieren. [INFODUDEN]
Textuelle Notationen zur Beschreibung von Algorithmen nennt man Programmiersprachen. [INFO94 S. 75]

Low−Level−Programmiersprachen

Bei Low−Level−Sprachen muss der Programmierer die Architektur und Schnittstellen sowie die Funktionsweise des Rechners mehr oder weniger genau kennen. Diese Sprachen abstrahieren nur schwach von der Rechenmaschine selbst. Der Übergang zu High−Level−Sprachen ist jedoch fließend.

High−Level−Programmiersprachen

… oder höhere Programmiersprachen oder auch Computerhochsprachen liegen von der Abstraktion her weit über der Maschine. Zusätzliche Fähigkeiten kommen mit höheren Operationen hinzu, wobei viele Operationen, aber auch Eigenschaften, welche rechnerspezifisch sind, dem Entwickler verborgen bleiben. Manche Sprachen befinden sich auf einer so hohen Abstraktionsstufe, dass sie scheinbar nichts mehr mit einer konkreten Rechenmaschine zu tun haben. Andere High-Level-Sprachen bieten dem Entwickler aber auch die Möglichkeit, Programmfragmente in einer Low-Level-Sprache zu formulieren.

Mensch vs. Computer

„Allem in alles unsere Gehirn machen falsche Sätze richtig.” Claudius Stauffenberg
Ein Computer hätte aber mit falschen Sätzen echte Probleme. Die Sprache der Computer ist im Allgemeinen sehr viel einfacher als die menschliche und erfordert zudem absolute syntaktische Korrektheit. Sie verfügt über einen weitaus geringeren Wortschatz (Lexeme), ihre Grammatik (Syntax) wird kaum von Ausnahmen belastet, und es gibt keine Mehrdeutigkeiten.
Der Mensch, der eine natürliche Sprache, die bestimmten Grammatikregeln, die – nebenbei bemerkt – von Menschen für Menschen gemacht wurden und die Bildung beliebig langer Sätze erlauben, folgt, spricht, versteht seltsamerweise bestimmte Sätze, die nicht nur grammatikalisch bzw. syntaktisch korrekt sind, sondern auch inhaltlich keine Fehler, das heißt, Sprecher und Hörer gehen vom selben Zusammenhang, der üblicherweise als Kontext bezeichnet wird, aus, enthalten, aber wie der vorliegende Satz, der hier als Beispiel eines besonders langen Satzes dient, extrem lang und somit unverständlich sind, nicht. :−( Auch wenn in diesem Satzbeispiel ungeheure Defizite in der Pragmatik zu verzeichnen sind, so konnte ich Ihnen doch hoffentlich eine offensichtliche Stärke des Computers näherbringen: Ihm bereiten lange Satzkonstruktionen, wenn sie grammatikalisch korrekt sind, im Prinzip nicht mehr Probleme als kurze.
Der Computer ist eine universell einsetzbare Rechenmaschine, die im Allgemeinen nur eine endliche Folge von Befehlen (Programm) abarbeiten kann. Ihm fehlt Einfühlungsvermögen und die Motivation etwas zu verstehen. Der Mensch hingegen ist ein kommunikatives und „frei” denkendes Lebewesen mit Motiven, Emotionen und Bewusstsein. Seine Fähigkeit Ideen zu bilden, Ideen zu präsentieren, Ideen zu verstehen und Ideen zu verwirklichen, ist die Voraussetzung dafür, Probleme zu lösen oder etwas Neues zu schaffen. Dies ist dem Computer leider nicht gegeben, es sei denn, man bringt es ihm bei.
Die menschlichen Ideen können verschiedenster Art sein. Sie müssen nicht einmal mit den Gegebenheiten der Wirklichkeit verträglich sein, zum Beispiel können einzelne Bestandteile eines Traumes als Ideen aufgefasst werden. Meine Tochter träumt des öfteren, sie könne zaubern – was leider nicht ganz der Wirklichkeit entspricht, sonst gäbe es eine bessere Welt. Ich wiederum liebe besonders die Träume, in denen ich federleicht bin und unendlich weit springen kann. Und sicherlich haben Sie auch schon ähnliche Ideen geträumt.
So manche Idee kann mithilfe eines Computers realisiert werden. Die Voraussetzung für eine Idee ist erst gegeben, wenn es eine Welt gibt, die sich in einem bestimmten Zustand (Ausgangssituation) befindet. Eine Idee könnte diese Welt derart verändern, dass sie in einen gewünschten Zielzustand gerät zum Beispiel durch Planen im Situationsraum. Dabei sei erwähnt, dass die Welt nur modellhaft (d. h. nicht exakt der Wirklichkeit entsprechend) dem Computer erklärt werden kann. Man spricht auch nicht von einer zu realisierenden Idee, sondern von einem zu lösenden Problem. Wenn nun der Mensch mithilfe eines Computers ein Problem lösen möchte, dann muss er dem Computer in dessen Sprache (Programmiersprache) das Problem bzw. eine Lösungsvorschrift zum Problem (Algorithmus) formulieren, wobei das Weltwissen nicht fehlen darf (meist inhärent vorhanden). Dies ist die einzige Möglichkeit, dem Computer eine Aufgabe zu übertragen. Dabei ist Vorsicht angesagt, denn nicht jede Aufgabe führt bei Ausführung in endlicher Zeit zum Resultat, d. h. nicht jede Idee kann mit einem Computer realisiert werden.
Der Computer kann jedenfalls dazu benutzt werden, so ziemlich jede „intuitiv berechenbare Funktion” (vielleicht Ihre Idee) zu simulieren bzw. auszuführen, wenn diese in einer dafür geeigneten Programmiersprache formuliert wird. Was allerdings in der menschlichen Sprache mit ein paar wenigen Worten erklärt werden kann, muss bei einer Programmiersprache schon um einiges genauer umschrieben werden. Auch das, was sozusagen zwischen den Zeilen steht, muss dem Computer explizit beschrieben werden.
Wenn Sie in Ihrer eigenen bzw. in einer speziellen Sprache ein Programm für den Computer schreiben möchten, dann müssen Sie auch über eine entsprechende Programmiersprache verfügen, die Sie sich durchaus auch selbst entwicklen können.

Wie entwickle ich eine eigene Programmiersprache?

Diese Frage kann leider nicht mit einem Satz beantwortet werden. Wer sich dahingehend näher informieren möchte, sollte sich mit dem Bau eines Compilers (Compilerbau, Übersetzerbau) auseinandersetzen. Ein Compiler übersetzt nämlich Texte einer Ausgangssprache — vielleicht sogar Ihre — in Texte einer Zielsprache, welche zur Ausführung auf einer Maschine gebracht werden können. Wenn Sie eine eigene Programmiersprache entwickeln, sollten Sie auch den dazugehörigen Compiler selbst schreiben oder diesen in Auftrag geben. Bevor Sie aber die Entwicklung einer neuen Programmiersprache einleiten, sollten Sie zunächst bedenken, welche Probleme {Der Begriff "Problem" wird hier als neutraler Fachbegriff verwendet und kann als Synonym für "Aufgabenstellung" angesehen werden.} Sie damit bewältigen wollen, mit welcher Problemklasse Sie es zu tun haben. Erst mit dieser Überlegung ist es Ihnen möglich, eine prägnante Programmiersprache zu formulieren, die genau Ihren Zweck erfüllt (problemorientiert).
Häufig wird die Schönheit einer Programmiersprache an der Kürze ihrer Ausdrücke bemessen (wenig Code, große Wirkung). Um dieser Forderung gerecht zu werden, muss die neu zu entwickelnde Programmiersprache bezüglich der zugrundeliegenden Problemklasse und auch bezüglich ihrer Lösungsstrategien designed (konzipiert) werden.
In den meisten Fällen ist jedoch die Entwicklung einer neuen Programmiersprache gar nicht nötig, denn es gibt schon so viele. :−)

Warum gibt es so viele Programmiersprachen?

Genügt denn nicht eine einzige Programmiersprache, mit der man alle Arten von Problemen bewältigen kann? In der Tat, prinzipell käme man mit einer einzigen, so genannten universellen Programmiersprache wie beispielsweise mit C++ oder mit Java aus. Doch bei der Verwendung einer Programmiersprache wollen Sie sicherlich ohne viel drumherum auf den Punkt kommen, dies ist jedoch mit universellen Programmiersprachen eher selten der Fall. Will man 3D-Grafiken programmieren, nutzt man besser eine Programmiersprache, die dafür ausgelegt ist wie zum Beispiel POV−Ray. Für Datenbankabfragen nutzt man normalerweise die dafür spezialisierte Programmiersprache SQL und für konsistente Dokumentgenerierung verwendet man LaTeX. Spezialisierte Programmiersprachen sind auf bestimmte Problemräume zugeschnitten, mit dem Ziel spezielle Probleme prägnant und somit kostengünstig lösen zu können. Sicherlich gibt es einige überflüssige Programmiersprachen, doch einige andere spezialisierte Programmiersprachen sind aus der Welt der Informatik nicht mehr wegzudenken.



Terminologie

Folgende Begrifflichkeiten legen den Grundwortschatz eines Compilerbauers fest, gleichwohl um welche (formale) Sprache es sich handeln sollte.

Das Alphabet

… oder auch der Zeichensatz der menschlichen Sprache (wohl eher englisch) umfasst alle Zeichen, die mit einer Tastatur bzw. mit einer Schreibmaschine erzeugt werden können (Sonderzeichen eingeschlossen). Das Alphabet der meisten Programmiersprachen entspricht in der Regel dem der englischen Sprache (echte Teilmenge des ASCII−Zeichensatzes, neuerdings UTF−8). Es gibt aber auch Programmiersprachen, die beispielsweise nur acht Zeichen verstehen (wie zum Beispiel Brainfuck, eine Turing-vollständige Sprache). Die Maschinensprache kennt nur zwei Zeichen, die 0 und die 1, also ein Alphabet mit nur zwei Zeichen, das Binäralphabet.
Wie kommuniziert man mit einer Maschine, die nur zwei Zeichen versteht?

Der Wortschatz

Möchte der Mensch eine Fremdsprache erlernen, dann lernt er nicht gleich einhunderttausend Vokabeln auswendig. Um sich in der neuen Sprache verständigen zu können, genügt eigentlich ein Grundwortschatz von etwa 500 Vokabeln. Unbekannte Begriffe werden umschrieben, der Text nimmt an Umfang zu. Umgekehrt gilt natürlich: Je größer der Wortschatz, desto prägnanter die Formulierung. So verhält es sich auch bei Programmiersprachen.
Ein Wort einer Programmiersprache wird als Lexem bezeichnet. Eine Low−Level−Sprache kennt etwa 50 Lexeme, wenn man einmal von den Zahlen absieht. Sie käme aber im Extremfall mit nur einer Handvoll aus; ein paar Lexeme für Rechenoperationen und ein paar weitere für Speicherzugriffe. Kennt der Programmierer alle Lexeme der jeweiligen Programmiersprache, kann er den Code zum einen prägnanter formulieren und zum anderen das Programm bezüglich seiner Ausführungszeit (Verbesserung der Laufzeit), bezüglich seines Platzbedarfs während der Ausführung (Verbesserung des Speicherbedarfs) optimieren.
Um eine neue Programmiersprache zu erlernen, nimmt man normalerweise zu allererst ein Beispielprogramm namens „Hello World” unter die Lupe, um die ersten Lexeme einer Sprache kennen zu lernen. Im folgenden „Hello World”−Programm, welches in PHP verfasst ist, lernt man zum Beispiel mit dem Lexem echo die Ausgabe des Lexems „Hello World!” kennen. Mit diesem Programm könnte man schon fast behaupten, ein PHP−Experte zu sein. :−)

Hello World in PHP

<?php echo "Hello World!"; ?>

Das Token

Das Lexem ist eine Zeichenkette, die in der Regel einem Token zugeordnet wird. Ein Token gibt einer Menge von Lexemen lediglich einen Namen. Im Folgenden sehen Sie ein Beispiel, das vier Lexeme dem Token KLAMMER_AUF zuordnet: Oft definiert man ein Lexem mit einem regulären Ausdruck wie zum Beispiel: Mit Tokens abstrahiert man von konkreten Zeichenketten, um beispielsweise Syntaxdefinitionen (Grammatiken) übersichtlicher bzw. abstrakter vornehmen zu können. Grundsätzlich wird versucht, die benötigten Tokens (Mengen von Lexemen) disjunkt zu halten, so dass sich zum Beispiel bei der Konstruktion einer Grammatik keine ungewollten Mehrdeutigkeiten einschleichen.
Wie Tokens nun verwendet werden, können Sie beispielsweise auf vorliegender Website unter
  1. Setty−Scanner und
  2. Tinyray−Scanner
nachlesen.

Die Syntax

Allein mit dem Alphabet und dem Wortschatz einer Sprache ist es noch nicht getan. Eine Sprache ist geprägt von ihrer Grammatik beziehungsweise von ihrer Syntax, das gilt für natürliche Sprachen sowie für Computersprachen gleichermaßen.
Die Grammatik verleiht der Sprache Struktur. Die englische Sprache kennt zum Beispiel die S−P−O−Regelung (S für Subjekt; P für Prädikat; O für Objekt), um Sätze zu bilden. Nur S−P−O−formulierte Sätze sind, von ein paar Ausnahmen abgesehen, grammatikalisch korrekte Sätze der englischen Sprache, wobei die Struktur eines Satzteiles wiederum von Grammatikregeln vorgegeben wird.
Ohne Grammatik wäre die Umschreibung bzw. die Definition von weiteren Begriffen gar nicht vorstellbar. Dabei soll aber nicht der Eindruck entstehen, dass grammatikalisch korrekte Sätze Sinn ergeben müssen, wie dies zum Beispiel bei der Aussage „Gestern ist morgen.” in unserer Realität nicht der Fall ist.
Mit einer Grammatik wird lediglich festgelegt, welche Begriffe und Zeichen (Token) in welcher Reihenfolge stehen dürfen.

Einfaches Grammatikbeispiel

Grammatik G = (Nichtterminale, Terminale, Produktionen, Start) mit

Nichtterminale = {ObjektPrädikatSatzSubjekt},
Terminale = {' ''.''frisst''Hund''Katze''mag''Maus'},

Produktionen = {
      Satz ::= Subjekt ' ' Prädikat ' ' Objekt '.',
   Subjekt ::= 'Hund' | 'Katze' | 'Maus',
  Prädikat ::= 'frisst' | 'mag',
    Objekt ::= Subjekt
},

Start = Satz.
Die Grammatik G beschreibt bzw. erkennt simple S−P−O−Sätze mit Hund, Katze und Maus. Es können mit der Grammatik G einige sinnvolle, jedoch auch unsinnige Sätze gebildet bzw. erkannt werden. Die Grammatik kümmert sich nicht um Sinn oder Unsinn eines Satzes, sondern nur um die Struktur. Die Frage nach Sinn bzw. Unsinn eines Satzes gehört der Semantik an.

Die Sprache

Eine Sprache ist lediglich eine Menge von ausgewählten Zeichenketten. Ein Element dieser Menge wird manchmal als Wort (kein Lexem) bezeichnet. Offensichtlich existieren sehr viele Sprachen, die unendlich viele Elemente enthalten, also unendlich sind, d. h. nicht regulär sind, sodass es einem fast schon schwer fallen könnte, eine endliche Sprache anzugeben. :−) Als Beispiel einer endlichen Sprache möchte ich Ihnen die von der oben vorgestellten Grammatik G induzierte Sprache L(G) vorstellen.
L(G) = {
    'Hund frisst Hund.''Hund frisst Katze.''Hund frisst Maus.',
    'Katze frisst Hund.''Katze frisst Katze.''Katze frisst Maus.',
    'Maus frisst Hund.''Maus frisst Katze.''Maus frisst Maus.',
    'Hund mag Hund.''Hund mag Katze.''Hund mag Maus.',
    'Katze mag Hund.''Katze mag Katze.''Katze mag Maus.',
    'Maus mag Hund.''Maus mag Katze.''Maus mag Maus.'
}
Die Sprache L(G) ist endlich (nur 18 Elemente bzw. 18 Wörter), überschaubar und zweifellos eine Teilmenge der deutschen Sprache. Später werden Sie sehen, wie die Sprache L(G) zur Programmiersprache wird.
Wenn eine Sprache von einer Grammatik induziert (erzeugt) wird, so enthält sie genau die Elemente, die von der zugrunde liegenden Grammatik beschrieben bzw. erkannt (akzeptiert) werden. Von einer Grammatik induzierbare Sprachen sind rekursiv−aufzählbare Sprachen.
Abgrenzend muss ich noch klar stellen, dass es auch Sprachen gibt, die nicht von einer Grammatik induziert werden können, also nicht rekursiv−aufzählbar sind. Es stellt sich mir gerade die philosophische Frage, ob die vollständige deutsche Sprache überhaupt rekursiv−aufzählbar ist. Oder kurz: Kann sich der Mensch mit dem Computer unterhalten?
Programmiersprachen gehören generell zur Klasse der rekursiv−aufzählbaren Sprachen. Der Computer benötigt nämlich zur Interpretation eines Programms eine Grammatik — Geht es auch ohne Grammatik? — der zugrundeliegenden Programmiersprache. Die Klasse der kontextfreien Sprachen, eine Unterklasse der Klasse rekursiv−aufzählbarer Sprachen, ist von zentraler Bedeutung für Bauer und Nutzer einer Programmiersprache.

Die Semantik

Mit der Semantik wird festgelegt, welche Bedeutung den syntaktischen Einheiten zukommt.
Was nun ein grammatikalisch korrekter Satz auf dem Rechner bewirken soll, wird mit der Semantik der jeweiligen Programmiersprache eindeutig festgelegt. Die Prüfung der Bedeutung eines Satzes, ob Sinn oder Unsinn, wird als Semantik−Check bezeichnet.
Einen Semantik−Check für eine endliche Sprache zu realisieren, ist im Prinzip sehr einfach, weil es nur endlich viele Elemente zu prüfen gibt. Man entfernt aus der endlichen Sprache die Elemente, die keinen Sinn ergeben, und überprüft, ob sich der Input als Element in der Sprachdifferenz befindet. Als Beispiel sehen Sie die Sprachdifferenz D bezüglich der Sprache L(G).
D = L(G) / {
    'Hund frisst Hund.',                      'Hund frisst Maus.',
    'Katze frisst Hund.''Katze frisst Katze.',
    'Maus frisst Hund.''Maus frisst Katze.''Maus frisst Maus.',
                      'Hund mag Katze.',
    'Katze mag Hund.',                     'Katze mag Maus.',
                      'Maus mag Katze.'
} = {
                        'Hund frisst Katze.',
                                               'Katze frisst Maus.',

    'Hund mag Hund.',                    'Hund mag Maus.',
                       'Katze mag Katze.',
    'Maus mag Hund.',                    'Maus mag Maus.'
}
In diesem Beispiel bin ich jedoch vom Basiskontext ausgegangen. Es könnte auch sein, dass in einem anderen Kontext (spezielles Wissen, spezielle Zusammenhänge) auch der Satz ‚Maus mag Katze.’ semantisch korrekt sein kann. Der Mensch gibt dem Computer vor, was er verstehen soll — Gewissen und Ethik gehen hierbei im gewissen Sinne vom Menschen aus.
Hat man es aber mit einer unendlichen Sprache zu tun, muss man sich Strategien überlegen, die semantisch fehlerbehaftete Elemente der Sprache herauszufiltern.
Streng genommen wäre ein Semantik−Check gar nicht nötig, wenn bereits die zugrundeliegende Grammatik keine semantisch fehlerbehafteten Elemente mehr zuließe. Doch dann würde diese Grammatik in der Praxis kaum handhabbar sein und eine kontextsensitive Sprache induzieren — der Kontext müsste der Grammatik bekannt sein.

Abstrakte Maschine

Die Skeptiker unter Ihnen könnten sich jetzt fragen, was nun das Hund−Katze−Maus−Beispiel mit Programmiersprachen zu tun hat und das mit Recht, denn es fehlt noch die Angabe einer Maschine, die die Sprachelemente aus dem Hund−Katze−Maus−Beispiel erwartet.
Eine Definition einer abstrakten Maschine zur Sprache L(G) macht sie automatisch zur Programmiersprache.
Ich definiere eine abstrakte Maschine, die die Infix−Relationen ‚frisst’ und ‚mag’ gemäß meiner Vorstellung von Fressen und Mögen bildlich darstellt. Diese Maschine erwartet als Eingabe ein Element der Sprache L(G). Die Akteure sind gemäß der Eingabe: Hund, Katze, Maus.
Mit dieser Maschinendefinition wird das hier vorgestellte Sprachbeispiel L(G) — wenn auch ein wenig an den Haaren herbeigezogen — zur Programmiersprache.

Analogie zum Alltag

Es gibt noch primitivere Programmiersprachen als das Hund−Katze−Maus−Beispiel sogar für reale Maschinen! Ich denke da gerade an einen Lichtschalter mit der Programmiersprache {'an', 'aus'}, der bei Eingabe 'an' bzw. 'aus' den Stromkreis mit einer Glühbirne als Verbraucher schließt bzw. unterbricht. Die Eingabe eines Programms der Programmiersprache {'an', 'aus'} wird von einem Kippschalter (Lichtschalter) simuliert. Wie viele Programme haben Sie heute eigentlich schon programmiert? :−)



Kategorien

Aufgrund der Vielfältigkeit menschlicher Ideen (Konzepte und Denkschemata) haben sich viele unterschiedliche Programmiersprachen etabliert, welche sich je nach Abstraktionsgrad einteilen lassen.

Maschinensprachen

Die von einem Rechner bzw. Prozessor auf unterster Ebene verarbeiteten Instruktionen bilden die sogenannte Maschinensprache. [INFO94 S. 472]
Diese Sprache besteht praktisch nur aus Nullen und Einsen (Binäralphabet).
Ein Maschinenprogramm, welches in einer Maschinensprache (Binärcode) formuliert wird, kann von einem Computer direkt ausgeführt werden.
Die Maschinensprache ist speziell auf einen bestimmten Prozessor und seine Möglichkeiten ausgelegt. [COMPULEX] Jeder Prozessor nutzt andere Formate und Maschinenbefehle. Die Vielzahl unterschiedlicher Prozessoren und somit unterschiedlicher Maschinensprachen macht es dem Menschen praktisch unmöglich, einen Überblick zu behalten, zumal die Interpretation eines Maschinenprogramms äußerst schwer ist.

Eine für den Menschen unverständliche Sprache

...010100000101111101010101010010111001001010010000101100100001
001101001000010010010100100100101110101101101001000101000110010
101010010100111010111101001010010010101101000101011010001010001
001000100000101000111111011101101010010100000000101010110000...
Dabei sei bemerkt, dass heutzutage die Maschinenprogramme tausendfach oder gar millionenfach größer sind als dieses Codebeispiel.
Manchmal will der Mensch ein Maschinenprogramm – man glaubt es kaum – analysieren, um zum Beispiel einen Computervirus {Computerviren hängen sich in der Regel an das Ende von ausführbaren Maschinenprogrammen (sog. Wirtsprogrammen) und manipulieren manche Jump-Befehle dergestalt, dass der unerwünschte Virus bei Ausführung des Wirtsprogramms sicher zur Ausführung kommt.} im Programm ausfindig zu machen. Dabei muss er die Instruktionen des zugrundeliegenden Prozessors genau kennen.
Instruktionen sind Maschinenbefehle in Binärschreibweise und können sogar eine variable Länge besitzen (siehe CISC; Complex Instruction Set Computer). Dabei bewirkt jede einzelne Instruktion eine bestimmte Operation auf dem zugrundeliegenden Prozessor, wie zum Beispiel: Mit Hilfe der Instruktionen, also Teile des Programms, bearteitet die Maschine wiederum das Programm.
Jedes korrekt arbeitende Maschinenprogramm in Binärschreibweise kann in eine Folge von Instruktionen, die der Mensch in einzelne, verständliche Operationen übersetzen kann, zerlegt werden (deassemblieren). Beim Erstellen eines Maschinenprogramms geht man allerdings in umgekehrter Weise vor (siehe Assemblersprachen).

Assemblersprachen

Für den Menschen ist der Umgang mit Maschinensprachen offensichtlich nicht einfach. Deshalb formuliert der heutige Programmierer keine binären Maschinenbefehle mehr aus, sondern verwendet stattdessen höhere Sprachkonzepte, die es ihm ermöglichen, einen leserlichen und somit korrigierbaren, erweiterbaren Programmcode zu schreiben, der schließlich von Hilfsprogrammen (Assembler oder Compiler) in einen Maschinencode übersetzt werden kann.
Ein Assembler ist ein solches Hilfsprogramm, welches einen Assemblercode in Maschinensprache übersetzt. Der Assemblercode ist eng an die Maschinensprache eines Prozessors angelehnt.
Viele Assemblerbefehle sind symbolische (mnemonische) Darstellungen der Maschinenbefehle, also ADD statt 0001101100000100. Meist beziehen sich solche Assemblerbefehle auf ein oder zwei Operanden, die numerische Werte darstellen oder auf Speicherplätze bzw. Register verweisen. [COMPULEX]
Die Assemblersprache bildet die nächsthöhere Abstraktionsstufe zur Maschinensprache, dennoch sind Assemblersprachen maschinennahe Sprachen, d. h. beinahe jede Prozessorfamilie benötigt ihre eigene Assemblersprache.
Im Folgenden werden einige Assemblerbefehle für die MIPS-Prozessorfamilie (siehe 3−Adress−Rechner) erklärt (die Kommentare werden mit # eingeleitet).

Erläuterungen einiger MIPS−Assemblerbefehle

                  # Die Addition zweier Registereinträge
ADD $1, $2, $3    # R[1] := R[2] + R[3]

                  # Die Subtraktion zweier Registereinträge
SUB $1, $2, $3    # R[1] := R[2] - R[3]

                  # Die Multiplikation zweier Registereinträge
MULT $1, $2, $3   # R[1] := R[2] * R[3]

                  # Die UND-Operation zweier Registereinträge
AND $1, $2, $3    # R[1] := R[2] & R[3]

                  # Unbedingter Sprung im Maschinencode
J 100             # goto 100

                  # Bedingter Sprung im Maschinencode
BEQ $1, $2, 100   # if R[1] = R[2] then goto 100
In der Regel generiert der Assembler aus jedem einzelnen Assemblerbefehl genau eine binäre Instruktion. Jede Instruktion benötigt einen Takt an Rechenzeit, wenn man einmal von komplexen Operationen wie Fließkommaoperationen absieht. Ein 1−Giga−Hertz−Prozessor könnte also theoretisch in einer Sekunde etwa 1.000.000.000 Assemblerbefehle abarbeiten, wenn er nicht ständig auf Speicherinhalte vom Hauptspeicher warten müsste.
Um wirklich effiziente Assemblerprogramme schreiben zu können, muss der Programmierer den jeweiligen Prozessor genau studiert haben und wissen, wie dieser arbeitet. Umgekehrt trägt das Erlernen einer Assemblersprache wesentlich zum Verständnis der Arbeitsweise eines Prozessors bei.


Der moderne Programmierer möchte jedoch einen Algorithmus weitgehend unabhängig von der verarbeitenden Maschine entwickeln. Aus diesem Grund entstanden die sogenannten höheren Programmiersprachen wie beispielsweise problemorientierte Sprachen, die schließlich in eine beliebige Assemblersprache übersetzt werden können.

Imperative Programmiersprachen

… sind befehlsorientierte Sprachen. Ein imperatives Programm besteht ähnlich wie ein Assemblerprogramm aus einer Folge von Befehlen an den Prozessor wie z. B. „Schreibe in die Variable a den Wert 3”, wobei wir schon beim ersten wesentlichen Merkmal dieser Sprachen wären – der Verwendung von Variablen. Speicherzellen können mit Namen versehen werden, den sogenannten Variablen. Bei einer Zuweisung beispielsweise wird der Inhalt einer Speicherzelle, also der Wert der Variable, überschrieben.
Während Variablen den Inhalt einer Speicheradresse repräsentieren, repräsentieren Zeiger – die sogenannten Pointer – Speicheradressen. Pointer können auf Variablen, also auf Speicherinhalte, zeigen, aber auch auf andere Pointer, welche wiederum als Speicherinhalte aufgefasst werden können.
Die Befehle eines imperativen Programms bezeichnet man als Anweisungen, sogenannte Statements, welche induktiv definiert sind: Diese Statements werden in sogenannten Programmblöcken (Prozeduren oder auch Unterprogramme) verwendet, die dann bei konventioneller Programmierung modular zur Ausführung gebracht werden können. Nicht nur deshalb werden imperative Programmiersprachen oft auch als prozedurale Programmiersprachen bezeichnet, sondern auch wegen der prozeduralen Sichtweise, zu der dieses Sprachkonzept erzieht:
  1. Der Benutzer sagt, was das Problem ist, und der Benutzer kontrolliert, wie es gelöst werden soll. [PROGINLOG S. 15]
    Je nach Art der Problemstellung sieht der Programmierer im Programmcode vor, mit welcher Prozedur das Problem gelöst werden soll (z. B. „Löse nach Verfahren-A und nicht nach Schema-F!”).
Eine imperative Programmiersprache stellt in der Regel die nächsthöhere Abstraktionsstufe zur Assemblersprache dar. Spezielle Übersetzerprogramme, die Compiler genannt werden, formulieren aus einem Programmcode ein (hoffentlich!) optimiertes Assemblerprogramm, welches wiederum in eine Maschinensprache überführt wird. Manchmal muss aber ein Programmierer bestimmte Zusicherungen bzgl. der Effizienz garantieren. In diesem Fall wird dem Compiler nicht vertraut, sondern an den kritischen Stellen der Assemblercode explizit ausformuliert. Dies wird auch von den meisten imperativen Programmiersprachen unterstützt.


Im Vergleich zu anderen Programmiersprachen unterscheiden sich imperative Programmiersprachen bis auf ein paar Kleinigkeiten nicht wesentlich von Assemblersprachen. Von der Art des Prozessor- und Speicherzugriffs wird nur geringfügig abstrahiert (z. B. mit der Verwendung von Variablen), wohingegen bei den prädikativen und funktionalen Programmiersprachen die Begriffe Prozessor und Speicher nur in Ausnahmefällen gebraucht werden.
Die Welt der Informatik hat schon recht früh erkannt, dass die imperative Programmierung in der Praxis einen üblen Nachteil mit sich bringt, nämlich eine aufwendige Programmkorrektur. Die Korrektur eines imperativen Quellprogramms kann zu einer wahren Odyssee werden, wenn beispielsweise viele globale Variablen vorkommen. Bei der imperativen Programmierung benötigt man zwangsläufig globale Variablen, um den aktuellen Programmzustand halten zu können. Der Wert einer globalen Variable kann praktisch an jeder Stelle des Programms nach Belieben geändert werden. Der Programmierer (möglicherweise ein Neuer im Team) wird bei der Neubelegung einer Variable nicht programmtechnisch gezwungen, irgendwelche Nebenbedingungen oder Spezifikationen einzuhalten. Ein Beispiel für einen Spezifikationsbruch sehen Sie im folgenden Codebeispiel.

Das Übel globaler Variablen

int geburtsjahr; // Globale Variable

geburtsjahr = 2001; // Spezifikation: vollständige Jahreszahl

...

if (geburtsjahr > 1682) print("Geboren nach Blaise Pascal!");

...

geburtsjahr = 74; // gemeint war aber 1974

// Programmtechnisch erlaubt, aber Bruch der Spezifikation!
// Programm wird stellenweise nicht mehr korrekt ablaufen.

...
Bis ein Programmierer ähnliche Fehler (Bruch der Spezifikation) entdecken wird, kann es lange dauern. Wertvolle Arbeitszeit muss in die Fehlersuche investiert werden – und das alles nur, weil der Programmierer eine Kleinigkeit übersieht und der Compiler trotzdem nicht meckert.
Die Antwort auf das Übel der globalen Variablen ist die objektorientierte Programmierung (kurz OOP).

Objektorientierte Programmiersprachen

Bei diesen Sprachen werden alle zum Lösen eines Problems notwendigen Informationen (Daten und Anweisungen bzw. Regeln) als Objekte aufgefasst. Objekte können durch Senden von Nachrichten (Mitteilungen an andere Objekte) miteinander Informationen austauschen. Für jedes Objekt sind die Nachrichten, die es verstehen kann, festgelegt. Empfängt ein Objekt eine Nachricht, so antwortet es mit einem anderen Objekt. [INFODUDEN]
Objektorientierte Programmiersprachen können als Erweiterung der imperativen Programmiersprachen aufgefasst werden. Sie zeichnen sich besonders durch ihr elegantes Speichermanagement aus. Beispielsweise regelt jedes Objekt mit seinen Methoden den externen Zugriff auf seine privaten Membervariablen. So kann das Objekt von außen nicht in einen inkonsistenten Zustand versetzt werden – ein Bruch der Spezifikation ist deshalb kaum noch möglich.
Der OOP−Programmierer kann für Methoden, Attribute und Konstanten die Sichtbarkeit mit diversen Schlüsselwörtern (public, protected, private etc.) festlegen. Manchmal ist es einem gar nicht bewusst, dass man mit der Festlegung der Sichtbarkeit den Speicher managt.

Funktionale Programmiersprachen

Programme berechnen Funktionen, die Eingabedaten in Ausgabedaten abbilden. In der funktionalen Programmierung beschreibt man daher die Beziehung zwischen Ein- und Ausgabedaten mithilfe mathematischer Ausdrücke, indem man elementare Ausdrücke für einfache Funktionen verwendet und mit Operationen, die auf Funktionen definiert sind, komplexere Funktionen darstellt – insbesondere auch Funktionen, die auf Funktionsräumen definiert sind (das Integral ist z. B. eine solche höhere Funktion, da es eine stetige Funktion als Parameter enthält und als Ergebnis wiederum eine Funktion, die Stammfunktion, liefert). Das wichtigste Konstruktionsprinzip ist hierbei die Rekursion.
Ein Programm besteht aus einer Menge von Ausdrücken, die Funktionen definieren. Eine Berechnung ist die Anwendung einer Funktion auf eine Liste von Werten oder Ausdrücken. Eine solche Anwendung einer Funktion auf einen Ausdruck nennt man Applikation, weshalb man auch von applikativen Programmiersprachen spricht.
Dass dieser Ansatz tatsächlich geeignet ist, alle algorithmischen Aufgabenstellungen bearbeiten zu können (Church'sche These), zeigt der Lambda−Kalkül, auf dem das funktionale Programmieren letztlich basiert. [INFODUDEN]

Prädikative Programmiersprachen

Bei diesen Sprachen wird Programmierung als das Beweisen in einem System von Tatsachen und Schlussfolgerungen aufgefasst: Der Anwender gibt eine Menge von Fakten (gültige Prädikate) und Regeln (d. h. wie man aus Fakten neue Fakten gewinnt) vor, und die Aufgabe des Rechners ist es, eine gestellte Frage richtig (true) oder falsch (false) zu beantworten. [INFODUDEN]
Ein kleines OnlineProlog ohne lästige Installation mit vielen Beispielen finden Sie unter SimpleProlog bzw. SimplePrologPlus.

Skriptsprachen

Bei der Programmierung eines Programms muss sich der Programmierer oft mit lästigen Detailfragen aufhalten. Selten kann sich der Programmierer mit dem Wesentlichen beschäftigen.
Skriptsprachen werden auf einem sehr hohen abstrakten Niveau angesiedelt und nehmen dem Programmierer in aller Regel lästige Detailimplementierungen ab. Beispielsweise ist es dem Skriptprogrammierer egal, ob er nun eine ArrayList oder eine LinkedList als Liste zur Verfügung hat. Listen werden in den Skriptsprachen gerne als assoziative Arrays oder HashTables realisiert, um eben extrem gute Zugriffskomplexitäten gewährleisten zu können. Um welche konkrete Datenstruktur es sich bei einer Liste tatsächlich handelt, bekommt der Skriptprogrammierer eigentlich gar nicht mit – es interessiert ihn auch nicht wirklich.
Aufgrund des hohen Abstraktionsniveaus einer Skriptsprache ist es dem Skriptprogrammierer möglich, extrem kurze und vor allem prägnante Programme zu erstellen.



Info

stefan−baur.de / Programmiersprachen
  • besucht am Freitag, den 12. März 2010 um 3:41 Uhr
  • geändert am Freitag, den 21. August 2009 von Stefan K. Baur
  • für Inhalte danke ich:
  • ähnliche Seite:

[INFODUDEN]  Duden Informatik,  Bibliographisches Institut & F. A. Brockhaus AG,  2. Auflage,  1993,  ISBN 3−411−05232−5.  Ein Sachlexikon für Studium und Praxis

[INFO94]  Heinz−Peter Gumm,  Manfred Sommer:  Einführung in die Informatik,  Addision−Wesley (Deutschland) GmbH,  1. Auflage,  1994,  ISBN 3−89319−574−2.

[COMPULEX]  Andreas Voss,  Christian Raabe:  Das große DATA BECKER Computer Lexikon,  DATA BECKER GmbH & Co.KG,  1. Auflage,  1997,  ISBN 3−8158−1575−4.  Index für den Sofortzugriff auf alle Stichworte und Unterbegriffe mit über 800 Seiten

[PROGINLOG]  Ramin Yasdi:  Logik und Programmieren in Logik,  Prentice Hall Verlag GmbH,  1995,  ISBN 3−93043−624−8.  Ein Lehrbuch







Startseite

Copyright © 2004-2009 Stefan K. Baur − Druck20042005200620072008200920102011