Programmiersprache

formale Sprache zur Formulierung von Rechenvorschriften, die von einem Computer ausgeführt werden können
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 12. Oktober 2004 um 10:57 Uhr durch HannesH (Diskussion | Beiträge) (Kurze Zeittafel: JavaScript statt ActionScript (früher)). Sie kann sich erheblich von der aktuellen Version unterscheiden.

Eine Programmiersprache ist eine formale Sprache zur Darstellung (Notation) von Computerprogrammen. Sie vermittelt dem Computersystem durch von einem Menschen geschriebenen Text genaue Angaben zu einer Kette von internen Verarbeitungsschritten, beteiligten Daten und deren Struktur in Abhängigkeit von internen oder externen Ereignissen.

Da digitale Computer intern nur die Werte 0 und 1 verarbeiten, wäre es nach heutigen Maßstäben extrem umständlich und mühsam, die vielen Formen der Informationsverarbeitung als Binärzahlen einzugeben (zu kodieren). Daher wurden in den letzten Jahrzehnten Verfahrensweisen etabliert, nach denen man häufig verwendete Zahlen und Zeichen und häufig verwendete grundlegende Operationen in symbolischen Befehlen angibt. Eine weitere technische Einrichtung übersetzt dann diese Angaben in interne Daten, einfachste Datenänderungsbefehle und Kontrollanweisungen, die der Computer dann schließlich ausführt.

Wird ein Programmtext als Ganzes übersetzt, spricht man in Bezug auf den Übersetzungsmechanismus von einem Compiler. Der Compiler ist selbst ein Programm, welches als Dateneingabe den menschenlesbaren Programmtext bekommt und als Datenausgabe den Maschinencode liefert, der direkt vom Prozessor verstanden wird (zum Beispiel Objectcode, EXE-Datei) oder in einer Laufzeitumgebung (zum Beispiel JVM oder .NET) ausgeführt wird. Wird ein Programmtext hingegen Schritt für Schritt übersetzt und der jeweils übersetzte Schritt sofort ausgeführt, spricht man von einem Interpreter. Interpretierte Programme laufen meist langsamer als kompilierte.

Eine logische Abfolge von Befehlen in einer Programmiersprache nennt man allgemein Programm, Programmcode oder Quelltext. (Quelltext betont besonders die Lesbarkeit). Dieser wird von Programmierern verfasst.

Elemente einer Programmiersprache

Die meisten Befehle einer Programmiersprache lassen sich auf 5 Hauptkategorien zurückführen:

  • Eingabe = input
Daten von der Tastatur, von einer Datei oder anderswo her übernehmen.
  • Ausgabe = output
Daten ausgeben: auf den Monitor, auf einen Drucker oder in eine Datei
  • Mathematische Berechnung
Eine mathematische Berechnung, wie Addition oder Multiplikation oder Ähnliches, durchführen.
  • Vergleich und Auswahl
Bestimmte Bedingungen überprüfen und anhand des Ergebnisses entscheiden, welche Befehle als nächstes ausgeführt werden.
  • Wiederholung
Eine Befehlsfolge wiederholt durchführen, meist mit einer gewissen Variation.

Eine andere Kategorisierung sieht wie folgt aus:

  • Arbeiten mit Variablen
Zuweisen, Auslesen und Ändern von Speicherinhalten
  • Elementare Mathematik
zumindest die vier Grundrechenarten
  • Bedingte Verzweigung
Abhängig von einer Bedingung wird ein anderer Programmfluss ausgewählt
Wiederholen von Programmteilen abhängig von Bedingungen
  • Blockbildung
Zusammenfassung mehrerer Befehle, zum Beispiel einer Schleife, in einem Unterprogramm
  • Umgang mit nicht mathematischen Elementen
zum Beispiel mit Text, Bildern, Sound...
  • Kommentare
Die meisten Programmiersprachen ermöglichen es, Kommentare in den Programmtext einzufügen.

