FH München, FB 07 Informatik/Mathematik
Inhaltsverzeichnis dieser Seite:
Es kommt nur darauf an, daß es irgendeinen Weg durch das Syntaxdiagramm gibt, der zum Text paßt. Es spielt keine Rolle, welche Abzweigungen dabei genommen werden.
+230859 entspricht z.B. dem Syntaxdiagramm,
die Zeichenfolge 3.141592 dagegen nicht.
Seltener verwendet wird die ursprüngliche, einfache "BNF" (= "Backus-Naur Form"). Dieser fehlen gegenüber der EBNF ein paar bequeme, abkürzende Schreibweisen; mit beiden Formen lassen sich aber dieselben Grammatiken wiedergeben.
Im Gegensatz zu Syntaxdiagrammen erlaubt eine EBNF nicht alle Verbindungen und erzwingt damit strengere Auflagen. Das Ergebnis sind zwangsläufig besser strukturierte Grammatiken.
Demnach kann zwar jede EBNF als Syntaxdiagramm formuliert werden, das Umgekehrte gilt nicht in jedem Fall.
::= gesetzt.
Das Ende einer Produktion wird mit einem Punkt gekennzeichnet, also
schematisch:
linke Seite ::= rechte Seite .
integer ::= [sign] digit {digit}.
In diesem Zusammenhang ist eine alternative Notation üblich, die in der EBNF eigentlich nicht vorgesehen ist: Hinter die geschweifte Klammer kann auch ein Plus-Zeichen gesetzt werden, um ein- oder mehrmalige Wiederholung auszudrücken. Das obige Beispiel würde dann kürzer lauten:
integer ::= [sign] {digit}+.
Um die ursprüngliche, beliebige Wiederholung davon klar abzuheben,
wird diese dann ausdrücklich mit einem Stern nach der geschweiften Klammer markiert.
Die folgende Schreibweise ist umständlicher,
aber inhaltlich gleichwertig mit der vorhergehenden:
integer ::= [sign] digit {digit}*.
integer-Konstante in "C"-Syntax:
integer ::= [sign] digit {digit}.
sign ::= "+" | "-".
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
integer ::= [sign]
("0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9")
{digit}.
sign ::= "+" | "-".
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
ebnf ::= {production}.
production ::= leftside "::=" rightside ".".
leftside ::= nonterminal.
rightside ::= alternative {"|" alternative}.
alternative ::= {sequence}.
sequence ::= "{" rightside "}" |
"[" rightside "]" |
"(" rightside ")" |
symbol.
symbol ::= terminal | nonterminal.
terminal ::= doublequote text doublequote.
nonterminal ::= identifier.
identifier ::= letter {letter | digit}.
Die folgenden Nichtterminale sind nicht mehr aufgelöst:
letter steht für einen Buchstaben;
digit steht für eine Ziffer;
text steht für eine Folge von beliebigen Zeichen außer Gänsefüßchen;
doublequote steht für das Terminal mit Gänsefüßchen als Inhalt;
filespec ::= [root] {dirspec} name.
root ::= "/".
dirspec ::= (name | "." | "..") "/".
name ::= {char}+.
char ::= letter | digit | "_" | "+" | "-" | ":".
Anmerkung:
Die konkrete Auswahl der in Dateinamen zulässigen Sonderzeichen
hängt von der Shell und deren Konfiguration ab.
"C" hat sich ursprünglich einen Namen als Sprache zur Systemprogrammierung gemacht (Implementierung von "Unix"). Ein erster Meilenstein in der Entwicklung von "C" war 1983 die Veröffentlichung des Buches "The C Programming Language" (Kernighan/Ritchie), das für Jahre eine Art Pseudo-Standard festlegte. Mit "ANSI-C" wurde die Sprache 1988 offiziell standardisiert, so daß heute praktisch alle "C"-Compiler den gleichen Sprachumfang beherrschen.
"C" ist seiner Art nach eine Programmiersprache aus der Ära der "strukturierten Programmierung", wie auch Pascal, Modula, u.a..
"C++" wurde als Nachfolger von "C" geschaffen mit dem erklärten Ziel objektorientierte Programmierung zu erschließen. Wie bei "C" machte "C++" einige Entwicklungsstufen durch, von denen eine erste 1990 mit der Veröffentlichung des Buches "The Annotated C++ Reference Manual" (Ellis/Stroustrup) ("the ARM") markiert wird. 1995 wurde der Draft-Standard für "ANSI-C++" verabschiedet. Dieses Dokument ist zwar noch keine endgültige Sprachnorm, tiefgreifende Änderungen sind aber nicht mehr zu erwarten.
"C++" ist eine objektorientierte Programmiersprache, die weitgehend aufwärtskompatibel zu "C" bleibt (d.h. daß "C"-Programme mit wenigen Modifikationen auch von "C++"-Compilern akzeptiert werden).
Mit "I/O", homogenen und heterogenen zusammengesetzten Datenstrukturen (Strings, Vektoren und Strukturen bzw. Klassen) werden aber Themen erreicht, die sich in "C++" wesentlich sicherer und eleganter behandeln lassen. Ab hier orientiert sich die Vorlesung an "C++".
Erst in der Nachfolgeveranstaltung "Programmieren II" kehren wir zu "C" zurück. Zu diesem Zeitpunkt haben Sie die notwendigen Vorkenntnisse, um die inneren Abläufe von "C" nachvollziehen zu können. Diese sind letztlich auch für "C++" maßgeblich, werden dort aber gut abgeschirmt.
Alle Ausführungen beziehen sich zunächst auf ANSI-C.
Sofern Spracheigenschaften besprochen werden,
die nur in ANSI-C++
(aber nicht in ANSI-C) erlaubt sind,
weist diese Markierung
darauf hin.
Während die Syntax der Sprachen nicht allzu schwer zu erlernen ist, sind die Kenntnis der Semantik und Pragmatik von C(++) eines der wesentlichen Lernziele dieser Vorlesung.
Die Syntax von C(++) läßt sich in einer Grammatik festschreiben, die z.B. in der EBNF verfaßt sein kann.
Die Semantik ist meist umgangssprachlich beschrieben und macht den größten Teil des Inhaltes der weitverbreiteten Literatur aus. Ein Beispiel für die Semantik ist z.B. die Forderung, daß jeder Bezeichner definiert bzw. deklariert sein muß, bevor er verwendet werden kann.
Die Pragmatik der Sprachen findet sich oft in der Dokumentation von einzelnen
Implementierungen,
wie z.B. Compiler-Handbüchern oder "Release Notes".
Ein Beispiel für die Pragmatik ist die Begrenzung des Wertebereiches des Datentyps int,
die von bestimmten Rechnerarchitekturen auferlegt wird.
Diese Übersetzung ist bei Sprachen wie "C" oder "C++" verhältnismäßig aufwendig. Sie läßt sich nicht mit vertretbarem Aufwand von Hand erledigen. Statt dessen werden die Sprachen mit spezialisierten Hilfsprogrammen, sogenannten "Übersetzern" oder "Compilern", automatisch in Maschinensprache transformiert. Compiler sind selbst sehr komplexe Programme, deren Konstruktion ein eigenes Lehrgebiet ausmacht.
Programm-Text wird "Quelltext" genannt (engl. source) und ist, für sich gesehen, nichts anderes als lesbarer Klartext. Das vom Compiler aus dem Quelltext automatisch produzierte und dann ausführbare Maschinenprogramm heißt "übersetztes Programm" oder "Binärprogramm" (engl. binary oder executable). Der Begriff des "Programms" wird sowohl für Quelltext, wie auch für übersetzte Programme benutzt. Ein Binärprogramm ist nicht mehr lesbar und i.d.R. wesentlich umfangreicher als der ursprüngliche Quelltext. Soweit der Compiler fehlerfrei arbeitet, verhält sich das Binärprogramm genau so, wie es im Quelltext beschrieben steht. (Daß sich das nicht immer mit den Vorstellungen des Programmierers deckt, ist eine andere Sache.)
Compiler erwarten kompletten und korrekten Quelltext, bevor sie ein Binärprogramm produzieren können. Bei umfangreichen Quelltexten wird die für die Übersetzung aufzuwendende Zeit spürbar, so daß "Interpreter" entwickelt wurden. Interpreter verlangen keinen kompletten Quelltext, sondern lesen Quelltext Schritt für Schritt und führen jeden gelesenen Schritt sofort aus. Damit kann z.B. der Anfang eines Programms schon ausprobiert werden, bevor das Ende überhaupt fertig ist. Der Preis für diese gewonnene Flexibilität ist gegenüber einem Binärprogramm reduzierte Ablaufgeschwindigkeit, weil Compiler Informationen aus dem gesamten Programm für Optimierungen verwenden können. Darüber hinaus eignen sich nicht alle Programmiersprachen (zu ihnen zählen auch "C" und "C++") von ihrem Aufbau her für schrittweise Ausführung, weil sich z.B. Programmteile an weit auseinander liegenden Stellen gegenseitig beeinflußen.
Diese Skizze zeigt schematisch die Entwicklung eines Programmes:
(Schraffierte Kästen sind ausführbare Programme,
Kästen ohne Schraffur sind Daten;)
Zwischenräume werden benutzt, um Quelltext optisch (d.h. für den menschlichen Leser übersichtlich) zu gliedern. Das Gesamt-Arrangement (Wort-Abstand, Einrückung, Leerzeilen) wird unter dem Begriff "Layout" zusammengefaßt.
Notwendig aus der Sicht von C(++) ist Zwischenraum nur an wenigen, seltenen Stellen. Überspitzt formuliert: Der Compiler ignoriert das Layout (weitgehend).
Es spielt keine Rolle, wie viel Zwischenraum eingeschoben wird. Ein einzelnes Leerzeichen ist gleichwertig mit eintausend Leerzeilen.
Die Entwicklung eines persönlichen Programmierstils schlägt sich zu einem guten Teil in einem einheitlichen Layout nieder. Dabei wird allgemein anerkannt, daß die konkrete Art des persönlichen Stils eine untergeordnete Rolle spielt, wenn er nur konsequent durchgehalten wird.
Insbesondere mögen deutsche Umlaute oder etwa das Paragraphenzeichen zwar von einem Compiler "geschluckt" werden, der nächste Compiler kann aber vollkommen unberechenbar darauf reagieren.
Kommentare können auf zwei Arten eingefügt werden:
/* eingeleitet und mit */
abgeschlossen.
Art und Umfang des eingebetteten Textes sind beliebig,
nur kann die Zeichenfolge */ nicht
Inhalt des Kommentartextes sein.
Ein Beispiel:
/* dieser Text wird vom Compiler ignoriert. */
Derartige Kommentare können nicht geschachtelt werden, wie etwa im folgenden (fehlerhaften) Versuch:
/* Anfang eines Kommentars /* Fortsetzung */ kein Kommentartext mehr! */
Kurze Passagen
// begonnen
und enden automatisch mit der Zeile.
(Dies ist einer der wenigen Fälle,
in denen ein bestimmtes Zwischenraumzeichen signifikant ist,
d.h. Einfluß auf die Bedeutung des Programms trägt.)
Ein Beispiel:
int a; // Zweck dieser Variablen
Beide Kommentararten dürfen gemischt werden. Die Art des Kommentar-Anfangs regelt die Art des Endes.
Über Regeln für sinnvolle Inhalt von Kommentaren gibt es lange und fruchtlose Debatten. Ich persönlich möchte mich den folgenden Vorschlägen anschließen:
Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the working is obvious, and it's a waste of time to explain badly written code.(Auszug aus:Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, you should probably go back to chapter 4 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does it.
Documentation/CodingStyle,
Dokumentation der Linux-Kernelquellen)
Letzte Änderung: Fri Oct 11 11:46:52 1996