Jede Programmiersprache hält Vereinbarungen bezüglich Syntax, Vokabular und Bedeutung bereit für:

  • Daten und Datenstrukturen
  • Befehle und Befehlsgruppen
  • Bezugnahmemechanismen und Wiederverwendung
  • zumeist eine primäre Designphilosophie

Daten und Datenstrukturen

Um die üblichen Arten von Informationen im Computer abbilden zu können, müssen Möglichkeiten zur Definition von Daten oder Datenstrukturen bereitstehen, auch als Datentyp bezeichnet. Hierbei kann zwischen typisierten (zum Beispiel C++ oder Java) und typenlosen Sprachen (zum Beispiel JavaScript, Tcl oder Prolog) unterschieden werden. Bei typisierten Sprachen sind dies entweder vordefinierte Einheiten für einzelne Zahlen (Byte, Integer, Word, etc.) und Zeichen (Char) oder auch zusammengesetzte für Daten, Wörter, Text, sensorische Information und so weiter (Strukturen, Klassen). Zumeist besteht auch die Möglichkeit, zusammengesetzte Objekte oder Strukturen aufzubauen und als neuen Typ zu vereinbaren (etwa Arrays, Listen, Stacks, ganze Dateien). Die typenlosen Sprachen behandeln oftmals alle Einheiten als Zeichenketten und kennen für zusammengesetzte Daten eine allgemeine Liste (zum Beispiel Perl).

Bei den typisierten Sprachen gibt es solche mit Typprüfungen zur Übersetzungszeit (statisch typisiert) und solche in denen Typprüfungen primär zur Laufzeit stattfinden (dynamisch typisiert, etwa Ruby, Smalltalk). Werden Typfehler spätestens zur Laufzeit erkannt, spricht man von typsicheren Sprachen. Oft wird fälschlicherweise die statische Typprüfung wegen des angenommenen qualitativen Vorteils gegenüber der dynamischen Typprüfung als "sicher" bezeichnet.

Es kann keine allgemeine Aussage über die Tauglichkeit beider Formen der Typprüfung getroffen werden - bei statischer Typprüfung ist der Programmierer versucht, diese zu umgehen, beziehungsweise sie wird erst gar nicht vollständig durchgesetzt (zum jetzigen Stand der Technik muss es in jeder statischen Sprache eine Möglichkeit geben, "Typlose" Daten zu erzeugen oder zwischen Typen zu wechseln - etwa wenn Daten vom Massenspeicher gelesen werden), in Sprachen mit dynamischer Typprüfung werden manche Typfehler erst gefunden, wenn es zu spät ist. Bei dynamischer Typprüfung wird jedoch der Programmcode meist sehr viel einfacher.

Oft kann an den "Bürgern erster Klasse" ("First class Citizens" - FCCs) einer Programmiersprache - also den Formen von Daten, die direkt verwendet werden können, erkannt werden, welchem Paradigma die Sprache gehorcht. In Java (Objektorientiert)zum Beispiel sind Objekte FCCs, in LISP (Funktional) ist jedes Stück Programm FCC, in Perl sind es Zeichenketten, Arrays und Hashes.

Befehle und Befehlsstrukturen

Konzeptionell ist ein programmierbarer Rechner weit mehr als eine Sammlung von Daten und auch keine starre Rechenmaschine. Vielmehr wird angegeben, wie der Computer mit variablen internen oder externen Daten zu verfahren hat. Elementare Anweisungen geben über Schlüsselwörter (key words) an, WANN WAS WIE geändert werden soll. Jede Sprache enthält eine WENN-DANN-artige Anweisung, die letztlich zusammen mit dem Sprungbefehl (Goto) die Universalität bereitstellt, um allgemein auf vielfältigste Fälle reagierende Programme schreiben zu können. Falls Konstrukte für Schleifen mit Bedingungen (Bedingung zuerst, dann Anweisungsgruppe, oder: erst Anweisungsgruppe und dann Bedingung) zur Verfügung stehen, kann auf den Sprungbefehl vollständig verzichtet werden. Niklaus Wirth hat dies mit Modula-2 gezeigt.

Bezugnahmemechanismen und Wiederverwendung

Der Kern der Bezugnahme ist die Benennung von Speicherplatz als so genannte Variable. Weitere Bezugnahmen sind Zeiger auf solche Variablen oder Variablengruppen. Auch Befehlsgruppen werden im Programm per Namensaufruf als Prozedur oder Funktionsaufruf verfügbar gemacht. Auf diese Weise wird durch Symbole einerseits eine große Variabilität erreicht und durch Referenz auf vorhandene Programm- oder Datenteile andererseits ein hohes Maß an Wiederverwendbarkeit erreicht. Viele anwendungsbezogene Sprachen integrieren typische Aufgaben als aufrufbare Befehle.

Entwurfsphilosophie

Für die oben genannten Zwecke hat jede Sprache meist eine besondere Philosophie entwickelt. Es wird der eine oder andere Aspekt besonders betont. Mehr Datenstrukturen oder Freiheit in der Notation oder Raffinesse, was Zeigerstrukturen angeht.

Die meisten Sprachen bieten eine gute Funktionalität, fordern aber auch ein hohes Maß an Disziplin bezüglich Fehlerfreiheit. Programmiersprachen sind nicht fehlertolerant, was durch Hilfen aber abgemildert ist. Einige wenige Sprachen bieten große gestalterische Freiheiten bis hin zum sich selbst verändernden Programm: dazu gehört Maschinensprache und auch LISP.

Die Entwicklung von Programmiersprachen selbst ist eine Aufgabe der Informatik. Die syntaktische Definition einer Sprache wird meist in der formalen Notation Backus-Naur-Form sowie Kontextbedingungen angegeben. Eine vollständige Spezifikation einer Programmiersprache in einem einzigen Kalkül ist Forschungsgebiet und erst ansatzweise gelungen.

Geschichte

Anfänge

Erste Arbeiten stammen bereits von Lady Ada, Countess of Lovelace (1815-1852, Assistentin von Charles Babbage), die als erste das Prinzip der variablen Programmierbarkeit erkannte. Dies entwickelte sich erst spürbar weiter mit Entwicklung der ersten elektronischen Rechenmaschinen und der Verwendung der booleschen Algebra.

Meilensteine sind etwa 1937 Patente von Konrad Zuse, die die Architektur beschreiben, die später als Von-Neumann-Maschine bekannt wird sowie 1945 die Fertigstellung von Plankalkül durch Zuse. Die ersten praktisch eingesetzten Programmiersprachen sind FORTRAN (1954, John Backus), COBOL (1959, Grace Hopper), LISP (1959, McCarthy), ALGOL-60 (1960, Strukturierte Programmierung)) und weitere (siehe Tabelle).

Darauf aufbauend wurde eine riesige Zahl von Progammiersprachen entwickelt, von denen aber die meisten nur Nischenprodukte sind. Es gibt gewissermaßen Sprachfamilien, z.B. C-artige Sprachen, die letztlich auf Algol zurückgehen. In neueren Generationen von spezifischen Sprachen werden erfolgreiche neue Konzepte von anderen Sprachen eingebaut. Die aktuelle Fortran-Vairante ist im Vergleichz zur Urvariante stark gewandelt.

Eine wichtige Rolle spielen dabei auch die Standardisierungs-Organisationen (ISO). Die vorgenannten Sprachen existieren bis heute.

Programmiersprachen werden geschichtlich auch in verschiedene Generationen eingeteilt:

1. Generation: Maschinensprachen Befehle werden direkt in einer Maschinensprache notiert, das heißt als Folge von Zahlencodes. Da sich der Befehlssatz von Rechner mit unterschiedlichen Prozessoren im allgemeinen unterscheidet, sind in Maschinensprache geschriebene Programme nur sehr schwer übertragbar. Die direkte Programmierung in einer Maschinensprache wird heute kaum noch verwendet.

Einige Programmiersysteme für höhere Programmiersprachen gestatten es, Maschinenbefehle in den Quelltext zu integrieren. Die Anwendung beschränkt sich dann auf solche Fälle, in denen es aus funktionalen oder Effektivitätsgründen unumgänglich oder nützlich ist, maschinennah zu programmieren.

2. Generation: Assembler Anstelle von Zahlencodes wird mit Hilfe von symbolischen Bezeichnern (Mnemonic) codiert. Eine Assembleranweisung wird in genau einen Maschinenbefehl umgesetzt. Auch Assemblerprogramme sind deshalb im allgemeinen an einen bestimmten Prozessortyp gebunden. Makroassembler gestatten die Bildung von parametrisierbaren Befehlsgruppen. Eine Makroanweisung wird im allgemeinen in mehr als einen Maschinenbefehl umgesetzt.

Der Anteil der Assemblerprogrammierung ist im Sinken. Der Möglichkeit der Erstellung effektiver Programme steht die erschwerte Wartbarkeit von Assemblerprogrammen gegenüber. Maschinennahe Programmierung - die Domäne von Assembler - kann heute überwiegend durch höhere Programmiersprachen abgedeckt werden.

Einige Programmiersysteme für höhere Programmiersprachen gestatten es, Assemblerbefehle in den Quelltext zu integrieren. Die Anwendung kann sich dann auf die Situationen beschränken, in denen es aus funktionalen oder Effektivitätsgründen notwendig oder nützlich ist, maschinennah zu programmieren.

3. Generation: höhere Programmiersprachen (high level language) Sprachen der 3. Generation unterstützen unmittelbar die Notation von Algorithmen, sie sind weitgehend anwendungsneutral und maschinenunabhängig.

Erste höhere Programmiersprachen entstanden ab Mitte der fünfziger Jahre (FORTRAN, COBOL, ALGOL-60). Weitere Sprachen dieser Generation sind zum Beispiel PASCAL, MODULA-2, PL1, C, ADA, BASIC, SIMULA.

4. Generation: Fourth Generation Language (4GL) Sprachen der 4. Generation sind anwendungsbezogen (applikative Sprachen). Sie stellen i.a. die wichtigsten Gestaltungsmittel von Sprachen der 3. Generation zur Verfügung, zusätzlich jedoch Sprachmittel zur Auslösung von relativ komplexen, anwendungsbezogenen Operationen, beispielsweise zum Zugriff auf Datenbanken und zur Gestaltung von Benutzeroberflächen (SQL).

5. Generation: (Very High Level Language, VHLL) Sprachen der 5.Generation gestatten das Beschreiben von Sachverhalten und Problemen. Sie kommen vor allem im Bereich der KI (künstliche Intelligenz) zum Einsatz. Die Wahl des Problemlösungsweges kann (entsprechend dem Sprachkonzept) dem jeweiligen System (weitgehend) überlassen werden.

Bekanntestes Beispiel für eine Sprache der 5. Generation ist PROLOG.

00-Generation: Objektorientierte Programmiersprachen Objektorientierte Programmiersprachen passen nicht in das bisherige Generationen-Modell. Sie werden daher in der Literatur oft als "00-Generation" bezeichnet.

Kurze Zeittafel

~1840 --'Erstes Programm' (Ada Lovelace)
1936 -- Die natürlichen Zahlen   (Turing, Gödel, Kleene et al., siehe Gödelnummerierung, Standardnummerierung, Berechenbarkeit)
1947 -- Plankalkül (Konrad Zuse)
1954 -- FORTRAN (John Backus)
1958 -- ALGOL
1959 -- LISP (McCarthy)
1960 -- COBOL (Grace Hopper)
1960 -- BASIC (Kemmeny, Kurtz)
1962 -- APL
1962 -- SNOBOL
1964 -- PL/1 (Heinz Zemanek)
1965 -- Simula
1968 -- Logo (Seymour Papert)
1969 -- BCPL
1970 -- Forth
1970 -- Icon
1971 -- Pascal (Niklaus Wirth, Kathleen Jensen)
1971 -- sh
1972 -- C (Brian W. Kernighan, Dennis Ritchie) (Vorläufer B durch Ken Thompson)
1973 -- ML
1975 -- Prolog (Colmerauer et. al.)
1978 -- Modula-2
1978 -- awk
1980 -- Smalltalk
1980 -- Ada
1983 -- Objective C (ObjC) (Brad Cox)
1983 -- C++ (Bjarne Stroustrup)
1985 -- Eiffel (Bertrand Meyer)
1987 -- Perl (Larry Wall)
1988 -- Erlang (Joe Armstrong et al.)
1988 -- Tcl (John Ousterhout)
1991 -- Python (Guido van Rossum)
1993 -- Ruby
1995 -- Delphi (auf Pascal basierende visuelle Entwicklungsumgebung, Borland)
1995 -- Java (Sun Microsystems)
1997 -- PHP (Rasmus Lerdorf), JavaScript
2001 -- C# (Microsoft im Rahmen von .NET)
2003 -- Delphi.NET (erste Drittanbietersprache von Borland für .NET)

Siehe auch die ausführliche Zeittafel der Programmiersprachen.

Die Geschichte der Programmierung (vor allem die Entwicklung von Unix) hat dazu geführt, dass sich imperative Sprachen (allen voran C mit der Erweiterung C++) für allgemeine Anwendungen durchgesetzt haben. Große Bedeutung erlangt hat das "objektorientierte" programmieren, das Daten-, Prozedur- und Referenzaspekte in dem einzigen Konzept des Objekts vereinigt.

Aktuelle Entwicklungen

Neuere integrierte, visuelle Entwicklungsumgebungen haben deutliche Fortschritte gebracht, was Aufwand an Zeit, Kosten (und Nerven) angeht. Bedienoberflächen lassen sich meist visuell gestalten, Codefragmente sind per Klick direkt erreichbar. Dokumentation zu anderen Programmteilen und Bibliotheken ist direkt einsehbar, meist gibt es sogar "lookup" Funktionalität, die noch während des Schreibens herausfindet, welche Symbole an dieser Stelle erlaubt sind und entsprechende Vorschläge macht.

Neben der mittlerweile etablierten objektorientierten Programmierung ist die Model Driven Architecture (MDA) ein weiterer Ansatz zur Verbesserung der Software-Entwicklung, in der Programme aus, meist visuellen, Modellen generiert werden.

Diese Techniken markieren gleichzeitig den Übergang von einer eher 'handwerklichen', individuellen 'Kunst' zu einem industriell organisierten Prozess.

Die Tatsache, dass jedoch irgendein Programmierer irgendwann ein Programm in einer Turing-Vollständigen Programmiersprache schreiben muss, hat sich trotz aller Industrialisierung nicht geändert und ist weiterhin ein Bestandteil des Selbstverständnisses vieler Programmierer.

Besondere Ausprägungen

Im Folgenden werden diverse Klassen von Programmiersprachen, unterteilt nach Anwendungsbereich oder zugrundeliegendem Paradigma, vorgestellt.

Maschinensprache

Maschinensprache ist der direkt auf einem Prozessor ausführbare Code und in diesem Sinne keine Programmiersprache. Jede Prozessorarchitektur hat eine eigene, unterschiedliche Maschinensprache.

Assemblersprachen

Assemblersprachen gehören immer direkt zu ihrer entsprechenden Maschinensprache. Die Maschinenbefehle werden jedoch in einer menschenlesbaren Form geschrieben. Weiter wird mittels Symbolen adressiert, die absoluten Adressen werden vom Assembler errechnet. Auch können symbolische Konstanten benutzt und Makros, die häufig wiederkehrende Befehlsfolgen repräsentieren, definiert werden.

LISP verwendet als konzeptionelle Hauptstruktur Listen. Auch das Programm ist eine Liste von Befehlen, die andere Listen verändern. FORTH verwendet als konzeptionelle Hauptstruktur Stacks und Stackoperationen.

Prolog orientiert sich in der Beschreibung der Programme an der Prädikatenlogik. Logikorientierte Programmiersprachen haben ungewöhnliche Eigenschaften (Backtracking, Unifikationsalgorithmen), die sie für gewisse Arten von Problemen vorherbestimmt, die mit anderen Programmiersprachen nur sehr schwer zu lösen sind.

AWK, BASIC, C, COBOL, FORTRAN, Pascal, PL/1, REXX sind Vertreter der prozeduralen Familie, in der Anweisungen hintereinander abgearbeitet werden.

Smalltalk, Eiffel, Modula-3, C++, Objective-C, D, Java, C#, ObjectPascal, Oberon, Ruby und XBase++ sind objektorientierte Sprachen. In objektorientierten Programmiersprachen werden Datenstrukturen und Methoden, die auf diesen Datenstrukturen operieren, in Klassen gekapselt.

LISP (Hybridsprache), Logo, ML, Scheme, Haskell

Bei regelbasierten Sprachen wie OPS-5 oder Prolog werden Regeln gegen eine Datenmenge auf ihre Instanziierbarkeit geprüft. Aus allen Regelinstanziierungen wird eine (mehrere, alle) ausgewählt und die zur Regel gehörenden Anweisungen werden ausgeführt.

Die Programmierung wird erleichtert, da zwischen

  • Eigenschaften von Objekten,
  • der Lage von Objekten auf dem Bildschirm und
  • dem eigentlichen Code

unterschieden wird. Beispiele: Visual Basic, Visual C++, Delphi (Sprache: Pascal), Kylix (Delphi/C++ für Linux), KDevelop (Sprache: C++), Microsoft Visual Studio. Ein sehr mächtiges Beispiel ist auch das Open Source-Projekt Eclipse, das sich konzeptionell nicht auf eine bestimmte Sprache festlegt.

Daneben wird der Begriff Visuelle Programmierung auch für Programmiersprachen benutzt, bei denen graphisch in Diagrammen (in der Regel Statechart-Dialekte) programmiert wird. Beispiele hierfür: Esterel (mit Esterel Studio, http://www.esterel-technologies.com ), Statemate (http://www.ilogix.com/statemate/statemate.cfm ), Matlab mit Simulink, LabView.

Sprachen, die als anspruchsvolle Scherze gedacht sind: Beispiele: Brainfuck, Ook, False, Shakespeare

Anwendungsbezogene Sprachen und Systeme

Datenbanksysteme: dBase, Clipper, Paradox. EASY

Programmieren

Programmieren ist eine anspruchsvolle Tätigkeit und erfordert Disziplin, Ausdauer, abstraktes Denkvermögen, Kreativität und hohe Lernbereitschaft. Unterschiedlichste Aufgaben müssen in die Symbole der Programmiersprache transferiert werden.

Das Programmieren als dieses reine Kodieren ist nur ein Teil der Tätigkeit eines guten Programmierers, der zum gesamten Softwareentwicklungsprozess beitragen können sollte: Analyse, Entwurf, Prototyping, Realisation, Testen, Einführung, Dokumentation, Konsolidierung. Erheblichen Aufwand nimmt auch das so genannte Debuggen ein, also die Diagnose oder Fehlersuche.

Berufe: Softwareentwickler, Anwendungsentwickler, Systemprogrammierer, Organisationsprogrammmierer, ...

Siehe auch:

Liste der Programmiersprachen, Algorithmus, Datenbanksysteme, Informatik, Softwaretechnik, Hallo Welt, Quine, Wikibooks: Programmieren