package saxblatt;
import static org.junit.Assert.*;
import java.io.StringReader;
import javax.xml.parsers.SAXParserFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.xml.sax.InputSource;
import org.xml.sax.helpers.DefaultHandler;
public class GetCodeTest{
@Rule
public Timeout globalTimeout = Timeout.millis(500);
GetCode handler;
String skript1 = "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Einführung in die Programmierung mit C\n"
+ "(Entwurf)\n"
+ "(Tutorenversion)\n"
+ "\n"
+ "WS 06/07\n"
+ "Sven Eric Panitz\n"
+ "FH Wiesbaden \n"
+ "\n"
+ "\n"
+ "Die vorliegende Fassung des Skriptes ist ein Entwurf\n"
+ "für die Vorlesung des Wintersemesters und wird im Laufe der\n"
+ "Vorlesung erst seine endgültige Form finden. Kapitel können dabei\n"
+ "umgestellt, vollkommen revidiert werden oder gar ganz wegfallen. \n"
+ "\n"
+ "Dieses Skript entsteht begleitend zur Vorlesung des\n"
+ "WS 06/07. \n"
+ "\n"
+ "Naturgemäß ist es in Aufbau und Qualität nicht mit einem Buch\n"
+ "vergleichbar. Flüchtigkeits- und Tippfehler werden sich im Eifer\n"
+ "des Gefechtes nicht vermeiden lassen und wahrscheinlich in nicht\n"
+ "geringem Maße auftreten. Ich bin natürlich stets dankbar, wenn\n"
+ "ich auf solche aufmerksam gemacht werde und diese korrigieren\n"
+ "kann.\n"
+ "\n"
+ "Der Quelltext dieses Skripts ist eine XML-Datei, die durch eine XQuery \n"
+ "in eine -Datei transformiert und für die schließlich eine\n"
+ "pdf-Datei und eine postscript-Datei\n"
+ "erzeugt wird. \n"
+ "Beispielprogramme werden direkt aus dem Skriptquelltext extrahiert und sind\n"
+ "auf der Webseite herunterzuladen.\n"
+ "Von diesem Skript gibt es zwei Versionen: eine Studentenversion und eine\n"
+ "Tutorenversion mit Lösungen der Aufgaben. Sie halten die Tutorenversion in\n"
+ "Händen. Sie zeichnet sich durch zusätzliche Informationen aus. Textteile die\n"
+ "nicht in der Studentenversion des Skriptes stehen, sind jeweils zu Beginn und\n"
+ "Ende mit einem schwarzen Haken am Rand markiert.\n"
+ "Beide Versionen des Skriptes werden aus ein und\n"
+ "derselben XML-Quelltextdatei generiert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit dem Begriff Programmierung wird zunächst \n"
+ "die eigentliche Codierung eines Programms assoziiert. \n"
+ "Eine genauerer Blick offenbart jedoch, daß dieses nur ein kleiner \n"
+ "Teil von vielen recht unterschiedlichen Schritten ist, die zur\n"
+ "Erstellung von Software notwendig sind:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Spezifikation: Bevor eine Programmieraufgabe\n"
+ "bewerkstelligt werden kann, muß das zu lösende\n"
+ "Problem spezifiziert werden. Dieses kann informell durch eine\n"
+ "natürlichsprachliche Beschreibung bis hin zu mathematisch\n"
+ "beschriebenen Funktionen geschehen. Gegen die Spezifikation \n"
+ "wird programmiert. Sie beschreibt das gewünschte Verhalten\n"
+ "des zu erstellenden Programms. \n"
+ "\n"
+ "Modellieren: Bevor es an die eigentliche Codierung\n"
+ "geht, wird in der Regel die Struktur des Programms modelliert.\n"
+ "Auch dieses kann in unterschiedlichen Detaillierungsgraden geschehen.\n"
+ "Manchmal reichen Karteikarten als hilfreiches Mittel aus,\n"
+ "andernfalls empfiehlt sich eine umfangreiche Modellierung \n"
+ "mit Hilfe rechnergestützter Werkzeuge. Für die objektorientierte\n"
+ "Programmierung hat sich UML als eine geeignete Modellierungssprache\n"
+ "durchgesetzt. In ihr lassen sich Klassendiagramme, Klassenhierarchien\n"
+ "und Abhängigkeiten graphisch darstellen. Mit bestimmten Werkzeugen\n"
+ "wie Together oder Rational Rose läßt sich direkt \n"
+ "für eine UML-Modellierung Programmtext generieren.\n"
+ "\n"
+ "Codieren: Die eigentliche Codierung ist in der\n"
+ "Regel der einzige Schritt, der direkt Code in der gewünschten\n"
+ "Programmiersprache von Hand erzeugt. Alle anderen Schritte \n"
+ "der Programmierung\n"
+ "sind mehr oder weniger unabhängig von der zugrundeliegenden\n"
+ "Programmiersprache.\n"
+ "\n"
+ "Für die Codierung empfiehlt es sich, Konventionen zu verabreden, wie \n"
+ "der Code geschrieben wird, was für Bezeichner benutzt werden, in\n"
+ "welcher Weise der Programmtext eingerückt wird. Entwicklungsabteilungen\n"
+ "haben zumeist schriftlich verbindlich festgeschriebene Richtlinien\n"
+ "für den Programmierstil. Dieses erleichtert, den Code der Kollegen im\n"
+ "Projekt schnell zu verstehen.\n"
+ "\n"
+ "\n"
+ "Testen: Beim Testen sind generell zu unterscheiden:\n"
+ "\n"
+ "Entwicklertests: Diese werden von den Entwicklern\n"
+ "während der Programmierung selbst geschrieben, um einzelne Programmteile\n"
+ "(MethodenMethode, \n"
+ "Funktionen) separat zu testen. Es gibt eine Schule, die\n"
+ "propagiert, Entwicklertests vor dem Code zu \n"
+ "schreiben (test first). Die Tests dienen in diesem Fall als\n"
+ "kleine Spezifikationen.\n"
+ "\n"
+ "Qualitätssicherung: In der Qualitätssicherung werden\n"
+ "die fertigen Programme gegen ihre Spezifikation getestet (black\n"
+ "box tests). Hierzu\n"
+ "werden in der Regel automatisierte Testläufe geschrieben. \n"
+ "Die Qualitätssicherung ist personell von der Entwicklung\n"
+ "getrennt. Es kann\n"
+ "in der Praxis durchaus vorkommen, daß die Qualitätsabteilung \n"
+ "mehr Mitarbeiter hat als die Entwicklungsabteilung.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Optimieren: Sollten sich bei Tests oder in der \n"
+ "Praxis Performanzprobleme zeigen, sei es durch zu hohen \n"
+ "Speicherverbrauch als auch durch zu lange Ausführungszeiten, so wird\n"
+ "versucht, ein Programm zu optimieren. Hierzu bedient man sich\n"
+ "spezieller Werkzeuge (profiler), die für einen Programmdurchlauf\n"
+ "ein Raum- und Zeitprofil erstellen. In diesem Profil können \n"
+ "Programmteile, die besonders häufig durchlaufen werden, oder Objekte,\n"
+ "die im großen Maße Speicher belegen, identifiziert werden. Mit diesen \n"
+ "Informationen lassen sich gezielt inperformante Programmteile \n"
+ "optimieren.\n"
+ "\n"
+ "Verifizieren: Eine formale Verifikation eines Programms\n"
+ "ist ein mathematischer Beweis der Korrektheit bezüglich der\n"
+ "Spezifikation. Das setzt natürlich voraus, daß die Spezifikation\n"
+ "auch formal vorliegt. Man unterscheidet:\n"
+ "\n"
+ "partielle Korrektheit: wenn das Programm für\n"
+ "eine bestimte Eingabe ein Ergebnis liefert, dann ist dieses bezüglich\n"
+ "der Spezifikation korrekt.\n"
+ "totale Korrektheit: Das Programm ist partiell korrekt\n"
+ "und terminiert für jede Eingabe, d.h.liefert immer nach endlich\n"
+ "langer Zeit ein Ergebnis.\n"
+ "\n"
+ "Eine formale Verifikation ist notorisch schwierig und allgemein nicht\n"
+ "automatisch durchführbar. In der Praxis werden nur in ganz speziellen\n"
+ "kritischen Anwendungen formale Verifikationen durchgeführt, z.B.bei\n"
+ "Steu\\-er\\-un\\-gen gefahrenträchtiger Maschinen, so daß Menschenleben von\n"
+ "der Korrektheit eines Programms abhängen können. \n"
+ "\n"
+ "\n"
+ "Wartung/Pflege: den größten Teil seiner Zeit verbringt\n"
+ "ein Programmierer nicht mit der Entwicklung neuer\n"
+ "\n"
+ "Software, sondern mit der Wartung bestehender Software. Hierzu gehören\n"
+ "die Anpassung des Programms an neue Versionen benutzter Bibliotheken\n"
+ "\n"
+ "oder des Betriebssystems, auf dem das Programm läuft, \n"
+ "sowie die Korrektur von Fehlern.\n"
+ "\n"
+ "Debuggen: Bei einem Programm ist immer damit\n"
+ "zu rechnen, daß es Fehler enthält. Diese Fehler werden im besten\n"
+ "Fall von der Qualitätssicherung entdeckt, im schlechteren Fall treten \n"
+ "sie beim Kunden auf. Um Fehler im Programmtext zu finden,\n"
+ "gibt es Werkzeuge, die ein schrittweises Ausführen des Programms\n"
+ "ermöglichen (debugger). Dabei lassen sich die Werte, die in\n"
+ "bestimmten Speicherzellen stehen, auslesen und auf diese Weise der Fehler\n"
+ "finden. \n"
+ " \n"
+ "\n"
+ "Internationalisieren (I18N)I18N ist eine Abkürzung\n"
+ "für das Wort internationalization, das mit einem i beginnt, mit\n"
+ "einem n endet und dazwischen 18 Buchstaben hat.: Softwarefirmen\n"
+ "wollen möglichst\n"
+ "viel Geld mit ihrer Software verdienen und streben deshalb an, ihre\n"
+ "Programme möglichst weltweit zu vertreiben. Hierzu muß gewährleistet\n"
+ "sein, daß das Programm auf weltweit allen Plattformen läuft und\n"
+ "mit verschiedenen Schriften und Textcodierungen umgehen kann. Das\n"
+ "Programm sollte ebenso wie mit lateinischer Schrift auch mit Dokumenten\n"
+ "in anderen Schriften umgehen können. Fremdländische Akzente und \n"
+ "deutsche Umlaute sollten bearbeitbar sein. Aber auch unterschiedliche\n"
+ "Tastaturbelegungen bis hin zu unterschiedlichen Schreibrichtungen sollten\n"
+ "unterstützt werden. \n"
+ "\n"
+ "Die Internationalisierung ist ein weites Feld, und wenn nicht am Anfang\n"
+ "der Programmerstellung hierauf Rücksicht genommen wird, so ist es schwer,\n"
+ "nachträglich das Programm zu internationalisieren.\n"
+ "\n"
+ "\n"
+ "Lokalisieren (L12N): Ebenso wie die \n"
+ "Internationalisierung beschäftigt\n"
+ "sich die Lokalisierung damit, daß ein Programm in anderen Ländern\n"
+ "eingesetzt werden kann. Beschäftigt sich die Internationalisierung damit,\n"
+ "daß fremde Dokumente bearbeitet werden können, versucht die Lokalisierung,\n"
+ "das Programm komplett für die fremde Sprache zu übersetzen. Hierzu \n"
+ "gehören Menueinträge in der fremden Sprache, Beschriftungen der\n"
+ "Schaltflächen oder auch Fehlermeldungen in fremder Sprache und Schrift.\n"
+ "Insbesondere haben verschiedene Schriften unterschiedlichen\n"
+ "Platzbedarf; auch das ist beim Erstellen der Programmoberfläche\n"
+ "zu berücksichtigen.\n"
+ "\n"
+ "\n"
+ "Portieren: Oft wird es nötig, ein Programm auf eine andere\n"
+ "Plattform zu portieren. Ein unter Windows erstelltes Programm soll\n"
+ "z.B.auch auf Unix-Systemen zur Verfügung stehen. \n"
+ "\n"
+ "Dokumentieren: Der Programmtext allein reicht in der Regel\n"
+ "\n"
+ "nicht aus, damit das Programm von Fremden oder \n"
+ "dem Programmierer selbst nach\n"
+ "geraumer Zeit gut verstanden werden kann. Um ein Programm näher\n"
+ "zu erklären, wird im Programmtext Kommentar eingefügt. Kommentare\n"
+ "erklären die benutzten Algorithmen, die Bedeutung bestimmter Datenfelder\n"
+ "oder die Schnittstellen und Benutzung \n"
+ "bestimmter MethodenMethode.\n"
+ "\n"
+ "Es ist zu empfehlen, sich anzugewöhnen, Quelltextdokumentation immer auf\n"
+ "Englisch zu schreiben. Es ist oft nicht abzusehen, wer einmal einen\n"
+ "Programmtext zu sehen bekommt. Vielleicht ein japanischer Kollege,\n"
+ "der das Programm für Japan lokalisiert, oder der irische Kollege, der,\n"
+ "nachdem die Firma mit einer anderen Firma fusionierte, das \n"
+ "Programm auf ein anderes Betriebssystem portiert, oder vielleicht\n"
+ "die englische Werksstudentin, die für ein Jahr in der Firma arbeitet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Frage danach, was ein Programm eigentlich ist, läßt sich \n"
+ "aus verschiedenen Perspektiven recht unterschiedlich beantworten.\n"
+ "Wir haben schon eine ganze Reihe von Programmen geschrieben, so dass zumindest\n"
+ "schon ein erstes intuitives Gefühl dafür, was ein Programm eigentlich ist,\n"
+ "existieren sollte. Tatsächlich lassen sich Computerprogramme unter\n"
+ "unterschiedlichsten Aspekten betrachten. Was ist also eigenbtlich ein\n"
+ "Programm? \n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Textdatei, die durch ein anderes Programm in einen \n"
+ "ausführbaren Maschinencode\n"
+ "übersetzt wird. Dieses andere Programm ist ein Übersetzer, \n"
+ "engl.compiler.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Funktion, die deterministisch für Eingabewerte \n"
+ "einen Ausgabewert berechnet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Ein Satz einer durch eine Grammatik beschriebenen Sprache. Für dieses\n"
+ "syntaktische Konstrukt existiert eine Bedeutung in Form einer\n"
+ "operationale Semantik.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Folge von durch den Computer ausführbaren Befehlen, die den Speicher des\n"
+ "Computers manipulieren.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es gibt mittlerweile mehr Programmiersprachen als \n"
+ "natürliche Sprachen. In Wikipedia existiert eine recht umfangreiche Liste von \n"
+ "Programmiersprachen. Die meisten Sprachen führen\n"
+ "entsprechend nur ein Schattendasein und die Mehrzahl der Programme\n"
+ "konzentriert sich auf einige wenige Sprachen. Programmiersprachen lassen\n"
+ "sich nach den unterschiedlichsten Kriterien klassifizieren. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Im folgenden eine hilfreiche Klassifizierung in fünf verschiedene \n"
+ "Hauptklassen.\n"
+ "\n"
+ "\n"
+ "imperativ (C, Pascal, Fortran, Cobol): das\n"
+ "Hauptkonstrukt dieser Sprachen sind Befehle, die den Speicher\n"
+ "manipulieren. Dieses äußert sich primär durch einen Zuweisungsbefehl, der es\n"
+ "erlaubt, Variablen neue Werte zuzuweisen.\n"
+ "\n"
+ "objektorientiert (Java, C++, C\\#, Eiffel, Smalltalk):\n"
+ "Daten werden in Form von Objekten organisiert. Diese Objekte\n"
+ "bündeln mit den Daten auch die auf diesen Daten anwendbaren\n"
+ "Methoden.\n"
+ "\n"
+ "funktional (Lisp, ML, Haskell, Scheme, Erlang, Clean, F\\#):\n"
+ "Programme werden als mathematische Funktionen verstanden und auch\n"
+ "Funktionen können Daten sein. Dieses Programmierparadigma versucht,\n"
+ "sich möglichst weit von der Architektur des Computers zu lösen.\n"
+ "Veränderbare Speicherzellen gibt es in rein funktionalen Sprachen\n"
+ "nicht und erst recht keine Zuweisungsbefehle.\n"
+ "\n"
+ "Skriptsprachen (Perl, AWK): solche Sprachen sind dazu\n"
+ "entworfen, einfache kleine Programme schnell zu erzeugen. Sie haben\n"
+ "meist kein Typsystem und nur eine begrenzte Zahl an \n"
+ "Strukturierungsmöglichkeiten, oft aber eine mächtige Bibliothek, um\n"
+ "Zeichenketten zu manipulieren.\n"
+ "\n"
+ "logisch (Prolog): aus der KI (künstlichen Intelligenz)\n"
+ "stammen logische Programmiersprachen. Hier wird ein Programm als\n"
+ "logische Formel, für die ein Beweis gesucht wird, verstanden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Der Programmierer schreibt den lesbaren Quelltext seines Programmes.\n"
+ "Um ein Programm auf einem Computer laufen zu lassen, muß es erst \n"
+ "in einen Programmcode übersetzt werden, den der Computer versteht.\n"
+ "Für diesen Schritt gibt es auch unterschiedliche Modelle:\n"
+ "\n"
+ "\n"
+ "kompiliert (C, Cobol, Fortran): in einem Übersetzungsschritt\n"
+ "wird aus dem Quelltext direkt das ausführbare Programm erzeugt, das dann\n"
+ "unabhängig von irgendwelchen Hilfen der Programmiersprache ausgeführt werden\n"
+ "kann.\n"
+ "\n"
+ "\n"
+ "interpretiert (Lisp, Scheme): der Programmtext wird nicht\n"
+ "in eine ausführbare Datei übersetzt, sondern durch einen Interpreter\n"
+ "Stück für Stück anhand des Quelltextes ausgeführt. Hierzu muß stets der\n"
+ "Interpreter zur Verfügung stehen, um das Programmm auszuführen. \n"
+ "Interpretierte Programme sind langsamer in der Ausführung als\n"
+ "übersetzte Programme.\n"
+ "\n"
+ "abstrakte Maschine über byte code (Java, ML):\n"
+ "dieses ist quasi eine Mischform aus den obigen zwei Ausführungsmodellen.\n"
+ "Der Quelltext wird übersetzt in Befehle nicht für einen konkreten\n"
+ "Computer, sondern für eine abstrakte Maschine. Für diese abstrakte Maschine\n"
+ "steht dann ein Interpreter zur Verfügung. Der Vorteil ist, daß durch\n"
+ "die zusätzliche Abstraktionsebene der Übersetzer unabhängig von einer konkreten\n"
+ "Maschine Code erzeugen kann und das Programm auf auf allen Systemen laufen\n"
+ "kann, für die es einen Interpreter der abstrakten Maschine gibt.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es gibt Programmiersprachen, für die sowohl Interpreter als auch Übersetzer\n"
+ "zur Verfügung stehen. In diesem Fall wird der Interpreter gerne zur \n"
+ "Programmentwicklung benutzt und der Übersetzer erst, wenn das Programm\n"
+ "fertig entwickelt ist.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wenn wir bisher tatsächlich Programme übersetzt haben, um eine ausführbare\n"
+ "Datei zu erhalten, haben wir genaugenommen nicht nur den Compiler aktiviert,\n"
+ "sondern auch zwei gute kleine Helfer des Compilers: den Präprozessor und den\n"
+ "Linker. Der Präprozessor bereitet den Quelltext noch ein wenig auf, bevor er\n"
+ "ihn den Compiler gibt, der Linker fügt erst die einzelnen verschiedenen\n"
+ "übersetzten Programmteile, die der Compiler erzeugt hat, zu einer ausführbaren\n"
+ "Datei zusammen. In diesem Kapitel wollen wir ein wenig genaueres Augenmerk auf\n"
+ "diese beiden Freunden nehmen.\n"
+ "\n"
+ "\n"
+ "Betrachtet wir hierzu zunächst ein ganz simples C Programm, in dem gerade mal\n"
+ "eine einfache Funktion geschrieben und anschließend aufgerufen wird.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int getAnswer(){\n"
+ " return 42;\n"
+ "};\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"Die Antwort lautet: %i\n\", getAnswer());\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wir definieren, die parameterlose Funktion getAnswer und rufen sie in der Funktion main auf.\n"
+ "\n"
+ "\n"
+ "In höheren Programmiersprachen spielt die Reihenfolge der Funktionen in einem\n"
+ "Programm keine Rolle. Der Kompilierer hat jeweils bereits die ganze\n"
+ "Quelldatei studiert, bevor er die Rümpfe der Methoden übersetzt. \n"
+ "Wir können versuchen, ob dieses für C auch gilt. Hierzu\n"
+ "vertauschen wir die\n"
+ "Reihenfolge der beiden Funktionen im letzten Programm.\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"Die Antwort lautet: %i\n\",getAnswer());\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "int getAnswer(){\n"
+ " return 42;\n"
+ "};]]>\n"
+ "\n"
+ "Der Versuch dieses Programm zu übersetzen führt zumindest zu einer Warnung:\n"
+ "\n"
+ " gcc -Wall -o ../obj/Answer2.o -c ./Answer2.c\n"
+ "Answer2.c: In function `main':\n"
+ "Answer2.c:4: warning: implicit declaration of function `getAnswer'\n"
+ "\n"
+ "sep@swe10:~/fh/c>]]>\n"
+ "\n"
+ "Offensichtlich können in C Funktionen nur benutzt werden, nachdem sie\n"
+ "vorher im Programm definiert wurden. Diese Einschränkung hat\n"
+ "sicherlich mit den Stand der Compilerbautechnik am Anfang der 70er\n"
+ "Jahre zu tun. Ein Compiler, der nur einmal sequentiell das Programm von\n"
+ "vorne nach hinten durchgeht, läßt sich leichter bauen, als\n"
+ "ein Compiler, der mehrmals über eine Quelldatei gehen muß, um\n"
+ "nacheinander verschiedene Informationen zu sammeln.\n"
+ "\n"
+ "Wollen wir die Reihenfolge der beiden Funktionen, wie wir sie in der\n"
+ "Datei Answer2.c geschrieben haben, beibehalten, so können\n"
+ "wir das in C, indem wir die Signatur der Funktion getAnswer vorwegnehmen. Man kann in C in ein und derselben\n"
+ "Datei die Signatur einer Funktion deklarieren und später in der Datei\n"
+ "diese Funktion erst implementieren.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int getAnswer();\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"Die Antwort lautet: %i\n\", getAnswer());\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "int getAnswer(){\n"
+ " return 42;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "Jetzt übersetzt das Programm wieder fehlerfrei. Mit dieser Technik\n"
+ "simuliert man gewissermaßen, was ein moderner Compiler sieht, wenn er\n"
+ "mehrfach über einen Quelltext geht. Zunächst sieht er die Signaturen aller\n"
+ "Funktionen und erst dann betrachtet er ihre Rümpfe. Sobald die\n"
+ "Signatur einer Funktion bekannt ist, kann die Funktion benutzt werden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Nach dem geraden gelernten, scheint es sinnvoll in einem C Programm\n"
+ "zunächst die Signaturen aller Funktionen zu sammeln und dann die\n"
+ "einzelnen Funktionen zu implementieren. Die Sammlung der Signaturen\n"
+ "ergibt eine schöne Zusammenfassung aller von einem Programm\n"
+ "angebotener Funktionen. Es liegt nahe, diese in eine getrennte Datei\n"
+ "auszulagern. Hierzu bedient man sich in C der sogenannten header-Dateien, die mit der Dateiendung .h abgespeichert werden.\n"
+ "\n"
+ "Für unser letztes Beispiel können wir eine header-Datei \n"
+ "schreiben, in der die Signatur der Funktion getAnswer steht:\n"
+ "\n"
+ "\n"
+ "int getAnswer();]]>\n"
+ "\n"
+ "Statt jetzt in einem Programm diese Signatur am Anfang des Programms\n"
+ "zu schreiben, sagen wir dem Compiler, er soll \n"
+ "die header-Datei mit in das Programm einfügen. Hierzu\n"
+ "benutzen wir das #include Konstrukt. Unser Programm sieht\n"
+ "dann wie folgt aus:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wichtig ist hier zu verstehen, daß mit dem include, bevor der\n"
+ "Compiler anfängt das Programm zu übersetzen, die Datei Answer4.h komplett wörtlich an der entsprechenden Stelle eingefügt\n"
+ "wird. Dieses Einfügen wird nicht vom eigentlichen Compiler gemacht,\n"
+ "sondern vom einem Programm, das vor dem Compiler den Quelltext\n"
+ "manipuliert, den sogenannten Präprozessor. Der Präprozessor liest die\n"
+ "Zeilen des Quelltext, die mit dem Zeichen # beginnen. Diese\n"
+ "nennt man Direktiven. Sie sind nicht Teil des eigentlichen Programms,\n"
+ "sondern erklären dem Präprozessor, wie das eigentliche Programm\n"
+ "zusammenzusetzen ist. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Wer unsere bisherigen Programme übersetzt hat, wird festgestellt\n"
+ "haben, daß Dateien mit der Endung .o ins Dateisystem\n"
+ "geschrieben wurden. Diese Dateien heißen Objektdateien.\n"
+ "Der eigentliche Compiler erzeugt nicht das ausführbare Programm,\n"
+ "sondern Objektdateien. Die Objektdateien werden dann erst durch den\n"
+ "sogenannten Linker zu ausführbaren Dateien zusammengefasst. \n"
+ "\n"
+ "Mit der Option -c kann man den Compiler dazu bringen, nur die\n"
+ "Objektdatei zu erzeugen und nicht anschließend noch das ausführbare\n"
+ "Programm zu erzeugen.\n"
+ "\n"
+ "Der Compiler kann Objektdateien erzeugen, in denen noch nicht jede\n"
+ "Funktion implementiert ist. Wir können das letzte Programm schreiben,\n"
+ "ohne die Funktion getAnswer implementiert zu haben:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit der Option -c kann der Compiler fehlerfrei die\n"
+ "Objektdatei erzeugen.\n"
+ "\n"
+ "sep@swe10:~/fh/prog3/examples> gcc -c Hello6.c\n"
+ "sep@swe10:~/fh/prog3/examples> ls -l Hello6.*\n"
+ "-rw-r--r-- 1 sep users 84 Sep 3 17:36 Hello6.c\n"
+ "-rw-r--r-- 1 sep users 8452 Sep 3 17:40 Hello6.o\n"
+ "sep@swe10:~/fh/prog3/examples>\n"
+ "\n"
+ "Wollen wir hingegen ein ausführbares Programm erzeugen, so bekommen\n"
+ "wir einen Fehler, und zwar nicht vom Compiler sondern vom Linker. Der\n"
+ "Linker versucht jede deklarierte Funktion mit konkreten Programmcode\n"
+ "aufzulösen. Wenn er einen solchen Code nicht findet, so meldet er\n"
+ "einen Fehler:\n"
+ "\n"
+ " gcc Hello6.c\n"
+ "/tmp/ccRKWUqK.o: In function `main':\n"
+ "/tmp/ccRKWUqK.o(.text+0xe): undefined reference to `getHallo(void)'\n"
+ "collect2: ld returned 1 exit status\n"
+ "sep@swe10:~/fh/prog3/examples>]]>\n"
+ "\n"
+ "Um einen solchen Fehler zu produzieren, bedarf es \n"
+ "keiner header-Datei. Wir können in einem Programm eine\n"
+ "Funktion deklarieren ohne sie zu implementieren.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "int getAnswer();\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"Die Antwort lautet: %i\n\", getAnswer());\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "Wir bekommen wieder einen Fehler des Linkers:\n"
+ "\n"
+ " gcc Hello7.c\n"
+ "/tmp/ccdm3ngO.o: In function `main':\n"
+ "/tmp/ccdm3ngO.o(.text+0xe): undefined reference to `getHallo(void)'\n"
+ "collect2: ld returned 1 exit status\n"
+ "sep@swe10:~/fh/prog3/examples>]]>\n"
+ "\n"
+ "\n"
+ "Der Linker hat im Gegensatz zum Compiler meherere Dateien als\n"
+ "Eingabe, die er gemeinsam verarbeitet. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Genauso wie Funktionen können auch Variablen in \n"
+ "Headerdateien deklariert sein. \n"
+ "\n"
+ "int x = 42;\n"
+ "\n"
+ "Ebenso können Headerdateien weitere Headerdateien inkludieren:\n"
+ "\n"
+ "#include \"C1.h\"\n"
+ "\n"
+ "Hier kann es zu einem Fehler kommen, wenn beide diese \n"
+ "Headerdateien auf die eine oder andere Art in eine Datei inkludiert wird. \n"
+ "\n"
+ "#include \"C1.h\"\n"
+ "#include \"C2.h\"\n"
+ "\n"
+ "int main(){};\n"
+ "\n"
+ "Der Versuch dieses Programm zu übersetzen, führt zu einem Fehler.\n"
+ "\n"
+ " gcc -Wall -LDIR../obj -o ../bin/C3 ./C3.c\n"
+ "In file included from C2.h:1,\n"
+ " from C3.c:2:\n"
+ "C1.h:1: error: redefinition of `x'\n"
+ "C1.h:1: error: `x' previously defined here\n"
+ "strip: '../bin/C3': No such file\n"
+ "sep@linux:~/fh/c>\n"
+ "]]>\n"
+ "\n"
+ "Um solche Fehler, die entstehen, wenn ein und dieselbe Datei mehrfach\n"
+ "inkludiert wird, von vorneherein auszuschließen, bedient man sich\n"
+ "standardmäßig einer weiteren Direktive. Mit der \n"
+ "Direktive #define können Werte definiert werden. Mit der \n"
+ "Direktive #ifndef kann abgefragt werden, ob ein Wert bereits\n"
+ "definiert wurde. Nun kann man in einer Headerdatei eine für diese Datei\n"
+ "spezifische Variable definieren. Üblicher Weise bildet man diese mit zwei\n"
+ "Unterstrichen, gefolgt vom komplett in Großbuchstaben geschriebenen Dateinamen\n"
+ "und der Endung _H. Den kompletten Inhalt der Headerdatei klammert man\n"
+ "nun in einer #ifndef-Direktive. So wird der Inhalt einer Headerdatei\n"
+ "nur dann eingefügt, wenn die spezifische Variable noch nicht definiert\n"
+ "wurde. Also höchstens einmal.\n"
+ "\n"
+ "Beherzigen wir diese Vorgehensweise, so erhalten wir statt der \n"
+ "Datei C1.h von oben, eine entsprechende Datei, die wir zur\n"
+ "Unterscheidung als D.h speichern.\n"
+ "\n"
+ "#ifndef __D1_H\n"
+ "\n"
+ "#define __D1_H ;\n"
+ "\n"
+ "int x = 42;\n"
+ "\n"
+ "#endif /* __D1_H */\n"
+ "\n"
+ "Die Variable, die markiert, ob die Datei schon inkludiert wurde, heißt nach\n"
+ "unserer entsprechenden Konvention __D1_H.\n"
+ "\n"
+ "Diese Datei läßt sich jetzt woanders inkludieren.\n"
+ "#include \"D1.h\"\n"
+ "\n"
+ "Eine mehrfache Inklusion führt jetzt nicht mehr zu einen Fehler.\n"
+ "#include \"D1.h\"\n"
+ "#include \"D2.h\"\n"
+ "\n"
+ "int main(){\n"
+ " return 0;\n"
+ "};\n"
+ "\n"
+ "Das Programm läßt sich jetzt fehlerfrei übersetzen:\n"
+ " gcc -o D3 D3.c\n"
+ "sep@linux:~/fh/prog3/examples/src> ./D3]]>\n"
+ "\n"
+ "In größeren C-Projekten kann man diese Vorgehensweise standardmäßig angewendet\n"
+ "sehen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Bisher haben wir die Direktiven des benutzt, um andere Dateien zu inkludieren\n"
+ "und um über die Bedingung ifndef zu verhindern, dass eine\n"
+ "header-datei mehrfach inkludiert wird. Eine sehr häufig verwendete Direktive,\n"
+ "erlaubt es konstante Werte zu definieren. Hierzu dient \n"
+ "das define, das bereits oben zu wehen war. Der Präprzessor ersetzt\n"
+ "jedesmal wenn ein Wort, das durch ein define einen Wrt erhalten hat,\n"
+ "dieses Wort durch den Wert. Der Compiler bekommt dieses Wort also gar nie zu\n"
+ "sehen, sondern nur nuch den Wert. Damit lassen sich nun die \n"
+ "Wörter true und false als Wahrheitswerte definieren.\n"
+ "\n"
+ "#ifndef __BOOLEAN_H\n"
+ "#define __BOOLEAN_H\n"
+ "#define true 1\n"
+ "#define false 0\n"
+ "#endif\n"
+ "\n"
+ "Inkludieren wir diese header-Datei, so können wir die bool'schen Werte\n"
+ "benutzen. \n"
+ "\n"
+ "\n"
+ "#include \"Boolean.h\"\n"
+ "\n"
+ "int odd(int i){\n"
+ " if (i%2==1)return true;\n"
+ " return false; \n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " if (odd(42)) printf(\"42 ist ungerade\n\");\n"
+ " else printf(\"42 ist gerade\n\");\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "Tatsächlich in großen Projekten werden die Direktiven des Präprozessors\n"
+ "exzessiv genutzt. Das Programm sieht damit manchmal etwas seltsam aus und\n"
+ "erinnert wenig an C. Eigentlich ist der Präprozessor nur nötig, weil der C\n"
+ "Compiler viele Funktionalität nicht selbst anbietet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es gibt ein wunderbares kleines Programm mit Namen make. Starten wir\n"
+ "dieses Programm einmal mit einem Argument:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Nun, etwas anderes hätten wir von einem Computer auch kaum erwartet.\n"
+ "Wenn er etwas machen soll, so müssen wir ihm in der Regel sagen, wie er das\n"
+ "machen kann. Hierzu liest das Programm make standardmäßig eine Datei\n"
+ "mit Namen Makefile. In dieser sind Ziele beschrieben und die\n"
+ "Operationen, die zum Erzeugen dieser Ziele notwendig sind.\n"
+ "\n"
+ "Ein Ziel ist in der Regel eine Datei, die zu erzeugen ist. Syntaktisch wird\n"
+ "ein Ziel so definiert, daß der Zielname in der ersten Spalte einer Zeile\n"
+ "steht. Ihm folgt ein Doppelpunkt, dem auf derselben Zeile Ziele aufgelistet\n"
+ "folgen, die zuerst erzeugt werden müssen, bevor dieses Ziel gebaut werden\n"
+ "kann. Dann folgt in den folgenden Zeilen durch einen Tabulatorschritt\n"
+ "eingerückt, welche Befehle zum Erreichen des Ziels ausgeführt werden müssen. \n"
+ "Eine einfache Makedatei zum Bauen des Programms HalloFreunde sieht\n"
+ "entsprechend wie folgt aus.\n"
+ "\n"
+ "HalloFreunde: HalloFreunde.o \n"
+ " gcc -o HalloFreunde HalloFreunde.o \n"
+ "\n"
+ "HalloFreunde.o: HalloFreunde.c\n"
+ " gcc -c HalloFreunde.c\n"
+ "\n"
+ "Es gibt in dieser Makedatei zwei Ziele: die ausführbare \n"
+ "Datei HalloFreunde und die Objektdatei HalloFreunde.o. Zum\n"
+ "Erzeugen der Objektdatei, muß die Datei HalloFreunde.c vorhanden\n"
+ "sein, zum Erzeugen der ausführbaren Datei, muß erst die Objektdatei generiert\n"
+ "worden sein. Wir können jetzt make bitten, \n"
+ "unsere HalloFreunde-Applikation zu bauen. Die Makedatei \n"
+ "nicht Makefile heißt, welches die standardmäßig \n"
+ "von make als Steuerdatei gelesene Datei ist, müssen \n"
+ "wir make mit der Option -f als Argument mitgeben, in welcher\n"
+ "Datei die Ziele zum Machen definiert sind.\n"
+ "\n"
+ "Für die Erzeugung des Programms HalloFreunde reicht also folgender\n"
+ "Aufruf von make:\n"
+ "\n"
+ "\n"
+ " make -f makeHalloFreunde\n"
+ "gcc -c -o HalloFreunde.o HalloFreunde.c\n"
+ "gcc -o HalloFreunde HalloFreunde.o\n"
+ "sep@pc216-5:~/fh/cpp/student/src> ./HalloFreunde\n"
+ "hello world\n"
+ "sep@pc216-5:~/fh/cpp/student/src> make -f makeHalloFreunde\n"
+ "make: »HalloFreunde« ist bereits aktualisiert.\n"
+ "sep@pc216-5:~/fh/cpp/student/src>]]>\n"
+ "\n"
+ "Gibt man make nicht als zusätzliches Argument an, welches Ziel zu\n"
+ "bauen ist, so baut make das erste in der Makedatei definierte \n"
+ "Ziel.\n"
+ "\n"
+ "Die wahre Stärke von make ist aber, daß nicht stur alle Schritte zum\n"
+ "Erzeugen eines Ziels durchgeführt werden, sondern anhand der Zeitstempel der\n"
+ "Dateien geschaut wird, ob es denn überhaupt notwendig ist, bestimmte Ziele neu\n"
+ "zu bauen, oder ob sie schon aktuell im Dateisystem vorliegen. So kommt es, daß\n"
+ "beim zweiten Aufruf von make oben, nicht erneut kompiliert\n"
+ "wurde. \n"
+ "\n"
+ "Um gute und strukturierte Makedateien zu schreiben, kennt make eine\n"
+ "ganze Anzahl weiterer Konstrukte. Zuvorderst Kommentare. Kommantarzeilen\n"
+ "beginnen mit einem Gattersymbol #. Dann lassen sich Konstanten\n"
+ "definieren. Es empfiehlt sich in einer Makedatei alle Pfade und Werkzeuge, wie\n"
+ "z.B. der benutzte Compiler als Konstante zu definieren, damit diese leicht\n"
+ "global geändert werden können. Schließlich lassen sich noch allgemeine Regeln,\n"
+ "wie man von einem Dateitypen zu einem anderen Dateitypen gelangt,\n"
+ "definieren; z.B. das der Aufruf gcc -c zu benutzen ist, für eine \n"
+ "C-Datei eine Objektdatei zu generieren.\n"
+ "\n"
+ "Eine typische Makedatei, die all diese Eigenschaften ausnutzt,\n"
+ " sieht für ein C-Projekt dann wie folgt aus.\n"
+ "\n"
+ "\n"
+ "Für eine genaue Beschreibung der Möglichkeiten einer Makedatei konsultiere man\n"
+ "die Dokumentation von make. Nahezu alle C++-Projekte \n"
+ "benutzen make zum Bauen des Projektes. Für Javaprojekte\n"
+ "hat sich hingegen das\n"
+ "Programm ant durchgesetzt.\n"
+ "\n"
+ "Auch die verschiedenen Versionen dieses Skripts werden mit Hilfe \n"
+ "von make erzeugt.\n"
+ "\n"
+ "Kopieren Sie sich den Quelltextordner dieses Skripts und rufen Sie\n"
+ "die darin enthaltene obige Makefile-Datei auf. \n"
+ "Rufen Sie dann make direkt nocheinmal auf.\n"
+ "Ändern Sie anschließend die\n"
+ "Datei ReadRoman.c und starten Sie erneut make. Was\n"
+ "beobachten Sie in Hinblick auf TestReadRoman.o. Starten Sie \n"
+ "schließlich make mit dem Ziel love.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schön wäre es sicher, wenn ein Programmtext immer so klar und eindeutig wäre,\n"
+ "dass man sofort ohne weitere Hilfe erkennen könnte, was die einzelnen\n"
+ "Programmzeilen ausdrücken und bewirken. Davon ist in der Regel nicht\n"
+ "auszugehen. Daher ist es unerläßlich, dass man im Programmtext Kommentarzeilen\n"
+ "schreibt. Diese helfer Kollegen und einem selbst mit etwas zeitlichen Abstand\n"
+ "das Programm weiterhin zu verstehen.\n"
+ "\n"
+ "In C gibt es mitlerweile zwei Arten, Kommentare im Programmtext zu schreiben.\n"
+ "Für einzeilige Kommentare gibt es den doppelten Schrägstrich. Von diesem an\n"
+ "wird der Rest der Zeile als Kommentar betrachtet.\n"
+ "\n"
+ "Längere Kommentarblöcke werden mit den Zeichen /* begonnen und enden\n"
+ "mit den Zeichen */. Hier zwischen können beliebig viele Zeichen in\n"
+ "mehreren Zeilen als Kommentar stehen.\n"
+ "\n"
+ "\n"
+ "Ein kleines Beispiel eines kommentierten Programms.\n"
+ "\n"
+ "\n"
+ " /*\n"
+ " Berechnet die Fakultät vom Eingabeparameter.\n"
+ " Der Eingabeparameter wird als nicht-negative Zahl erwartet.\n"
+ " Ansonsten kann es zur Nichtterminierung der Funktion kommen.\n"
+ " */\n"
+ " int f(int n){\n"
+ " // Lösung iterativ mit einer for-Schleife\n"
+ "\n"
+ " int result=1; //akkumuliert das Ergebnis\n"
+ " int i=n; //Laufvariable für die Schleife\n"
+ " for (;i>0;i++){\n"
+ " result = result*i; \n"
+ " }\n"
+ " return result;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Heutzutage schreibt niemand mehr ein einzelnen Programm ganz allein. In der\n"
+ "Regel wird komplexere Software entwickelt. Die meiste Software wird damit\n"
+ "nicht einmal ein Programm sein, sondern eine Bibliothek, die in anderen\n"
+ "Softwareprodukten verwendet wird.\n"
+ "Um die von einer Bibliothek bereitgestellte Funktionalität sinnvoll verwenden\n"
+ "zu können, ist diese auf einheitliche Weise zu dokumentieren. Zur\n"
+ "Dokumentation von C-Bibliotheken kann man das \n"
+ "Werkzeug Doxygen einsetzen. Dieses Programm liest die C Kopfdateien und\n"
+ "generiert eine umfassende Dokumentation in verschiedenen Formaten. Am\n"
+ "häufigsten wird dabei wahrscheinlich die HTML-Dokumentation verwendet. \n"
+ "\n"
+ "Doxygen kommt mit einem Kommandozeilenprogramm doxygen. Dieses\n"
+ "erwartet als Eingabe eine Steuerdatei zur Konfiguration. In dieser\n"
+ "Konfigurationsdatei können eine große Menge von Parametern eingestellt werden,\n"
+ "wie die Sprache, in der die Dokumentation erstellt werden soll oder auch den\n"
+ "Projektnamen, des zu dokumentierenden Projekts. Zum Glück \n"
+ "ist doxygen in der Lage, sich selbst eine solche Konfigurationsdatei\n"
+ "zu generieren, die man dann manuell nacheditieren kann. Hierzu benutzt man das\n"
+ "Kommandozeilenargument -g von doxygen.\n"
+ "\n"
+ "\n"
+ "Ein kurzer Start mit doxygen sieht also wie folgt aus:\n"
+ "\n"
+ "Generierung einer Konfigurationsdatei mit:\n"
+ " doxygen -g MeinProjekt\n"
+ "\n"
+ "Configuration file `MeinProjekt' created.\n"
+ "\n"
+ "Now edit the configuration file and enter\n"
+ "\n"
+ " doxygen MeinProjekt\n"
+ "\n"
+ "to generate the documentation for your project\n"
+ "\n"
+ "sep@pc216-5:~/fh/c>]]>\n"
+ "Anschließend editiert man die erzeugte Datei MeinProjekt. Für\n"
+ "den Anfang sei zu empfehlen EXTRACT_ALL = YES zu setzen. Selbst wenn\n"
+ "keine eigentlich für Doxygen gedachte Dokumentation im Quelltext steht, werden\n"
+ "alle Programmteile in der Dokumentation aufgelistet.\n"
+ "Die Eigentliche Generierung der Dokumentation.\n"
+ " doxygen MeinProjekt\n"
+ "sep@pc216-5:~/fh/c>]]>\n"
+ " \n"
+ "\n"
+ "Jetzt befindet sich die generierte Dokumentation im in der Konfigurationsdatei\n"
+ "angegebenen Ausgabeordner.\n"
+ " \n"
+ "\n"
+ "Laden Sie sich die kompletten Quelltextdateien dieses Skriptes von\n"
+ "der Webseite und generieren Sie mit Doxygen eine Dokumentation für\n"
+ "diese. \n"
+ "\n"
+ "\n"
+ "Bisher konnte sich Doxygen lediglich auf den Quelltext beziehen, um\n"
+ "eine Dokumentation zu erzeugen. Natürlich reicht das nicht aus. Daher kann man\n"
+ "im Quelltext Kommentare anbringen, die Doxygen auswertet. Es gibt\n"
+ "verschiedene Syntax hierzu, z.B. die klassische aus C bekannte Art zu\n"
+ "kommentieren:\n"
+ "\n"
+ "/**\n"
+ " Dies ist eine einfache Funktion.\n"
+ "Sie ist in der Lage ganz tolle Dinge zu tun.\n"
+ " */\n"
+ "void f();\n"
+ "\n"
+ "In diesen Kommentarblöcken, die über dem zu dokumentierenden Code stehen,\n"
+ "können mit dem Backslashsymbol bestimmte Attribute gesetzt werden. \n"
+ "So gibt es z.B. das Attribut \\param für einen Funktionsparameter oder\n"
+ "das Attribut \return für das Funktionsergebnis.\n"
+ "/**\n"
+ " Die Additionsfunktion.\n"
+ " Sie kann dazu benutzt werden, um einen Funktionszeiger \n"
+ " auf die Addition\n"
+ " zur Verfügung zu haben. Sie ruft in ihrem Rumpf lediglich den\n"
+ " Additionsoperator auf.\n"
+ "\n"
+ " \\param x der erste Operand.\n"
+ " \\ param y der zweite (rechte) Operand.\n"
+ " \\return die Summe der beiden Operanden.\n"
+ "*/\n"
+ "int add(int x, int y);\n"
+ "\n"
+ "Betrachten Sie, die für die \n"
+ "Funktionen f und add in der letzten Aufgabe generierte\n"
+ "Dokumentation. \n"
+ "\n"
+ "Doxygen kennt viele solcher Attribute und viele verschiedene mächtige\n"
+ "unterschiedliche Weisen der Dokumentation. Zum Glück \n"
+ "ist Doxygen selbst recht gut erklärt, so daß mit dem hier gegebenen\n"
+ "Einstieg es hoffentlich leicht möglich ist, sich mit dem Programm\n"
+ "auseinanderzusetzen. \n"
+ "\n"
+ "Beschäftigen Sie sich mit der Dokumentation \n"
+ "von Doxygen und testen einige der darin beschriebenen Möglichkeiten\n"
+ "der Dokumentation.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wie Ernie in der Sesamstraße bereits gesungen hat: Jeder macht mal Fehler\n"
+ "ich und du; so müssen wir davon ausgehen, daß unsere Programme nicht auf\n"
+ "Anhieb die gewünschte Funktionalität bereitstellen. Insbesondere da C uns\n"
+ "relativ viel Möglichkeiten und Freiheiten in der Art der Programmierung\n"
+ "ermöglicht, läßt es uns auch die Freiheiten, viele Fehler zu machen.\n"
+ "\n"
+ "Um einen Fehler aufzuspüren können wir auf zwei Techniken zurückgreifen:\n"
+ "\n"
+ " Logging: Während der Ausführung wird in bestimmten Programmzeilen eine\n"
+ " Statusmeldung in eine Log-Datei geschrieben. Diese läßt sich hinterher\n"
+ " analysieren. Die einfachste Form des Loggings ist, ab und zu eine Meldung\n"
+ " auf die Konsole auszugeben.\n"
+ " Debuggen: mit Hilfe eines Debuggers läßt sich schrittweise das\n"
+ " Programm ausführen und dabei in den Speichern schauen.\n"
+ "\n"
+ "\n"
+ "Der Begriff debuggen, den man wörtlich \n"
+ "mit Entwanzen übersetzen könnte, bezieht sich auf die Benutzung des\n"
+ "Wortes bug für Macken in einem technischen System. Der Legende nach\n"
+ "stammt diese Benutzung des Wortes bug aus der Zeit der ersten\n"
+ "Rechner,\n"
+ "die noch nicht mit Halbleitern geschaltet haben, sondern mit Relais, ähnlich,\n"
+ "wie man sie von Flipperautomaten kennt. Es soll einmal zu einem Fehler im\n"
+ "Programmablauf gekommen sein, der sich schließlich darauf zurückführen ließ,\n"
+ "daß ein Käfer zwischen ein Relais geklettert war. Eine schöne Ankdote, die\n"
+ "leider falsch ist, denn aus schon früheren Dokumenten geht hervor, daß der\n"
+ "Begriff bug schon im 19. Jahrhundert für technische Fehler\n"
+ "gebräuchlich war.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Um ein Programm mit einem Debugger zu durchlaufen, ist es notwenig, daß der\n"
+ "Debugger Informationen im ausführbaren Code findet, die sich auf den Quelltext\n"
+ "beziehen. Dieses sind umfangreiche Informationen. Der gcc generiert\n"
+ "die notwendige Information in die Objektdateien, wenn man ihn mit der \n"
+ "Option -g startet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die rudimentärste Art des Debuggens ist mit Hilfe des \n"
+ "Kommandozeilenprogramms gdb. Wir wollen das einmal\n"
+ "ausprobieren. Hierzu brauchen wir natürlich ein entsprechendes Programm,\n"
+ "welches hier zunächst definiert sei. Es handelt sich dabei um ein Programm\n"
+ "zur Konvertierung von als Strings vorliegenden römischen Zahlen in die\n"
+ "entsprechende arabische Zahl als int. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine rudimentäre Implementierung dieser Kopfdatei:\n"
+ "\n"
+ "last) result = result-2*last;\n"
+ " result=result+current;\n"
+ " last=current;\n"
+ " } \n"
+ " return result;\n"
+ "}]]>\n";
String skript5 =
"\n"
+ "Wir benutzen hier schon ein paar wenige C Eigenschaften, die uns aber\n"
+ "vorerst nicht stören sollen und nicht weiter hinterfragt werden sollen. \n"
+ "Es folgt ein minimaler Test dieses Programms:\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"romanToInt(\\\"MMMCDXLIX\\\") = %i\n\",romanToInt(\"MMMCDXLIX\")); \n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Übersetzen Sie das Programm mit der -g-Option\n"
+ "des gcc und lassen Sie das Programm ausführen.\n"
+ "\n"
+ "Jetzt können sie den gnu debugger mit dem \n"
+ "Programm TestReadRoman aufrufen. Sie geraten in einen\n"
+ "Kommandozeileninterpreter des Debuggers. Der Debugger kennt eine Reihe\n"
+ "Kommandos, über die er mit dem Kommando help gerne informiert:\n"
+ "\n"
+ " gdb TestReadRoman\n"
+ "GNU gdb 6.1\n"
+ "Copyright 2004 Free Software Foundation, Inc.\n"
+ "GDB is free software, covered by the GNU General Public License, and you are\n"
+ "welcome to change it and/or distribute copies of it under certain conditions.\n"
+ "Type \"show copying\" to see the conditions.\n"
+ "There is absolutely no warranty for GDB. Type \"show warranty\" for details.\n"
+ "This GDB was configured as \"i586-suse-linux\"...\n"
+ " Using host libthread_db library \"/lib/tls/libthread_db.so.1\".\n"
+ "\n"
+ "(gdb) help\n"
+ "List of classes of commands:\n"
+ "\n"
+ "aliases -- Aliases of other commands\n"
+ "breakpoints -- Making program stop at certain points\n"
+ "data -- Examining data\n"
+ "files -- Specifying and examining files\n"
+ "internals -- Maintenance commands\n"
+ "obscure -- Obscure features\n"
+ "running -- Running the program\n"
+ "stack -- Examining the stack\n"
+ "status -- Status inquiries\n"
+ "support -- Support facilities\n"
+ "tracepoints -- Tracing of program execution without stopping the program\n"
+ "user-defined -- User-defined commands\n"
+ "\n"
+ "Type \"help\" followed by a class name for a list of commands in that class.\n"
+ "Type \"help\" followed by command name for full documentation.\n"
+ "Command name abbreviations are allowed if unambiguous.\n"
+ "(gdb)]]>\n"
+ "\n"
+ "Das naheliegendste Kommando ist das Kommando run. Es führt dazu, daß\n"
+ "das Programm ausgeführt wird.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir wollen aber nicht, daß das Programm einmal komplett ausgeführt wird,\n"
+ "sondern schrittweise die Ausführung nachverfolgen. Hierzu müssen wir einen\n"
+ "sogenannten breakpoint setzen. Wir können diesen auf die erste Zeile\n"
+ "des Programms setzen:\n"
+ "\n"
+ "\n"
+ "Wenn wir jetzt run sagen, stoppet der Debugger in der Zeile vier:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Jetzt können wir z.B. Zeile für Zeile das Programm vom Debugger ausführen\n"
+ "lassen. Hierzu gibt es den Befehl step:\n"
+ "\n"
+ "\n"
+ "Wie man sieht gibt der Debugger immer die aktuelle Zeile aus, und beim\n"
+ "Funktionsaufruf auch den Wert des Parameters. Wollen wir zusätzlich in den\n"
+ "Speicher schauen, so gibt es den Befehl print, der dazu da ist, sich\n"
+ "den Wert einer Variablen anzuschauen:\n"
+ "\n"
+ "last) result = result-2*last;\n"
+ "(gdb) step\n"
+ "23 result=result+current;\n"
+ "(gdb) step\n"
+ "24 last=current;\n"
+ "(gdb) print result\n"
+ "$1 = 1000\n"
+ "(gdb)]]>\n"
+ "\n"
+ "Wenn auch mühselig, so können wir so doch ganz genau verfolgen, wie das\n"
+ "Programm arbeitet. \n"
+ "\n"
+ "Spielen Sie die Session des \n"
+ "Debuggers gdb aus dem Skript einmal nach und\n"
+ "experimentieren sie mit den verschiedenen Befehlen des Debuggers.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Aufbauend auf die Möglichkeiten des Debuggens über die\n"
+ "Kommandozeilenschnittstelle, lassen sich leicht konfortable graphische\n"
+ "Debugger implementieren. Ein recht schöner solcher Debugger ist das \n"
+ "Programm ddd. Hier können Breakpoints mit der Maus im Programmtext\n"
+ "gesetzt werden und die verschiedenen Speicherbelegungen betrachtet\n"
+ "werden. Abbildung zeigt das Programm in Aktion.\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "Üben Sie den Umgang mit dem Debugger ddd anhand des\n"
+ "Programms TestReadRoman. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "In großen Projekten existieren viele hundert Quelltextdateien und\n"
+ "Header-Dateien, die alle zum Gesamtprogramm beitragen. Viele Bibliotheken\n"
+ "werden benutzt. Es ist dann etwas mühseelig nur mit einem Editor und der\n"
+ "Kommandozeile zu arbeiten. Um hier die Arbeit zu erleichtern gibt es\n"
+ "Entwicklungsumgebungen. Entwicklungsumgebungen enthalten in der Regel \n"
+ "einen integrierten Editor, der eine gute Syntaxhervorhebung durchführt,\n"
+ "öffnende und schließende Klammern paart und Fehler direkt im Programmtext\n"
+ "markiert. Sie unterstützen den Build-Prozess und übersetzen in der Regel das\n"
+ "ganze Projekt bei einer neu abgespeicherten Änderung, so dass Fehler \n"
+ "sofort erkannt werden können.\n"
+ "\n"
+ "Eine recht mächtige Programmierumgebung ist eclipse. Sie \n"
+ "ist frei verfügbar und kann \n"
+ "unter diesen Link heruntergeladen werden. eclipse ist\n"
+ "ursprünglich eine von IBM zur Entwicklung von Javaprojekten gedachte\n"
+ "Entwicklungsplatform. eclipse ist in Java entwickelt. \n"
+ "Daher braucht man, um mit eclipse arbeiten zu können auch \n"
+ "die Java-Laufzeitumgebung.\n"
+ " \n"
+ "\n"
+ "Durch den stark komponentenbasierten Aufbau \n"
+ "der Architektur von eclipse ist es möglich Zusätze zu Entwickeln, die\n"
+ "aus eclipse eine Entwicklungsumgebung für Projekte in anderen\n"
+ "Sprachen als Java zu benutzen. Ein solches sogenanntes Plugin ermöglicht die C\n"
+ "und C++-Entwicklung mit eclipse. Es ist \n"
+ "dies das Pugin CDT.\n"
+ "\n"
+ "Eclipse mit CDT ist auf unseren Übungsrechnern installiert. Wenn man eclipse\n"
+ "startet, wird zumeist nach einem Arbeitsordner gefragt. In diesem Ordner will \n"
+ "eclipse alle seine Projekte verwalten.\n"
+ "\n"
+ "Zum Start mit eclipse ist zunächst ein neues Projekt zu definieren. Hierzu\n"
+ "wähle man im Menu den Punkt: new managed c project. Einem solchem\n"
+ "Projekt können nnun neue .c und .h Quelltextdateien zugefügt\n"
+ "wreden, die in einem Editorfenster ediert werden können. In der Regel versucht\n"
+ "eclipse sobald eine Datei neu gespeichert wird, mit dem C compiler das Projekt\n"
+ "neu zu bauen und zeigt eventuelle Fehler direkt an.\n"
+ "\n"
+ "Legen sie ein eclipse Projekt an und \n"
+ "Programmieren die nächste Aufgaben dieses Blattes \n"
+ "mit eclipse. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Bisher haben wir außer Zahlen und direkt ausgegebenen Zeichenketten noch keine\n"
+ "Daten in unseren Programmen verarbeitet. Daten sind natürlich das A und O der\n"
+ "Programmierung. Das spiegelt sich auch in der historischen Entwicklung \n"
+ "von Programmiersprachen wieder. Während die Grundkonzepte, wie Funktionen,\n"
+ "Ausdrücke und Schleifenbefehle sich schon vor mehreren Jahrzehnten in allen\n"
+ "Programmiersprachen auf ähnliche Weise befanden, sind sämtliche Fortschritte\n"
+ "in der Programmierung im Bezug auf Definition und Modifikation von Daten\n"
+ "bezogen. Objektorientierte Programmierung zeichnet sich als eine\n"
+ "datenzentrierte Programmierung aus. Aber auch in funktionalen\n"
+ "Programmiersprachen liegt der Fortschritt insbesondere in den sogenannten\n"
+ "algebraischen Datentypen, die es erlauben, strukturierte Daten einfach zu\n"
+ "definieren, zu erzeugen und zu bearbeiten. Nicht von ungefähr ist mit \n"
+ "XML eine \n"
+ "der jüngsten Sprachen, die in der Informatik definiert wurde, eine\n"
+ "Sprache zum Beschreiben strukturierter Daten entstanden. \n"
+ "\n"
+ "In diesem Kapitel werden wir die Möglichkeiten der Definition und Verarbeitung\n"
+ "von Daten in C kennenlernen.\n"
+ "\n"
+ "\n"
+ "Bevor wir uns strukturierten Daten widmen können, betrachten wir noch einmal\n"
+ "die primitiven Basistypen zur Zahlendarstellung.\n"
+ "Bisher haben wir uns beim Rechnen auf einen Typen für ganze Zahlen beschränkt,\n"
+ "den Typ int. C stellt vielerlei Typen zur\n"
+ "Repräsentation von Zahlen zur Verfügung. \n"
+ "\n"
+ "Die im Folgenden vorgestellten Typen nennt man primitive Typen. Sie\n"
+ "sind fest von C vorgegeben. \n"
+ "\n"
+ "\n"
+ "Um Daten der primitiven Typen aufschreiben zu können, gibt es jeweils\n"
+ "Literale für die Werte dieser Typen. Für den Typ int haben wir\n"
+ "einfach die Zahlen als Literale hinschreiben können.\n"
+ "\n"
+ "\n"
+ "\n"
+ "In der Mathematik sind wir gewohnt, mit verschiedenen Mengen von Zahlen zu\n"
+ "arbeiten:\n"
+ "\n"
+ "\n"
+ "natürliche Zahlen: Eine induktiv definierbare Menge\n"
+ "mit einer kleinsten Zahl, so daß es für jede Zahl eine eindeutige\n"
+ "Nachfolgerzahl gibt.\n"
+ "ganze Zahlen: Die natürlichen Zahlen erweitert um die\n"
+ "mit einem negativen Vorzeichen behafteten Zahlen, die sich ergeben, wenn man\n"
+ "eine größere Zahl von einer natürlichen Zahl abzieht.\n"
+ "rationale Zahlen: Die ganzen Zahlen erweitert um\n"
+ "Brüche, die sich ergeben, wenn man eine Zahl durch eine Zahl teilt, von der\n"
+ "sie kein Vielfaches ist.\n"
+ "reelle Zahlen: Die ganzen Zahlen erweitert um\n"
+ "irrationale Zahlen, die sich z.B.aus der Quadratwurzel von Zahlen \n"
+ "ergeben, die nicht das Quadrat einer rationalen Zahl sind. \n"
+ "komplexe Zahlen: Die reellen Zahlen erweitert um\n"
+ "imaginäre Zahlen, wie sie benötigt werden, um einen Wurzelwert\n"
+ "für negative Zahlen darzustellen.\n"
+ "\n"
+ "\n"
+ "Es gilt folgende Mengeninklusion zwischen diesen Mengen: \n"
+ "
\n"
+ "
\n"
+ "\n"
+ "Da bereits nicht endlich ist, ist keine dieser Mengen endlich. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Da wir nur von einer endlich großen Speicherkapazität ausgehen können, lassen\n"
+ "sich für keine der aus der Mathematik bekannten Zahlenmengen alle Werte in\n"
+ "einem Rechner darstellen. Wir können also schon einmal nur Teilmengen der\n"
+ "Zahlenmengen darstellen.\n"
+ "\n"
+ "\n"
+ "Von der Hardwareseite stellt sich heute zumeist die folgende Situation dar: \n"
+ "Der Computer hat einen linearen Speicher, der in Speicheradressen unterteilt\n"
+ "ist. Eine Speicheradresse\n"
+ "bezeichnet einen Bereich von 32 Bit. Wir bezeichnen diese als ein Wort. Die\n"
+ "Einheit von 8 Bit wird als Byte bezeichnetein anderes selten\n"
+ "gebrauchtes Wort aus dem Französischen ist: Oktett. Heutige\n"
+ "Rechner verwalten also in der Regel Dateneinheiten von 32 Bit. Hieraus ergibt\n"
+ "sich die Kardinalität der Zahlenmengen, mit denen ein Rechner als primitive\n"
+ "Typen rechnen kann. Soll mit größeren Zahlenmengen gerechnet werden, so muß\n"
+ "hierzu eine Softwarelösung genutzt werden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Natürliche Zahlen werden in der Regel durch Zeichenketten von \n"
+ " Symbolen, den Ziffern, eines \n"
+ "endlichen Alphabets dargestellt. Die Größe dieser Symbolmenge\n"
+ " wird als die Basis b der Zahlendarstellung bezeichnet. \n"
+ "Für die Basis b gilt: b > 1. Die Ziffern bezeichnen die\n"
+ " natürlichen Zahlen von 0 bis b-1. \n"
+ "\n"
+ "Der Wert der Zahl einer \n"
+ "Zeichenkette an-1a0 berechnet sich für die Basis b nach folgender\n"
+ "Formel: \n"
+ "\n"
+ "i=0n-1 ai*bi\n"
+ "= a0*b0 + + an-1*b n-1\n"
+ "\n"
+ "\n"
+ "Gebräuchliche Basen sind:\n"
+ "\n"
+ "2: Dualsystem\n"
+ "8: Oktalsystem\n"
+ "10: Dezimalsystem\n"
+ "16: HexadezimalsystemEine etwas unglückliche Namensgebung aus\n"
+ "der Mischung eines griechischen mit einem lateinischen Wort.\n"
+ "\n"
+ "\n"
+ "Zur Unterscheidung wird im Zweifelsfalle die Basis einer Zahlendarstellung als\n"
+ "Index mit angegeben.\n"
+ "\n"
+ "\n"
+ "Die Zahl 10 in Bezug auf unterschiedliche Basen dargestellt:\n"
+ "(10)10=\n"
+ "(A)16=\n"
+ "(12)8=\n"
+ "(1010)2\n"
+ "\n"
+ "\n"
+ "\n"
+ "Darüberhinaus gibt es auch Zahlendarstellungen, die nicht in Bezug auf eine\n"
+ "Basis definiert sind, wie z.B.die römischen Zahlen.\n"
+ "\n"
+ "\n"
+ "Betrachten wir wieder unsere Hardware, die in Einheiten von 32 Bit agiert, so\n"
+ "lassen sich in einer Speicheradresse durch direkte Anwendung der Darstellung\n"
+ "im Dualsystem die natürlichen \n"
+ "Zahlen von 0 bis 232-1 darstellen.\n"
+ "\n"
+ "C kennt tatsächlich Datentypen um natürliche Zahlen darzustellen. Ein solcher\n"
+ "Typ wird mit zwei Wörtern als unsigned int bezeichnet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Zur Darstellung ganzer Zahlen bedarf es einer\n"
+ "Darstellung vorzeichenbehafteter Zahlen in den Speicherzellen des Rechners. \n"
+ "Es gibt mehrere Verfahren, wie in einem dualen System vorzeichenbehaftete\n"
+ "Zahlen dargestellt werden können.\n"
+ "\n"
+ "\n"
+ "Die einfachste Methode ist, von den n für die Zahlendarstellung zur\n"
+ "Verfügung stehenden Bit eines zur Darstellung des Vorzeichens und die\n"
+ "übrigen n-1 Bit für eine Darstellung im Dualsystem zu nutzen.\n"
+ "\n"
+ "In der Darstellung durch Vorzeichen und Betrag werden bei einer\n"
+ "Wortlänge von 8 Bit die Zahlen 10 und -10 durch folgende \n"
+ "Bitmuster repräsentiert: 00001010 und 10001010. \n"
+ "\n"
+ "\n"
+ "Wenn das\n"
+ "linkeste Bit das Vorzeichen bezeichnet, ergibt sich daraus, daß es zwei\n"
+ "Bitmuster für die Darstellung der Zahl 0 \n"
+ "gibt: 10000000 und 00000000. \n"
+ "\n"
+ "In dieser Darstellung lassen sich bei einer \n"
+ "Wortlänge n die Zahlen von -2n-1-1 bis 2n-1-1 darstellen.\n"
+ "\n"
+ "\n"
+ "Die Lösung der Darstellung mit Vorzeichen und Betrag erschwert das Rechnen.\n"
+ "Wir müssen zwei Verfahren bereitstellen: eines zum Addieren und eines zum\n"
+ "Subtrahieren (so wie wir in der Schule schriftliches Addieren und Subtrahieren\n"
+ "getrennt gelernt haben). \n"
+ "\n"
+ "Versuchen wir, das gängige Additionsverfahren \n"
+ "für 10 und -10 in der Vorzeichendarstellung anzuwenden, so\n"
+ "erhalten wir:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " 00\n"
+ " 00\n"
+ " 10\n"
+ " 10\n"
+ "\n"
+ "\n"
+ " 10\n"
+ " 00\n"
+ " 10\n"
+ " 10\n"
+ "\n"
+ "\n"
+ " 10\n"
+ " 01\n"
+ " 01\n"
+ " 00\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das Ergebnis stellt keinenfalls die \n"
+ "Zahl 0 dar, sondern die Zahl -20.\n"
+ "\n"
+ "\n"
+ "Es läßt sich kein einheitlicher Algorithmus für die\n"
+ "Addition in dieser Darstellung finden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Ausgehend von der Idee, daß man eine Zahlendarstellung sucht, in der allein\n"
+ "durch das bekannte Additionsverfahren auch mit negativen Zahlen korrekt\n"
+ "gerechnet wird, kann man das Verfahren des Einerkomplements wählen.\n"
+ "Die Idee des Einerkomplements ist, daß für jede Zahl die entsprechende\n"
+ "negative Zahl so dargestellt wird, indem jedes Bit gerade andersherum gesetzt\n"
+ "ist. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Bei einer Wortlänge von 8 Bit werden die \n"
+ "Zahlen 10 und -10 durch folgende Bitmuster \n"
+ "dargestellt: 00001010 und 11110101.\n"
+ "\n"
+ "Jetzt können auch negative Zahlen mit dem gängigen Additionsverfahren addiert\n"
+ "werden, also kann die Subtraktion durch ein Additionsverfahren durchgeführt\n"
+ "werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " 00\n"
+ " 00\n"
+ " 10\n"
+ " 10\n"
+ "\n"
+ "\n"
+ " 11\n"
+ " 11\n"
+ " 01\n"
+ " 01\n"
+ "\n"
+ "\n"
+ " 11\n"
+ " 11\n"
+ " 11\n"
+ " 11\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das errechnete Bitmuster stellt die negative Null\n"
+ "dar.\n"
+ "\n"
+ "In der Einerkomplementdarstellung läßt sich zwar fein rechnen, wir haben aber\n"
+ "immer noch zwei Bitmuster zur Darstellung der 0. Für eine \n"
+ "Wortlänge n lassen sich auch wieder die Zahlen von -2n-1-1 bis 2n-1-1 darstellen..\n"
+ "\n"
+ "Ebenso wie in der Darstellung mit Vorzeichen und Betrag erkennt man in der\n"
+ "Einerkomplementdarstellung am linkesten Bit, ob es sich um eine negative oder\n"
+ "um eine positive Zahl handelt.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Zweierkomplementdarstellung verfeinert die Einerkomplementdarstellung, so\n"
+ "daß es nur noch ein Bitmuster für die Null gibt. Im Zweierkomplement wird für\n"
+ "eine Zahl die negative Zahl gebildet, indem zu ihrer\n"
+ "Einerkomplementdarstellung noch 1 hinzuaddiert wird.\n"
+ "\n"
+ "Bei einer Wortlänge von 8 Bit werden die \n"
+ "Zahlen 10 und -10 durch folgende Bitmuster \n"
+ "dargestellt: 00001010 und 11110110.\n"
+ "\n"
+ "Jetzt können weiterhin auch negative Zahlen mit dem \n"
+ "gängigen Additionsverfahren addiert\n"
+ "werden, also kann die Subtraktion durch ein Additionsverfahren durchgeführt\n"
+ "werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " 00\n"
+ " 00\n"
+ " 10\n"
+ " 10\n"
+ "\n"
+ "\n"
+ " 11\n"
+ " 11\n"
+ " 01\n"
+ " 10\n"
+ "\n"
+ "\n"
+ " 00\n"
+ " 00\n"
+ " 00\n"
+ " 00\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das errechnete Bitmuster stellt die Null\n"
+ "dar.\n"
+ "\n"
+ "Die negative Null aus dem Einerkomplement stellt im Zweierkomplement\n"
+ "keine Null dar, sondern die Zahl -1, wie man sich vergegenwärtigen\n"
+ "kann, wenn man von der 1 das Zweierkomplement bildet.\n"
+ "\n"
+ "\n"
+ "Das Zweierkomplement ist die in heutigen Rechenanlagen gebräuchlichste Form\n"
+ "der Zahlendarstellung für ganze Zahlen. In modernen Programmiersprachen\n"
+ "spiegelt sich dieses in den Wertebereichen primitiver Zahlentypen wieder. \n"
+ "\n"
+ "\n"
+ "
\n"
+ "\n"
+ "\n"
+ "In der Programmiersprache Java sind die konkreten Wertebereiche für die\n"
+ "einzelnen primitiven Typen in der Spezifikation festgelegt. In anderen\n"
+ "Programmiersprachen wie z.B.C ist dies nicht der Fall. Hier hängt es\n"
+ "vom Compiler und dem konkreten Rechner ab, welchen Wertebereich die\n"
+ "entsprechenden Typen haben.\n"
+ "\n"
+ "Es gibt Programmiersprachen wie \n"
+ "z.B.Haskell, in denen es einen Typ\n"
+ "gibt, der potentiell ganze Zahlen von beliebiger Größe darstellen kann.\n"
+ " \n"
+ "\n"
+ "\n"
+ "Gängige Spezifikationen moderner Programmiersprachen sehen einen\n"
+ "ausgezeichneten Wert nan für not a number in \n"
+ "der Arithmetik vor, der ausdrücken soll,\n"
+ "dass es sich bei dem Wert nicht mehr um eine darstellbare Zahl handelt. In der\n"
+ "Arithmetik moderne Prozessoren ist ein solcher zusätzlicher Wert nicht\n"
+ "vorgesehen. Warum eigentlich nicht?! Es sollte doch möglich sein, einen\n"
+ "Prozessor zu bauen, der beim Überlauf des Wertebereichs nicht stillschweigend\n"
+ "das durch den Überlauf entstandene Bitmuster wieder als Zahl interpretiert,\n"
+ "sondern den ausgezeichneten Wert nan als Ergebnis hat. Ein\n"
+ "Programmierer könnte dann entscheiden, wie er in diesem Fall verfahren\n"
+ "möchte. Eine große Fehlerquelle wäre behoben. Warum ist eine solche\n"
+ "Arithmetik noch nicht in handelsüblichen Prozessoren verwirklicht?\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wollen wir Kommazahlen, also Zahlen aus den \n"
+ "Mengen und , im Rechner darstellen,\n"
+ "so stoßen wir auf ein zusätzliches Problem: es gilt dann nämlich nicht mehr,\n"
+ "daß ein Intervall nur endlich viele Werte enthält, wie es für ganze Zahlen\n"
+ "noch der Fall ist. Bei ganzen Zahlen konnten wir immerhin wenigstens alle\n"
+ "Zahlen eines bestimmten Intervalls darstellen. Wir können also nur endlich\n"
+ "viele \n"
+ "dieser unendlich vielen Werte in einem Intervall darstellen. Wir werden also\n"
+ "das Intervall diskretisieren.\n"
+ "\n"
+ "\n"
+ "Vernachlässigen wir für einen Moment jetzt einmal wieder negative Zahlen. Eine\n"
+ "einfache und naheliegende Idee ist, bei einer Anzahl von n Bit, die\n"
+ "für die Darstellung der Kommazahlen zur Verfügung stehen, einen Teil\n"
+ "davon für den Anteil\n"
+ "vor dem Komma und den Rest für den Anteil nach dem Komma zu benutzen. Liegt\n"
+ "das Komma in der Mitte, so können wir eine Zahl aus n Ziffern durch\n"
+ "folgende Formel ausdrücken:\n"
+ "\n"
+ "Der Wert der Zahl einer \n"
+ "Zeichenkette an-1an/2,an/2-1a0 berechnet sich für die Basis b nach folgender\n"
+ "Formel: \n"
+ "\n"
+ "i=0n-1 aii-n/2\n"
+ "= a0*b-n/2 + + an-1*bn-1-n/2\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir kennen diese Darstellung aus dem im Alltag gebräuchlichen Zehnersystem.\n"
+ "Für Festkommazahlen ergibt sich ein überraschendes Phänomen. Zahlen, die sich\n"
+ "bezüglich einer Basis darstellen lassen, sind bezüglich einer anderen Basis\n"
+ "nicht darstellbar. So läßt sich schon die sehr einfach zur Basis 10\n"
+ "darstellbare Zahl 0,1 nicht zur Basis 2 als Festkommazahl darstellen,\n"
+ "und umgekehrt können Sie einfach zur Basis 2 als Festkommazahl darstellbare\n"
+ "Zahlen nicht zur Basis 10 darstellen.\n"
+ "\n"
+ "Dem Leser wird natürlich nicht entgangen sein, daß wir keine irrationale Zahl \n"
+ "über die Festkommadarstellung darstellen können.\n"
+ "\n"
+ "Festkommazahlen spielen in der Rechnerarchitektur kaum eine Rolle. Ein sehr\n"
+ "verbreitetes Anwendungsgebiet für Festkommazahlen sind Währungsbeträge. Hier\n"
+ "interessieren in der Regel nur die ersten zwei oder drei Dezimalstellen nach\n"
+ "dem Komma.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Alternative zu der Festkommadarstellung von Zahlen ist die\n"
+ "Fließkommadarstellung. Während die Festkommadarstellung einen Zahlenbereich\n"
+ "der rationalen Zahlen in einem festen Intervall durch diskrete, äquidistant\n"
+ "verteilte Werte darstellen kann, sind die diskreten Werte in der\n"
+ "Fließkommadarstellung nicht gleich verteilt.\n"
+ "\n"
+ "In der Fließkommadarstellung wird eine Zahl durch zwei Zahlen charakterisiert\n"
+ "und ist bezüglich einer Basis b: \n"
+ "\n"
+ "die Mantisse für die darstellbaren Ziffern. Die Mantisse\n"
+ "charakterisiert die Genauigkeit der Fließkommazahl.\n"
+ "der Exponent, der angibt, wie weit die Mantisse hinter bzw. vor dem\n"
+ "Komma liegt. \n"
+ "\n"
+ "\n"
+ "Aus Mantisse m, Basis b und Exponent exp ergibt sich\n"
+ "die dargestellte Zahl durch folgende Formel:\n"
+ "z = m * b exp\n"
+ "\n"
+ " \n"
+ "Damit lassen sich mit Fließkommazahlen sehr große und sehr kleine Zahlen\n"
+ "darstellen. Je größer jedoch die Zahlen werden, desto weiter liegen sie von der\n"
+ "nächsten Zahl entfernt.\n"
+ "\n"
+ "\n"
+ "Für die Fließkommadarstellung gibt es in Java zwei Zahlentypen, die nach der\n"
+ "Spezifikation des IEEE 754-1985 gebildet werden:\n"
+ "\n"
+ "float: 32 Bit Fließkommazahl nach IEEE 754. Kleinste positive\n"
+ "Zahl: 2-149. \n"
+ "Größte positive \n"
+ "Zahl: (2-2-23)*127\n"
+ "\n"
+ "\n"
+ "double: 64 Bit Fließkommazahl nach IEEE 754. Kleinste positive\n"
+ "Zahl: 2-1074. \n"
+ "Größte positive \n"
+ "Zahl: (2-2-52)*1023\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im Format für double steht das erste Bit für das Vorzeichen, die\n"
+ "nächsten 11 Bit markieren den Exponenten und die restlichen 52 Bit kodieren\n"
+ "die Mantisse. \n"
+ "\n"
+ "Im Format für float steht das erste Bit für das Vorzeichen, die\n"
+ "nächsten 8 Bit markieren den Exponenten und die restlichen 23 Bit kodieren\n"
+ "die Mantisse. \n"
+ "\n"
+ "\n"
+ "Bestimmte Bitmuster charakterisieren einen Wert für negative\n"
+ "und positive unbeschränkte Werte (unendlich) sowie Zahlen, Bitmuster, die\n"
+ "charakterisieren, daß es sich nicht mehr um eine Zahl handelt.\n"
+ "\n"
+ "\n"
+ "Der folgende Test zeigt, daß bei einer Addition von zwei Fließkommazahlen die\n"
+ "kleinere Zahl das Nachsehen hat: \n"
+ "\n"
+ "\n"
+ "\n"
+ "Wie man an der Ausgabe erkennen kann: selbst die Addition der \n"
+ "Zahl 100000 bewirkt keine Veränderung auf einer großen\n"
+ "Fließkommazahl: \n"
+ "\n"
+ "\n"
+ " java DoubleTest\n"
+ "3.25E202\n"
+ "3.25E-198\n"
+ "3.25E202\n"
+ "3.25E202\n"
+ "sep@linux:~/fh/prog3/examples/src>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das folgende kleine Beispiel zeigt, inwieweit und für den Benutzer oft auf\n"
+ "überraschende Weise die Fließkommadarstellung zu Rundungen führt:\n"
+ "\n"
+ "Das Programm hat die folgende Ausgabe. Insbesondere in der letzten Zeile\n"
+ " fällt auf, daß Addition und anschließende Subtraktion ein und derselben Zahl\n"
+ " nicht die Identität ist. Für Fließkommazahlen gilt \n"
+ "nicht: x + y - y = x.\n"
+ " java Rounded\n"
+ "8.0\n"
+ "88.0\n"
+ "8888.0\n"
+ "88888.0\n"
+ "888888.0\n"
+ "8888888.0\n"
+ "8.8888888E7\n"
+ "8.888889E8\n"
+ "8.8888893E9\n"
+ "8.8888885E10\n"
+ "8.8888889E11\n"
+ "8.8888889E12\n"
+ "0.0\n"
+ "sep@linux:~/fh/prog3/examples/src>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "C kennt vier verschiedene Basistypen um Zahlen darzustellen. Zwei Typen\n"
+ "zur Darstellung ganzer Zahlen und zwei zur Darstellung von Fließkommazahlen.\n"
+ "Eine Übersicht findet sich auch in Anhang .\n"
+ "\n"
+ " \n"
+ "\n"
+ "Ganze Zahlen werden mit dem \n"
+ "Typ int bezeichnet. Wieviel Byte im Speicher zur Darstellung einer\n"
+ "ganzen Zahl tatsächlich benutzt wird, ist im C-Standard nicht\n"
+ "spezifiziert. Es hängt von der benutzten Hardware und dem benutzten Compiler\n"
+ "ab. \n"
+ "\n"
+ "Der zweite Typ, der in der Lage ist, ganze Zahlen zu speichern, ist der \n"
+ "Typ char. Wie der Name (character, Zeichen) schon andeutet, wird\n"
+ "dieser Typ primär benutzt um alphabetische Zeichen zu speichern. Er kann aber\n"
+ "ebenso zum Rechnen mit Zahlen benutzt werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "C bietet zwei Typen zur Speicherung von Fließkommazahlen \n"
+ "an: float und double. Ihr Unterschied liegt in dem Platz,\n"
+ "der im Speicher benutzt wird. In der Regel wird für den \n"
+ "Typ float weniger Speicherplatz benutzt, es kann aber Compiler geben,\n"
+ "die für beide Typen die gleiche interne Darstellung benutzen.\n"
+ "\n"
+ "Beim Rechnen mit Fließkommazahlen sollte man heutzutage immer den \n"
+ "Typ double verwenden\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Ganze Zahlen können in C auf drei verschiedene Weise notiert werden. Diese\n"
+ "drei Arten der Literale unterscheiden sich in der Basis zu der die Zahlen\n"
+ "notiert sind.\n"
+ "\n"
+ "\n"
+ "zur Basis 10 (dezimal): beginnt mit einer Ziffer ungleich 0 gefolgt\n"
+ " von einer\n"
+ "(möglicherweise leeren) Folge von Ziffern 0--9.\n"
+ "\n"
+ "zur Basis 16 (hexadecimal):beginnt \n"
+ "mit 0x oder 0X gefolgt von einer nichtleeren Folge von\n"
+ "hexadezimalen Ziffern 0--9,A,B,C,D,E,F. \n"
+ "zur Basis 16 (oktal): beginnt mit einer 0 gefolgt von einer\n"
+ "(möglicherweise leeren) Folge von Ziffern 0--7.\n"
+ "\n"
+ "\n"
+ "So kann die Zahl 63 in C \n"
+ "als 63, 0x3F und 077 notiert werden.\n"
+ "\n"
+ "\n"
+ "Den ganzzahligen Literalen kann ein Buchstaben angehänt sein, der den Datentyp\n"
+ "des Literals genauer spezifiziert. \n"
+ "
\n"
+ "u oder Umarkiert das Literal\n"
+ "als unsigned\n"
+ "l oder Lmarkiert das Literal\n"
+ "als long\n"
+ "ll oder LLmarkiert das Literal\n"
+ "als long long (erst seit dem Standard C99)\n"
+ "
\n"
+ "\n"
+ "\n"
+ "\n"
+ "Fließkommaliterale benutzen den Punkt . als Dezimalkomma. Mit dem\n"
+ "Buchstaben e oder E kann ein ganzzahliger Exponent notiert\n"
+ "werden. So bezeichnen die folgenden Literale Fließkommazahlen vom \n"
+ "Typ double: 3.1415926535897932, 4.0, 6.022e+23.\n"
+ "\n"
+ "Hängt an einem Fließkommaliteral noch der Buchstabe f, so wird das\n"
+ "Literal als Zahl vom Typ float gespeichert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Einzelne Zeichen des Typs char können in einfachen Hochkommas\n"
+ "eingeschlossen notiert werden. Zeichen, für die es keine einfache sichtbare\n"
+ "Repräsentation gibt, wie z.B. das Zeilenendezeichen, werden mit einer\n"
+ "Fluchtsequenz beginnend mit dem rückwärtigen Schrägstrich bezeichnet.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Von den bisher vorgestellten primitiven Typen gibt es modifizierte\n"
+ "Versionen. Zum einen können sie modifiziert werden, in der Speichergröße, die\n"
+ "für sie verwendet wird, zum anderen ob sie Vorzeichen behaftete Zahlen\n"
+ "speichern können oder nicht.\n"
+ "\n"
+ "\n"
+ "Der Typ int kann noch mit den Attribute long oder short behaftet sein. Das Attribut long bewirkt dann, dass\n"
+ " auf bestimmten Systemen eventuell \n"
+ "mehr Byte zur Darstellung einer Zahl genutzt werden.\n"
+ "Garantiert doppelt so viel Byte wie ein int hat schließlich\n"
+ "der Typ long long int.\n"
+ "\n"
+ "Das Attribut short signalisiert, dass nur halb soviel Byte zu\n"
+ "Zahlendarstellung genutzt werden sollen, wie für normale int Zahlen.\n"
+ "Ebenso kann ein System einen long double Typ zur Verfügung stellen,\n"
+ "der größer ist als der normale double Datentyp.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Datentypen für ganze Zahlen können so modifiziert werden, dass in Ihnen\n"
+ "nur natürliche Zahlen (inklusive 0) gespeichert werden können. Dieses\n"
+ "geschieht durch das Attribut unsigned. Ebenso mit dem \n"
+ "Attribute signed signalisiert werden, dass z.B. beim \n"
+ "Datentyp char auch negative Zahlen darstellbar sein sollen.\n"
+ "Für die Typen float und double führt das \n"
+ "Attribut unsigned zu einem Fehler des Compilers.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Den ganzzahligen Literalen kann ein Buchstaben angehänt sein, der den Datentyp\n"
+ "des Literals genauer spezifiziert. \n"
+ "
\n"
+ "u oder Umarkiert das Literal\n"
+ "als unsigned\n"
+ "l oder Lmarkiert das Literal\n"
+ "als long\n"
+ "ll oder LLmarkiert das Literal\n"
+ "als long long (erst seit dem Standard C99)\n"
+ "
\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wie zu sehen war, ist es gar nicht genau festgelegt, wie viel Byte zur\n"
+ "Darstellung der einzelnen primitiven Typen benutzt werden. Das kann von System\n"
+ "zu System variieren. Will man wissen, wieviel Speicherplatz für bestimmte\n"
+ "Datentypen tatsächlich bereit gehalten werden, so bietet C eine eingebaute\n"
+ "Funktion an, die dieses Information bereit hält: die Funktion sizeof.\n"
+ "Ihr kann als Parameter ein primitiver Typ übergeben werden. Als Rückgabe\n"
+ "erhält man die Anzahl der Byte, die für diesen Typ zur Darstellung verwendet\n"
+ "werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Zur Ausgabe von Zahlen benutzen wir die Funktion printf. Bisher\n"
+ "haben wir als Steuerzeichen für printf nur %i benutzt um\n"
+ "ganze Zahlen auszugeben. Die Prozedur printf ist eine sehr\n"
+ "ausgeklügelte Prozedur. In ihr kann sehr genau spezifiziert werden, auf welche\n"
+ "Weise Zahlen formattiert werden sollen. Zusätzlich zum angegebenen Zahlentypen\n"
+ "der dargestellt werden soll, kann auch angegeen werden, wieviel Platz zur\n"
+ "Darstellung der Zahl benutzt werden soll. Folgende Tabelle gibt einen kleinen Überblick:\n"
+ "\n"
+ "
\n"
+ "\n"
+ "\n"
+ " Zeichen\n"
+ " Argumenttyp\n"
+ " darstellt als\n"
+ "\n"
+ "\n"
+ "d,iint Dezimal mit Vorzeichen.\n"
+ "\n"
+ "\n"
+ "o intOktal ohne Vorzeichen (ohne führende Null).\n"
+ "\n"
+ "\n"
+ "x,X int\n"
+ "Hexadezimal ohne Vorzeichen (ohne führendes 0x oder 0X) mit abcdef bei 0x oder\n"
+ " ABCDEF bei 0X.\n"
+ "\n"
+ "\n"
+ "u intDezimal ohne Vorzeichen.\n"
+ "\n"
+ "\n"
+ "c int einzelnes Zeichen, nach Umwandlung in unsigned char.\n"
+ "\n"
+ "\n"
+ "s char*aus einer Zeichenkette werden Zeichen ausgegeben bis vor '\\0' oder so viele Zeichen, wie die Genauigkeit verlangt.\n"
+ "\n"
+ "\n"
+ "f doubleDezimal als [-]mmm.ddd, wobei die Genauigkeit die\n"
+ "Anzahl der d festlegt. Voreinstellung ist 6; bei 0 entfällt der Dezimalpunkt.\n"
+ "\n"
+ "\n"
+ "\n"
+ "e,E double Dezimal als [-]m.dddddde±xx oder [-]m.ddddddE±xx,\n"
+ " wobei die Genauigkeit die Anzahl der d festlegt. Voreinstellung ist 6; bei 0\n"
+ " entfällt der Dezimalpunkt. \n"
+ "\n"
+ "\n"
+ "g,Gdouble \n"
+ "\\%e oder \\%E wird verwendet, wenn der\n"
+ " Exponent kleiner als -4 oder nicht kleiner als die Genauigkeit ist; sonst\n"
+ " wird \\%f benutzt. Null und Dezimalpunkt am Schluß werden nicht\n"
+ " ausgegeben. \n"
+ "\n"
+ "
\n"
+ "\n"
+ "\n"
+ "Es ist an der Zeit, all das in diesem Kapitel angesprochene einmal\n"
+ "auszuprobieren. Im folgenden Programm werden von den unterschiedlichen\n"
+ "primitiven Typen einmal Variablen angelegt.\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " char c = 'a';\n"
+ " int i = 42;\n"
+ " float f = 42.0f;\n"
+ " double d = 42.0;\n"
+ "\n"
+ " long int li = 42;\n"
+ " long long int lli = 42;\n"
+ " long double ld = 42.0;\n"
+ "\n"
+ " short int si = 42;\n"
+ "\n"
+ " unsigned char uc = 'a';\n"
+ " unsigned int ui = 42;\n"
+ "\n"
+ " signed char sic = 'a';\n"
+ " signed int sii = 42;\n"
+ "\n"
+ " printf(\"sizeof(c): %3i value: %10c\n\",sizeof(c),c);\n"
+ " printf(\"sizeof(i): %3i value: %10i\n\",sizeof(i),i);\n"
+ " printf(\"sizeof(f): %3i value: %10f\n\",sizeof(f),f);\n"
+ " printf(\"sizeof(d): %3i value: %10f\n\",sizeof(d),d);\n"
+ " printf(\"\n\");\n"
+ " printf(\"sizeof(li): %2i value: %10i\n\",sizeof(li),li);\n"
+ " printf(\"sizeof(lli):%2i value: %10i\n\",sizeof(lli),lli);\n"
+ " printf(\"sizeof(ld): %2i value: %10f\n\",sizeof(ld),ld);\n"
+ " printf(\"\n\");\n"
+ " printf(\"sizeof(si): %2i value: %10i\n\",sizeof(si),si);\n"
+ " printf(\"\n\");\n"
+ " printf(\"sizeof(uc): %2i value: %10u\n\",sizeof(uc),uc);\n"
+ " printf(\"sizeof(ui): %2i value: %10u\n\",sizeof(ui),ui);\n"
+ " printf(\"\n\");\n"
+ " printf(\"sizeof(sic): %1i value: %10i\n\",sizeof(sic),sic);\n"
+ " printf(\"sizeof(sii): %1i value: %10i\n\",sizeof(sii),sii);\n"
+ "\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Die Ausgabe des Programms gibt darüber Aufschluß, wie viel Byte für die\n"
+ "einzelnen unterschiedlichen Datentypen der gcc auf Linux belegt.\n"
+ "\n"
+ " bin/ArithTypes\n"
+ "sizeof(c): 1 value: a\n"
+ "sizeof(i): 4 value: 42\n"
+ "sizeof(f): 4 value: 42.000000\n"
+ "sizeof(d): 8 value: 42.000000\n"
+ "\n"
+ "sizeof(li): 4 value: 42\n"
+ "sizeof(lli): 8 value: 42\n"
+ "sizeof(ld): 12 value: -0.000000\n"
+ "\n"
+ "sizeof(si): 2 value: 42\n"
+ "\n"
+ "sizeof(uc): 1 value: 97\n"
+ "sizeof(ui): 4 value: 42\n"
+ "\n"
+ "sizeof(sic): 1 value: 97\n"
+ "sizeof(sii): 4 value: 42\n"
+ "sep@pc305-3:~/fh/c/tutor>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Auf dem Typen int haben wir bereits die bekannten arithmetischen\n"
+ "Operatoren kennengelernt. Diese existieren auch für die anderen Zahlentypen\n"
+ "und erstaunlicher Weise auch für den Typen char. So kann man eine\n"
+ "Variable sehr schön von A bis Z laufen lassen:\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " char abstand='A'-'a';\n"
+ " for (char c='A';c<='Z';c++) printf(\"%c\",c-abstand);\n"
+ " printf(\"\n\");\n"
+ " return 0;\n"
+ "}]]> \n"
+ "\n"
+ "\n"
+ "\n"
+ "Zahlen, die von einem Typ mit einem kleineren Zahlenbereich sind, können\n"
+ "problemlos als Zahlen eines Typs mit einem größeren Zahlenbereich betrachtet\n"
+ "werden. Umgekehrt ist das nicht so. Eine int-Zahl kann so zum\n"
+ "Beispiel zu groß sein, um als ein char-Wert gespeichert zu werden.\n"
+ "Es ist also Vorsicht walten zu lassen, wenn man zwischen den verschiedenen\n"
+ "Typen die Daten hin und her bewegt.\n"
+ "\n"
+ "Es gibt eine explizite Notation, um eine Zahl zu einem anderen Zahlentypen zu\n"
+ "konvertieren. Hierzu wird in runden Klammern der neue gewünschte Typ für einen\n"
+ "Ausdruck vor diesen geschrieben. Diese Operation wird \n"
+ "als cast bezeichnet. Man muß bei dieser expliziten Typkonvertierung\n"
+ "genau darauf acht geben, welchen Ausdruck man genau konvertieren \n"
+ "möchte. So bedeutet (double)(3/2), dass erst die ganzzahlige \n"
+ "Division durchzuführen ist und dieses Ergebnis dann als Fließkommazahl\n"
+ "betrachtet wird; dahingegen bedeutet (double)3/2), dass zunächst \n"
+ "die 3 als Fließkommazahl zu betrachten ist und dann für\n"
+ "Fließkommazahlen die Division durchzuführen ist. \n"
+ "\n"
+ "Man überzeuge sich davon im folgenden textprogramm:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " int x = 224;\n"
+ " double d = x;\n"
+ " char c = x;\n"
+ "\n"
+ " printf(\"%i\n\",x);\n"
+ " printf(\"%f\n\",d);\n"
+ " printf(\"%i\n\",c);\n"
+ "\n"
+ " printf(\"%f\n\",(double)(3/2));\n"
+ " printf(\"%f\n\",(double)3/2);\n"
+ " printf(\"%f\n\",3/2);\n"
+ " printf(\"%i\n\",3/2);\n"
+ " printf(\"%i\n\",(int)(3.0/2.0));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Die Ausgabe offenbahrt den fundamentalen unterschied.\n"
+ "\n"
+ " ./Convert\n"
+ "224\n"
+ "224.000000\n"
+ "-32\n"
+ "1.000000\n"
+ "1.500000\n"
+ "1.500000\n"
+ "1\n"
+ "sep@pc305-3:~/fh/c>]]>\n"
+ "\n"
+ "Tatsächlich ist meine persönliche Erfahrung: bei einem cast lieber\n"
+ "eine Klammer zu viel setzen als zu wenig.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Der folgende Test zeigt, daß bei einer Addition von zwei Fließkommazahlen die\n"
+ "kleinere Zahl das Nachsehen hat: \n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " double x = 325e200;\n"
+ " double y = 325e-200;\n"
+ " double z = -325e+200;\n"
+ " printf(\"x: %6.20e\n\",x);\n"
+ " printf(\"y: %6.20e\n\",y);\n"
+ " printf(\"x+y: %6.20e\n\",(x+y));\n"
+ " printf(\"x+z: %6.20e\n\",(x+z));\n"
+ " printf(\"x+100000: %6.20e\n\",x+100000.0);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Wie man an der Ausgabe erkennen kann: selbst die Addition der \n"
+ "Zahl 100000 bewirkt keine Veränderung auf einer großen\n"
+ "Fließkommazahl: \n"
+ "\n"
+ "\n"
+ " ./DoubleTest\n"
+ "x: 3.24999999999999978946e+202\n"
+ "y: 3.25000000000000017969e-198\n"
+ "x+y: 3.24999999999999978946e+202\n"
+ "x+z: 0.00000000000000000000e+00\n"
+ "x+100000: 3.24999999999999978946e+202\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "Das folgende kleine Beispiel zeigt, inwieweit und für den Benutzer oft auf\n"
+ "überraschende Weise die Fließkommadarstellung zu Rundungen führt:\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"8.0f =%20f \n\",8.0f);\n"
+ " printf(\"88.0f =%20f \n\",88.0f);\n"
+ " printf(\"888.0f =%20f \n\",888.0f);\n"
+ " printf(\"8888.0f =%20f \n\",8888.0f);\n"
+ " printf(\"88888.0f =%20f \n\",88888.0f);\n"
+ " printf(\"888888.0f =%20f \n\",888888.0f);\n"
+ " printf(\"8888888.0f =%20f \n\",8888888.0f);\n"
+ " printf(\"88888888.0f =%20f \n\",88888888.0f);\n"
+ " printf(\"888888888.0f =%20f \n\",888888888.0f);\n"
+ " printf(\"8888888888.0f =%20f \n\",8888888888.0f);\n"
+ " printf(\"88888888888.0f =%20f \n\",88888888888.0f);\n"
+ " printf(\"888888888888.0f =%20f \n\",888888888888.0f);\n"
+ "\n"
+ " printf(\"1.0f+1000000000000.0f-1000000000000.0f =%15f \n\"\n"
+ " ,1.0f+1000000000000.0f-1000000000000.0f);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "Das Programm hat die folgende Ausgabe. Insbesondere in der letzten Zeile\n"
+ " fällt auf, daß Addition und anschließende Subtraktion ein und derselben Zahl\n"
+ " nicht die Identität ist. Für Fließkommazahlen gilt \n"
+ "nicht: x + y - y = x.\n"
+ " ./bin/Rounded\n"
+ "8.0f = 8.000000\n"
+ "88.0f = 88.000000\n"
+ "888.0f = 888.000000\n"
+ "8888.0f = 8888.000000\n"
+ "88888.0f = 88888.000000\n"
+ "888888.0f = 888888.000000\n"
+ "8888888.0f = 8888888.000000\n"
+ "88888888.0f = 88888888.000000\n"
+ "888888888.0f = 888888896.000000\n"
+ "8888888888.0f = 8888889344.000000\n"
+ "88888888888.0f = 88888885248.000000\n"
+ "888888888888.0f = 888888885248.000000\n"
+ "1.0f+1000000000000.0f-1000000000000.0f = 0.000000\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Oft reichen die Grundrechenarten nicht aus. Insbesondere in der graphischen\n"
+ "Datenverarbeitung muß an in der Lage sein, die trigeometrischen Funktionen \n"
+ "wie \\cos, \\sin, \\tan,\\dots für Zahlen zu berechnen. Desweiteren werden\n"
+ "oft die Zahl \\pi benötigt. Hierzu gibt es in C eine Standardbibliothek,\n"
+ "in der eine Vielzahl von mathematischen Funktionen enthalten sind. Die\n"
+ "Bibliothek ist in der Kopfdatei math.h definiert.\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "int main(){\n"
+ " printf(\"%f\n\",sin(M_PI));\n"
+ " printf(\"%f\n\",sin(90));\n"
+ " printf(\"%f\n\",sqrt(64));\n"
+ " printf(\"%f\n\",pow(2,10));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Tatsächlich kann es zu Fehlern kommen, wenn der Linker ein Programm mit den\n"
+ "mathematischen Funktionen linken soll. \n"
+ "\n"
+ " gcc src/MathExample.c\n"
+ "/tmp/ccs63nup.o(.text+0x23): In function `main':\n"
+ ": undefined reference to `sin'\n"
+ "/tmp/ccs63nup.o(.text+0x51): In function `main':\n"
+ ": undefined reference to `sin'\n"
+ "/tmp/ccs63nup.o(.text+0x7f): In function `main':\n"
+ ": undefined reference to `sqrt'\n"
+ "/tmp/ccs63nup.o(.text+0xb9): In function `main':\n"
+ ": undefined reference to `pow'\n"
+ "collect2: ld returned 1 exit status\n"
+ "sep@pc305-3:~/fh/c/tutor>]]>\n"
+ " \n"
+ "Hierzu ist dem Compiler für den Linker noch die Information mitzugeben, dass\n"
+ "die entsprechende mathematische Standardbibliothek mit einzulinken ist. Das\n"
+ "entsprechende Flag heisst: -lm.\n"
+ "\n"
+ " gcc -lm src/MathExample.c\n"
+ "sep@pc305-3:~/fh/c/tutor>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion \n"
+ "double kreisUmfang(double radius); \n"
+ "Sie soll für einen gegebenen Radius den Umfang eines Kreises berechnet.\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "double kreisUmfang(double radius){\n"
+ " return 2*M_PI*radius;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"der Umfang bei einem radius 5 ist: %f\n\",kreisUmfang(5));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion \n"
+ "double kreisFlaeche(double radius); \n"
+ "Sie soll für einen gegebenen Radius den Flächeninhalt eines Kreises berechnet.\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "double kreisFlaeche(double radius){\n"
+ " return M_PI* pow(radius,2.0);\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"die Flaeche bei einem radius 5 ist: %f\n\",kreisFlaeche(5));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "(1 Punkt)Schreiben Sie eine Prozedur \n"
+ "void printDual(int i); \n"
+ "Sie soll den Parameter i als Dualzahl auf der Konsole ausdrucken.\n"
+ "\n"
+ "Hinweis: Leiten Sie Ihre Lösung aus der Lösung für die \n"
+ "Funktion alsText ab. Jetzt ist eine Funktion: \n"
+ "int hoechsteZweierPotenz(int i); \n"
+ "hilfreich.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "int hoechsteZweierPotenz(int i){\n"
+ " int result = 1;\n"
+ " while (i>=2){\n"
+ " result=result*2;\n"
+ " i=i/2;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "void printDual(int i){\n"
+ " if (i<0){ \n"
+ " printf(\"-\");\n"
+ " i= -i;\n"
+ " }\n"
+ "\n"
+ " for (int hoechsteStelle = hoechsteZweierPotenz(i)\n"
+ " ;hoechsteStelle>0\n"
+ " ;hoechsteStelle=hoechsteStelle/2) {\n"
+ " printf(\"%i\",i/hoechsteStelle);\n"
+ " i=i%hoechsteStelle;\n"
+ " }\n"
+ " printf(\"\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printDual(-142);\n"
+ " printDual(1024);\n"
+ " printDual(0);\n"
+ " printDual(-0);\n"
+ " printDual((17+4)*2);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir schon im letzten Abschnitt zu sehen war, können in C Typennamen aus\n"
+ "mehreren Wörtern bestehen. Wie noch zu sehen wird, können in C Typnamen noch\n"
+ "weitgehend komplizierter zu notieren sein. Daher ist es eine schöne\n"
+ "Eigenschaft von C, dass man neue Namen für Typen definieren kann. Hierzu gibt\n"
+ "es das typedef-Konstrukt. Nach dem \n"
+ "Wort typedef ist der Typ zu schreiben, für den ein neuer Name\n"
+ "eingeführt wird. Schließlich ist der neue Typnme zu schreiben.\n"
+ "\n"
+ "Im folgenden kleinen Programm wird der neue Name nat für den\n"
+ "Typ unsigned int eingeführt. Von da an kann im Programm der neue \n"
+ "Name nat statt des komplizierteren \n"
+ "Namen unsigned int benutzt werden.\n"
+ "\n"
+ "\n"
+ "\n"
+ "typedef unsigned int nat;\n"
+ "\n"
+ "int main(){\n"
+ " nat n=42;\n"
+ " printf(\"n = %u\n\",n);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Das typedef-Konstrukt in C ist äußerst sinnvoll. der Vorteil liegt\n"
+ "nicht nur in einfacheren sprechenden Namen, die für Typen eingeführt werden\n"
+ "können. Es ist so auch möglich den physikalischen Typ flexibel zu halten. So\n"
+ "kann ein Name für \n"
+ "\n"
+ "\n"
+ "\n"
+ "Bisher haben wir gänzlich auf den eingebauten primitiven Typen in C für Zahlen\n"
+ "aufgebaut. Bei der Programmierung von Anwendungen werden zumeist aber Daten\n"
+ "bearbeitet, die nicht nur aus einer Zahl bestehen, sondern oft ein Tupel aus\n"
+ "mehereren Zahlen sind. So könnte es z.B.~sein, dass wir als Aufgabe ein\n"
+ "Programm für eine Wetterstation schreiben sollen. Die Wetterstation ermittelt\n"
+ "dabei mehrere unterschiedliche Messwerte. Es wird z.B.~Temperatur, Luftdruck,\n"
+ "Luftfeuchtigkeit und die Windstärke gemessen. Die Wetterstation liefert zu\n"
+ "einem Zeitpunkt also vier Meßzahlen mit unterschiedlicher Bedeutung. Diese\n"
+ "vier Zahlen zusammen sind damit ein Datensatz der Wetterstation. Gerne wollen\n"
+ "wir diese vier Zahlen nun auch als ein einziges Datenobjekt betrachten\n"
+ "können. Hierzu bietet C die Möglichkeit über eine Struktur, den \n"
+ "sogenannten structs einen\n"
+ "neuen Typ zu definieren, der aus mehreren bereits bestehenden Typen\n"
+ "zusammengesetzt ist. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Struktur wird definiert mit dem Schlüsselwort struct, dann folgt\n"
+ "der frei wählbare Name, den die Struktur haben soll. Anschließend wird in\n"
+ "geschweiften Klammern der Inhalt der Struktur definiert. Der Inhalt sind im\n"
+ "Prinzip die Definition struktureigener Variablen. \n"
+ "\n"
+ "So kann die Struktur definiert werden, die die vier verschiedenen Messwerte\n"
+ "einer Wetterstation zusammenfasst:\n"
+ "\n"
+ "\n"
+ "\n"
+ "struct Messwert{\n"
+ " int temperatur;\n"
+ " int luftfeuchtigkeit;\n"
+ " int luftdruck;\n"
+ " int windstaerke;\n"
+ "};]]> \n";
String skript2 =
"\n"
+ "Die Definition einer Struktur endet immer noch mit einem abschließenden\n"
+ "Semikolon. Dieses wird häufig vergessen und führt mitunter zu verwirrenden\n"
+ "Fehlermeldungen.\n"
+ "\n"
+ "Strukturen werden in anderen Programmiersprachen üblicher Weise auch \n"
+ "als record bezeichnet.\n"
+ "\n"
+ "Die einzelnen Teile einer Struktur werden auch als ihre Attribute oder auch\n"
+ "als ihre Felder bezeichnet. In dieser Terminologie hat die \n"
+ "Struktur Messwert vier Felder, bzw. vier Attribute.\n"
+ "\n"
+ "Nachdem einmal eine Struktur definiert wurde, steht sie als neuer\n"
+ "Typname zur Verfügung. Der neue Typname ist dann in unserem \n"
+ "Beispiel: struct Messwert, besteht also aus zwei Wörtern, so wie ja\n"
+ "auch schon der Typname für natürliche Zahlen aus zwei den \n"
+ "Wörtern unsigned int besteht. \n"
+ "\n"
+ "Dieser neue eigene Typ kann nun genauso verwendet werden, \n"
+ "wie z.B.~Typ int bisher: Variablen von diesem Typ können definiert\n"
+ "werden und Parameter von diesem Typ können definiert werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "Wir haben uns angewöhnt, möglichst gleich bei der Delkaration einer Variablen,\n"
+ "dieser Variablen auch einen Wert zu geben. Das geht auch mit Variablen von\n"
+ "einem struct-Typ. Hierzu sind dann die Werte der einzelnen Attribute\n"
+ "aufzuzählen. Diese Aufzählung ist durch Komma getrennt innerhalb geschweifter\n"
+ "Klammern vorzunehmen. Die Reihenfolge der einzelnen Werte entspricht der\n"
+ "Reihenfolge, in der die Attribute in der Struktur definiert wurden.\n"
+ "\n"
+ " \n"
+ "\n"
+ "Hiermit wurde ein Objekt der Struktur Messwert angelegt, das eine\n"
+ "Temperaturwert von 22, Feuchtigkeitswert von 41, einen\n"
+ "Luftdruck von 997 und eine Windstärke von 8 speichert.\n"
+ "\n"
+ "Ebenso wie bei einfachen Variablen vom Typ int können wir zunächst\n"
+ "aber auch erst einmal die Variable deklarieren und dann schrittweise die\n"
+ "einzelnen Werte zuweisen. Hierzu gibt es den Punkt-Operator, mit dem man auf\n"
+ "die einzelnen Attribute einer Struktur einzeln zugreifen und diese dann\n"
+ "einzeln auch mit Werten belegen kann:\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "Hiermit wurde ein zweites Objekt der Struktur Messwert angelegt und die\n"
+ "einzelnen Felder mit Werten belegt.\n"
+ "\n"
+ "\n"
+ " \n"
+ "Die Punkt-Notation kann natürlich nicht nur genutzt werden, um den einzelnen\n"
+ "Felder einer Struktur Werte zuzuweisen, sondern auch um auf die darin\n"
+ "gespeicherten Werte zuzugreifen. So können wir die Werte einzelner Felder\n"
+ "unserer Strukturobjekte ausgeben:\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ " \n"
+ "Wir haben gelernt, dass C bei Funktionsaufrufen eine Parameterübergabe per\n"
+ "Wert vornimmt. Das ist für einfache Zahlen, also int-Parameter recht\n"
+ "einsichtig. Wie sieht dieses aber für Variablen eines Strukturtyps aus? Wir\n"
+ "können dies einmal mit einer simplen Struktur testen. Hierzu sei eine Struktur\n"
+ "definiert, in der die x- und y-Koordinate eines Punktes im\n"
+ "zweidimensionalen Raum gespeichert werden können:\n"
+ "\n"
+ "\n"
+ "struct Punkt{\n"
+ " int x;\n"
+ " int y;\n"
+ "};]]>\n"
+ "\n"
+ "Für diese Struktur seien ein Paar Funktionen definiert. Zunächst eine schöne\n"
+ "Ausgabe eines Punktes auf der Kommandozeile:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine zweite Funktion soll einen Punkt um eins auf der x-Achse und um 2 auf der\n"
+ "y-Achse verschieben.\n"
+ "\n"
+ "\n"
+ "Nun seien die beiden Funktionen einmal ausgetestet. Hierzu legen wir einen\n"
+ "Punkt an, geben diesen aus, verschieben ihn und geben ihn erneut aus:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Tatsächlich wird zweimal der gleiche Punkt ausgegeben:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Funktion verschiebe hat den Punkt p gar nicht\n"
+ "verändert. Was man hier sieht, ist, dass auch Strukturobjekte in C per Wert\n"
+ "übergeben werden. Da eine Struktur über ihre Felder mehrere Werte\n"
+ "repräsentiert, werden also tatsächlichen sämtliche Werte eines\n"
+ "Strukturobjektes übergeben. Anders ausgedrückt: es wird eine komplette Kopie\n"
+ "des Strukturobjektes vorgenommen.Bei Javaprogrammierern, die\n"
+ "Neulinge in C \n"
+ "sind, führt in der Regel das obige Testprogramm zu einiger \n"
+ "Bestürzung. Jeder Aufruf einer Funktion in C mit einer Struktur als\n"
+ "Parameter führt zur Erzeugung einer Kopie dieser Struktur. Die Kopie ist dann\n"
+ "vollkommen unabhängig zum Originalstrukturobjekt.\n"
+ "\n"
+ "\n"
+ "Da Parameter auch nur eine Form von Variable sind, gilt obiges in gleicher\n"
+ "Weise für die Zuweisung einer Variable. Auch dabei wird eine Kopie der Daten\n"
+ "vorgenommen.\n"
+ "\n"
+ "\n"
+ "Hierzu betrachte man folgendes Programm:\n"
+ "\n"
+ "\n"
+ "struct Punkt{\n"
+ " int x;\n"
+ " int y;\n"
+ "};\n"
+ "void printPunkt(struct Punkt p){\n"
+ " printf(\"(%i,%i)\n\",p.x,p.y);\n"
+ "}\n"
+ "int main(){\n"
+ " struct Punkt p1={17,4};\n"
+ " struct Punkt p2=p1;\n"
+ " printPunkt(p1);\n"
+ " printPunkt(p2);\n"
+ " p2.x=0;\n"
+ " printPunkt(p1);\n"
+ " printPunkt(p2);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Mit der Zuweisung p2=p1 werden die Werte des \n"
+ "Strukturobjekts p1 genommen um ein neues \n"
+ "Strukturobjekt p2 zu erzeugen. Von da an, \n"
+ "haben p1 und p2 nichts mehr miteinander zu tun. Wird eine\n"
+ "der Variablen verändert, so bleibt die andere davon unberührt, wie an der\n"
+ "Ausgabe des Programms zu sehen ist:\n"
+ "\n"
+ " bin/Punkt2\n"
+ "(17,4)\n"
+ "(17,4)\n"
+ "(17,4)\n"
+ "(0,4)\n"
+ "sep@pc305-3:~/fh/c/tutor>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "Die Typnamen von Strukturen bestehen in C aus zwei Wörtern: dem \n"
+ "Wort struct und dem eigentlichen Namen der Struktur. Das ist relativ\n"
+ "umständlich. Man kann sich hier das Leben durch einen Trick vereinfachen,\n"
+ "indem man gleichzeitig mit der Deklaration einer Struktur über \n"
+ "das typedef-Konstrukt einen Kurznamen für diese Struktur\n"
+ "erfindet. Hierzu betrachte man noch einmal das Beispiel der simplen Struktur\n"
+ "für zweidimensionale Punkte.\n"
+ "\n"
+ "\n"
+ "\n"
+ "typedef struct {\n"
+ " int x;\n"
+ " int y;\n"
+ "} Punkt;\n"
+ "\n"
+ "void printPunkt( Punkt p){\n"
+ " printf(\"(%i,%i)\n\",p.x,p.y);\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " Punkt p={17,4};\n"
+ " printPunkt(p);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Hier konnte bei der Deklaration von Variablen des Typs Punkt auf \n"
+ "das Wort struct verzichtet werden.\n"
+ "\n"
+ "\n"
+ "(1 Punkt) \n"
+ "Auf diesen Übungsblatt sollen Sie eine kleine Bibliothek für komplexe Zahlen\n"
+ " schreiben und austesten. Komplexe Zahlen werden meist in der \n"
+ "Form a+b * i\n"
+ "dargestellt, wobei a und b reelle Zahlen sind und i die\n"
+ " imaginäre Einheit ist. a wird dabei als Realteil \n"
+ "und b als Imaginärteil bezeichnet.\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Struktur, mit der komplexe Zahlen dargestellt werden\n"
+ "kann. Die Struktur soll unter den Typnamen Complex benutzbar \n"
+ "sein.\n"
+ "Schreiben Sie eine Funktion void printComplex(Complex c), die\n"
+ "eine komplexe Zahl auf der Kommandozeile ausgibt.\n"
+ "Für komplexe Zahlen sind Addition und Multiplikation wie folgt\n"
+ "definiert:\n"
+ "\n"
+ "(a+b\\,\\mathrm i)+(c+d\\,\\mathrm i)=(a+c)+(b+d)\\,\\mathrm i\n"
+ "(a + b \\, \\mathrm i) - (c + d \\, \\mathrm i) = (a - c) + (b - d) \\, \\mathrm i\n"
+ "(a+b\\,\\mathrm{i})\\cdot(c+d\\,\\mathrm{i})=(ac-bd) + (ad+bc)\\cdot\\mathrm i\n"
+ "\\frac{a+b\\,\\mathrm i}{c+d\\,\\mathrm i} = \\frac{(a+b\\,\\mathrm i)(c-d\\,\\mathrm i)}{(c+d\\,\\mathrm i)(c-d\\,\\mathrm i)} = \\frac{ac+bd}{c^2+d^2}+\\frac{bc-ad}{c^2+d^2}\\cdot\\mathrm i\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Implementieren Sie die Funktionen add, sub, mult und div, die zwei\n"
+ "komplexe Zahlen als Argument haben und eine komplexe Zahl \n"
+ "nach obigen Definitionen als Ergebnis zurückgeben.\n"
+ "\n"
+ "\n"
+ "Die Norm einer\n"
+ " komplexen Zahl erhält man, wenn\n"
+ " zum Quadrat des Realteils das Quadrat des Imaginärteils\n"
+ " addiert wird. Schreiben Sie eine Funktion norm, die für\n"
+ "eine komplexe Zahl ihre Norm berechnet.\n"
+ "\n"
+ "\n"
+ "Für die Erzeugung der als Apfelmännchen bekannten \n"
+ "Fraktale spielt die folgende Rekursionsgleichung auf komplexen Zahlen eine\n"
+ "entscheidene Rolle: z_{n+1} = z_n^2 + c, wobei z_0 die \n"
+ "komplexe Zahl 0+0i mit dem Real-\n"
+ "und Imaginärteil 0 ist. \n"
+ "Schreiben Sie eine \n"
+ "Funktion \n"
+ "unsigned int schwelle(Complex c,double schwellwert) \n"
+ "die das kleinste n berechnet, für das \n"
+ "gilt: \\mathrm norm(z_n)> \\mathrm schwellwert.\n"
+ "\n"
+ "Schreiben Sie umfangreiche Tests in einer Funktion main.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "void printComplex(Complex c){\n"
+ " printf(\"(%f,%f)\",c.real,c.imag);\n"
+ "}\n"
+ "\n"
+ "Complex add(Complex x,Complex y){\n"
+ " Complex result = {x.real+y.real,x.imag+y.imag};\n"
+ " return result;\n"
+ "}\n"
+ "Complex sub(Complex x,Complex y){\n"
+ " Complex result = {x.real-y.real,x.imag-y.imag};\n"
+ " return result;\n"
+ "}\n"
+ "Complex mult(Complex x,Complex y){\n"
+ " Complex result = {x.real*y.real-x.imag*y.imag\n"
+ " ,x.real*y.imag+x.imag*y.real};\n"
+ " return result;\n"
+ "}\n"
+ "Complex divC(Complex x,Complex y){\n"
+ " Complex result = { (x.real*y.real+x.imag*y.imag)\n"
+ " /(y.imag*y.imag+y.real*y.real)\n"
+ " , (x.imag*y.real-x.real*y.imag)\n"
+ " /(y.imag*y.imag+y.real*y.real)};\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "double norm(Complex x){\n"
+ " return x.real*x.real+x.imag*x.imag;\n"
+ "}\n"
+ "unsigned int schwelle(Complex c,double schwellwert){\n"
+ " unsigned int result = 0;\n"
+ " Complex z = {0,0};\n"
+ " while(!(norm(z)> schwellwert) && result<50){\n"
+ " z=add(mult(z,z),c);\n"
+ " result=result+1;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "Complex mapComplex(Complex c,double f(double x)){\n"
+ " Complex result={f(c.real),f(c.imag)};\n"
+ " return result;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "void nl(){printf(\"\n\");}\n"
+ "\n"
+ "double doppel(double d){return 2*d;}\n"
+ "\n"
+ "int main(){\n"
+ " Complex c1={-0.3,-0.76868};\n"
+ " printComplex(c1);\n"
+ " nl();\n"
+ " printComplex(add(c1,c1));\n"
+ " nl();\n"
+ " printComplex(mult(c1,c1));\n"
+ " nl();\n"
+ " printComplex(divC(c1,c1));\n"
+ " nl();\n"
+ " printComplex(sub(c1,c1));\n"
+ " nl();\n"
+ " printf(\"Norm(c1) = %f\n\",norm(c1));\n"
+ " printf(\"schwelle(c1,4): %i\n\",schwelle(c1,4));\n"
+ " printComplex(mapComplex(c1,doppel));\n"
+ " nl();\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Oft kommt es vor, dass man Daten hat, von denen es eine endliche Menge\n"
+ "unterschiedlicher Ausprägung gibt. Diese stehen dann oft auch in einer\n"
+ "bestimmten Reihenfolge. Ein typisches solches Beispiel sind die sieben\n"
+ "Wochentage. Ein Wochentag ist genau einer dieser sieben Werte, und die sieben\n"
+ "Werte stehen in einer festen Reihenfolge.\n"
+ "\n"
+ "Um solche Daten adequat auszudrücken, gibt\n"
+ "es in C die Möglichkeit Aufzählungen zu deklarieren.\n"
+ "\n"
+ "\n"
+ "Die Deklaration einer Aufzählung beginnt mit dem Wort enum, dann\n"
+ "folgt der Name, den der Aufzählungstyp haben soll. Schießlich kommen in\n"
+ "geschweiften Klammern per Komma getrennt die verschiedenen Namen der\n"
+ "Aufzählungselemente. So läßt sich eine Aufzählung für die sieben Wochentage\n"
+ "wie folgt definieren.\n"
+ "\n"
+ "\n"
+ "enum Tag\n"
+ " {Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Sonnabend,Sonntag};]]>\n"
+ "\n"
+ "Wie auch schon bei Strukturen sind Aufzählungstypen Typen, die aus zwei\n"
+ "Wörtern bestehen: erst das Wort enum und dann der eigentliche Name\n"
+ "des Typs. Es lassen sich nun die Namen der einzelnen Elemente als Werte \n"
+ "dieses neuen Typs benutzen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Ein wenig Enttäuschung macht sich breit, wenn wir anfangen mit diesen Werten\n"
+ "zu rechnen. Tatsächlich sind die Werte einer Aufzählung nichts weiter als\n"
+ "einfache natürliche Zahlen. Wir können auf diesen Zahlen rechnen, können Werte\n"
+ "bekommen, die gar nicht in den Aufzählungsrahmen passen und können\n"
+ "Aufzählungsvariablen auch einfach mit willkürlichen Werten belegen, die gar\n"
+ "nichts mit der Aufzählung zu tun haben.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Ausgabe dieses Programms zeigt deutlich, dass wir es nur mit einfachen\n"
+ "Zahlen zu tun haben.\n"
+ " bin/Tage\n"
+ "heute ist 0\n"
+ "heute ist 1\n"
+ "heute ist 7\n"
+ "heute ist 42\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine schöne Eigenschaft von Aufzählungen ist, dass sich für\n"
+ "Fallunterscheidungen sehr gut der switch-Befehl nutzen läßt. Hierzu\n"
+ "betrachte man folgendes Beispiel:\n"
+ "\n"
+ "\n"
+ "enum Tag\n"
+ " {Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Sonnabend,Sonntag};\n"
+ "\n"
+ "int istWerktag(enum Tag t){\n"
+ " switch (t){\n"
+ " case Sonnabend:\n"
+ " case Sonntag: return 0;\n"
+ " default: return 1;\n"
+ " } \n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"heute ist %i\n\",Montag);\n"
+ " if (istWerktag(Montag)){\n"
+ " printf(\"und das ist ein Werktag\n\");\n"
+ " }\n"
+ " if (!istWerktag(Sonntag)){\n"
+ " printf(\"Sonntag ist kein Werktag\n\");\n"
+ " }\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Zweiwortnamen eines Typen sind unpraktisch. Ebenso wie für Strukturen kann\n"
+ "man auch für Aufzählungen mit dem Trick über das typedef-Konstrukt\n"
+ "bei der Deklaration einer Aufzählung gleich den Einworttypnamen für die\n"
+ "Aufzählung einführen.\n"
+ "\n"
+ "Obiges Programm wird damit etwas kompakter zu:\n"
+ "\n"
+ "\n"
+ "typedef enum \n"
+ " {Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Sonnabend,Sonntag} \n"
+ "Tag;\n"
+ "int istWerktag(Tag t){\n"
+ " switch (t){\n"
+ " case Sonnabend:\n"
+ " case Sonntag: return 0;\n"
+ " default: return 1;\n"
+ " } \n"
+ "}\n"
+ "int main(){\n"
+ " printf(\"heute ist %i\n\",Montag);\n"
+ " if (istWerktag(Montag)){\n"
+ " printf(\"und das ist ein Werktag\n\");\n"
+ " }\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir haben schon mehrfach über einen Typen gesprochen, der sich geradezu dazu\n"
+ "eignet, als Aufzählung dargestellt zu werden. Es ist der Typ für\n"
+ "Wahrheitswerte, von dem es genau zwei Werte gibt. Wir können, da es ja keinen\n"
+ "Typ bool in C gibt, diesen in einer winzigen Bibliothek als\n"
+ "Aufzählung definieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Implementierung der Prozedur printBool ist entsprechend einfach:\n"
+ "\n"
+ "\n"
+ "void printBool(bool b){\n"
+ " if (b) {printf(\"true\n\");\n"
+ " }else {printf(\"false\n\");}\n"
+ "}]]>\n"
+ "\n"
+ "Inkludieren wir diese kleine Bibliothek so können wir endlich auch in C mit\n"
+ "den Wrten true und false arbeiten.\n"
+ "\n"
+ "\n"
+ "#include \"Bool.h\"\n"
+ "int main(){\n"
+ " bool b = 1>2;\n"
+ " printBool(b);\n"
+ " b=b||true;\n"
+ " printBool(b);\n"
+ " b=!b;\n"
+ " printBool(b);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir befinden uns im Kapitel über Daten. Trotzdem sollten wir uns noch einmal\n"
+ "den Funktionen widmen. C hat eine wunderbare Eigenschaft, dass auch Funktionen\n"
+ "als Daten betrachtet werden können. Das ist auch gar nicht einmal so\n"
+ "verwunderlich, denn auch Funktionen liegen ja irgendwo im Speicher unseres\n"
+ "Computers. Damit sind auch Funktionen als Daten gespeichert. \n"
+ "In C gibt es nun die Möglichkeit, Funktionen als Parameter an andere\n"
+ "Funktionen zu übergeben. Leider ist die Typnotation für Funktionen dabei etwas\n"
+ "kompliziert. Betrachten wir das Konzept an einem Beispiel.\n"
+ "\n"
+ "\n"
+ "Wir können in C eine Funktion schreiben, die eine andere Funktion als\n"
+ "Parameter erhält. Wozu sollte das gut sein. Es ist z.B. vorstellbar, dass wir\n"
+ "die übergebene Funktion zweimal auf einen Parameter anwenden wollen. Hierzu\n"
+ "können wir die Funktion twice definieren. Sie soll als ersten\n"
+ "Parameter eine Funktion f bekommen und als zweiten \n"
+ "Parameter eine Zahl x. Das\n"
+ "Ergebnis soll so berechnet werden, dass zunächst die \n"
+ "Funktion f auf die Zahl x angewendet wird. Anschließend wird\n"
+ "nocheinmal die Funktion f auf das Ergebnis der ersten Berechnung\n"
+ "angewendet. Mathematisch läßt sich die Funktion twice durch folgende\n"
+ "Gleichung spezifizieren:\n"
+ "\n"
+ "\\begin{displaymath}\n"
+ "\\textrm{twice}(f,x) = f(f(x))\n"
+ "\\end{displaymath}\n"
+ "\n"
+ "\n"
+ "Tatsächlich läßt sich obige Spezifikation ziemlich genau eins-zu-eins in C\n"
+ "umsetzen. Hierzu ist lediglich zu wissen, wie der Typ des Parameters eines\n"
+ "Funktionstyps zu deklarieren ist:\n"
+ "\n"
+ "Soll ein Paramter eine Funktion darstellen, so ist vor dem Parameternamen \n"
+ "der Rückgabetyp zu schreiben, danach in\n"
+ "runde Klammern eingeschlossen die Parametertypen. Damit ist z.B. ein\n"
+ "Parameter, der die Übergabe einer Funktion erwartet, die \n"
+ "einen int Parameter erwartet und auch ein int als Rückgabe\n"
+ "hat, zu deklarieren als int f(int). Der Parametername ist \n"
+ "hierbei f. Wie man sieht, schreibt man einfach die Signatur der\n"
+ "erwarteten Funktion, die übergeben werden soll.\n"
+ "\n"
+ "\n"
+ "So läßt sich die Funktion twice direkt ausdrücken als:\n"
+ "\n"
+ "\n"
+ "int twice(int f(int),int x){\n"
+ " return f(f(x));\n"
+ "}]]>\n"
+ "\n"
+ "Um diese Auszutesten, brauchen wir erst einmal eine Funktion, die wir für den\n"
+ "Parameter f übergeben können. Hierzu sei einfach die Funktion, die\n"
+ "den Parameter quadriert definiert:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wendet man diese Funktion zweimal hintereinander an, so wird hoch vier\n"
+ "gerechnet. Das läßt sich jetzt direkt mit der \n"
+ "Funktion twice ausdrücken:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Und nun ist endlich Zeit, das alles einmal in einer Hauptfunktion\n"
+ "auszuprobieren: \n"
+ "\n"
+ "\n"
+ "\n"
+ "Tatsächlich kompiliert das Programm nicht nur, sondern errechnet auch die\n"
+ "erwarteten Ausgaben.\n"
+ "\n"
+ " bin/Twice\n"
+ "twice(add5,32): 42\n"
+ "hoch4(2): 16\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Alle Arten von Funktionen können als Parameter übergeben werden. So auch \n"
+ "Prozeduren und Funktionen ohne Parameter. Wollen wir z.B. ausdrücken, dass\n"
+ "etwas endlich oft wiederholt werden soll, so läßt sich hierzu eine \n"
+ "Funktion repeat schreiben:\n"
+ "\n"
+ "\n"
+ "\n"
+ "void repeat(int n, void f()){\n"
+ " if (n>0) {\n"
+ " f();\n"
+ " repeat(n-1,f);\n"
+ " }\n"
+ "}]]>\n"
+ "\n"
+ "Nun läßt sich über den Aufruf von repeat mehrfach dieselbe Prozedur\n"
+ "aufrufen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Was noch ein wenig esoterisch anmuten mag, Funktionen als Parameter zu\n"
+ "übergeben, ist in der Programmierung graphischer Benutzeroberflächen in C Gang\n"
+ "und Gebe. Dabei werden graphischen Komponenten, wie z.B. Knöpfen auf der\n"
+ "Oberfläche Funktionen übergeben, die diese Komponenten in bestimmten\n"
+ "Situationen ausführen sollen (z.B. wenn der Knopf gedrückt wird). Solche\n"
+ "Funktionen in der Programmierung graphischer Benutzeroberflächen in C werden\n"
+ "als callback function bezeichnet.\n"
+ "\n"
+ "Allgemein werden Funktion, die wiederum Funktionen als Parameter haben, als\n"
+ "Funktionen höherer Ordnung bezeichnet. Die \n"
+ "Funktionen twice und repeat in den obigen Beispielen sind\n"
+ "Funktionen höherer Ordnung.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Genauso wie für Strukturen und für Aufzählungen läßt sich \n"
+ "das typedef-Konstrukt nutzen, um einfachere oder sprechendere Namen\n"
+ "für Funktionstypen einzuführen. So läßt sich das letzte Beispiel\n"
+ "umformulieren, indem Prozeduren ohne Parameter auch als solche bezeichnet\n"
+ "werden: \n"
+ "\n"
+ "typedef void prozedur();\n"
+ "\n"
+ "void repeat(int n,prozedur f){\n"
+ " if (n>0) {\n"
+ " f();\n"
+ " repeat(n-1,f);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "void printHallo(){\n"
+ " printf(\"Hallo\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " repeat(10,printHallo);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Nehmen Sie Ihre Implementierung aus der letzten Aufgabe und schreiben Sie\n"
+ "jetzt eine Funktion höherer Ordnung: \n"
+ "Complex mapComplex(Complex c,double f(double x)). \n"
+ "Sie soll die komplexe Zahl zurückgeben, die man erhält, wenn man die\n"
+ "übergebene Funktion sowohl auf Real- als auch auf den Imaginärteil\n"
+ "anwendet. Testen Sie auch die Funktion an einigen Beispielen. \n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Alle Daten stehen irgendwo im Hauptspeicher. Nicht nur Daten sondern, wie wir\n"
+ "gesehen haben, auch Funktionen, weshalb wir sie auch als Daten benutzen \n"
+ "können. Da der Speicher unseres Komputers in einzelne Speicherzellen\n"
+ "eingeteilt ist, und diese in Adressen durchnummeriert sind, liegt jedes\n"
+ "Datenobjekt im Speicher in einer Speicherzelle mit einer bestimmten Adresse. \n"
+ "\n"
+ "\n"
+ "C ermöglicht es über den &-Operator auf eben diese Adresse\n"
+ "zuzugreifen. Die Adressen sind dabei eigentlich nur Zahlen, die wir ausgeben\n"
+ "können. \n"
+ "\n"
+ "\n"
+ "int f(int x){return 2*x;}\n"
+ "\n"
+ "typedef struct {int x;int y;} Point; \n"
+ "\n"
+ "int main(){\n"
+ " int x = 42;\n"
+ " int y = 17;\n"
+ " Point p ={x,y};\n"
+ " double d=0.0;\n"
+ " printf(\"&x: %u\n\",&x);\n"
+ " printf(\"&y: %u\n\",&y);\n"
+ " printf(\"&p: %u\n\",&p);\n"
+ " printf(\"&d: %u\n\",&d);\n"
+ " printf(\"&f: %u\n\",&f);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Starten wir das Programm, so gibt uns C ein wenig von seinem Innenleben\n"
+ "bekannt. Für die einzelnen Variablen wird die Adresse ausgegeben, an deren\n"
+ "Stelle im Speicher die einzelnen Variablen stehen.\n"
+ "\n"
+ " bin/GetAddress\n"
+ "&x: 3221219380\n"
+ "&y: 3221219376\n"
+ "&p: 3221219368\n"
+ "&d: 3221219360\n"
+ "&f: 134513548\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Wie man sieht, gibt es für fast alle Daten Adressen: für einfache Typen \n"
+ "wie int und double, für Strukturen aber auch für\n"
+ "Funktionen. Wie man der Ausgabe entnehmen kann liegen die lokalen Variablen\n"
+ "recht nah beieinander im Hauptspeicher. Die Funktion hingegen an einer\n"
+ "komplett anderen Stelle. Wo im Speicher die einzelnen Daten gespeichert\n"
+ "werden, darauf haben wir allerdings keinen Einfluss, das bestimmt der Compiler\n"
+ "automatisch. \n"
+ "\n"
+ "Adressen gibt es allerdings nur für Daten, die in Variablen gespeichert\n"
+ "sind. Konstanten und beliebige Ausdrücke sind davon ausgeschlossen. Versucht\n"
+ "man es trotzdem, die Adresse zu erfragen, \n"
+ "so kommt es zu einem Übersetzungsfehler:\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"&42: %u\n\",&42);\n"
+ " printf(\"&(17+4): %u\n\",&(17+4));\n"
+ "}]]>\n"
+ "\n"
+ "Der Compiler gibt folgende Fehlermeldung:\n"
+ "\n"
+ " gcc src/GetAddressError.c\n"
+ "src/GetAddressError.c: In function `main':\n"
+ "src/GetAddressError.c:3: error: invalid lvalue in unary `&'\n"
+ "src/GetAddressError.c:4: error: invalid lvalue in unary `&'\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit dem &-Operator läßt sich für jede Variable die\n"
+ "Speicheradresse geben. Doch was für einen Typ hat dieser Operator als\n"
+ "Rückgabe? Im oberen Testprogramm haben wir die Rückgabe einfach als Zahl\n"
+ "interpretiert zurückgegeben. Wer das Programm aber mit allen Warnungen\n"
+ "übersetzt, ahnt bereits, dass da mehr hinter steckt. \n"
+ "Tatsächlich gibt es zu jedem\n"
+ "Typ in C einen passenden Adresstyp, auch als Zeigertyp oder \n"
+ "Pointertyp bezeichnet. Er wird mit einem Sternsymbol nach dem\n"
+ "Typnamen notiert. So gibt es also zum Typ int den \n"
+ "Zeigertyp int*. Der &-Operator liefert zu einer\n"
+ "Variablen vom Typ x die Adresse des Typs x*, also für\n"
+ "Variablen des Typs int eine Adresse vom Typ int*.\n"
+ "\n"
+ "\n"
+ "typedef struct {int x;int y;} Point;\n"
+ "\n"
+ "int main(){\n"
+ " int x = 42;\n"
+ " int* xp = &x; \n"
+ " int** xpp = &xp; \n"
+ " Point p = {17,4};\n"
+ " Point* pp = &p;\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Wie man an der Variablen xpp sieht kann man das Spielchen tatsächlich\n"
+ "mehrfach spielen. Man kann auch von einer Adressvariablen sich wiederum die\n"
+ "Adresse geben lassen. \n"
+ "\n"
+ "Betrachten wir das Programm noch einmal im Detail. In Zeile 5 wird eine\n"
+ "Variable deklariert. Hierzu bedarf es einem Speicherplatz im Hauptspeicher um\n"
+ "eine Zahl zu speichern. Dieser Speicherplatz hat eine Adresse. In Zeile 6 wird\n"
+ "mit &x die Adresse des Speicherplatz für die \n"
+ "Variable x erfragt. Diese Adresse hat den Typ int*. Auch sie\n"
+ " soll in einer Variablen gespeichert werden. Deshalb wird die\n"
+ "Variable xp vom Typ int* deklariert. Auch die \n"
+ "Variable xp wird im Speicher in einer Speicherzelle mit einer \n"
+ "eigenen Adresse gespeichert. Diese Adresse läßt sich \n"
+ "mit &xp erfragen. Sie ist vom Typ int** und wird in\n"
+ "Zeile 7 in der Variablen xpp gespeichert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit dem &-Operator läßt sich die Speicheradresse einer Variablen\n"
+ "in Form eines Zeigertyps erfragen. Als inversen Operator gibt es in C den\n"
+ "Sternoperator *. Wird er auf einen Zeigertyp angewendet, dann werden\n"
+ "die Daten angesprochen, die an der entsprechenden Adresse gespeichert sind.\n"
+ "Er kehrt quasi die Operation des &-Operators wieder um. Während\n"
+ "der &-Operator für ein int eine Adresse auf \n"
+ "ein int berechnet, also ein int*, berechnet der *-Operator\n"
+ "für ein int* wieder ein int.\n"
+ "\n"
+ "Jetzt können wir das obige Programm erweitern, und mit Hilfe des\n"
+ "Sternoperators für die Zeigervariablen wieder die Werte erfragen, die in den\n"
+ "Speicherzellen gespeichert sind, auf den die Zeigervariablen verweisen.\n"
+ "\n"
+ "\n"
+ "typedef struct {int x;int y;} Point;\n"
+ "\n"
+ "int main(){\n"
+ " int x = 42;\n"
+ " int* xp = &x; \n"
+ " int y = *xp;\n"
+ " printf(\"y = %i\n\",y);\n"
+ "\n"
+ " int** xpp = &xp; \n"
+ " int z = **xpp;\n"
+ " printf(\"z = %i\n\",z);\n"
+ " printf(\"**xpp = %i\n\",**xpp);\n"
+ "\n"
+ " *xp = 17;\n"
+ " printf(\"x = %i\n\",x);\n"
+ "\n"
+ " Point p = {17,4};\n"
+ " Point* pp = &p;\n"
+ " printf(\"(*pp).x = %i\n\",(*pp).x);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ " bin/StarOperator\n"
+ "z = 42\n"
+ "**xpp = 42\n"
+ "x = 17\n"
+ "(*pp).x = 17\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Beim Zugriff auf die Felder einer Struktur, die über einen Zeiger zugegriffen\n"
+ "wird, ist zunächst mit dem Sternoperator die Referenz aufzulösen, um dann mit\n"
+ "dem Punktoperator auf das Feld der Struktur zuzugreifen. Das gibt dann\n"
+ "Ausdrücke der Form (*pp).x, wie bereits im letzten Beispiel gesehen. \n"
+ "C bietet hierfür eine speziellen Operator an, der dieses etwas kürzer notieren\n"
+ "läßt, der Pfeiloperator ->. (*pp).x kann mit ihm\n"
+ "als pp->x ausgedrückt werden.\n"
+ "\n"
+ "\n"
+ "typedef struct {int x;int y;} Point;\n"
+ "\n"
+ "int main(){\n"
+ " Point p = {17,4};\n"
+ " Point* pp = &p;\n"
+ " printf(\"(*pp).x = %i\n\",(*pp).x);\n"
+ " printf(\"pp->x = %i\n\",pp->x);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Was haben wir mit den Zeigern gewonnen (außer dass es etwas komplizierter wird\n"
+ "Programme zu verstehen)? Man erinnere sich daran, dass in C die Parameter an\n"
+ "eine Funktion immer per Wert übergeben werden. Das bedeutet, dass die\n"
+ "Parameterwerte kopiert werden. Man erinnere sich dazu an das \n"
+ "Beispielprogramm NoRefparam. Mit Zeigertypen gibt es nun eine \n"
+ "Möglichkeit, nicht den Wert, sondern die Speicheradresse einer Variablen an\n"
+ "eine Funktion zu übergeben. \n"
+ "Durch minimale Änderungen am Programm NoRefparam erhalten wir ein\n"
+ "Programm, in dem die Funktion tatsächlich die übergebene Variable verändern\n"
+ "kann. \n"
+ "\n"
+ "\n"
+ "int f1(int* x){\n"
+ " *x = 2* (*x);\n"
+ " return *x;\n"
+ "}\n"
+ "int main(){\n"
+ " int y = 42;\n"
+ " int z = f1(&y);\n"
+ " printf(\"y = %i\n\",y);\n"
+ " printf(\"z = %i\n\",z);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "Wie man an der Ausgabe sehen kann, ändert der Aufruf von f1 jetzt\n"
+ "tatsächlich den Wert der Variablen y.\n"
+ " ./student/bin/RefParam\n"
+ "y = 84\n"
+ "z = 84\n"
+ "sep@pc305-3:~/fh/c>]]>\n"
+ "\n"
+ "Parameter per Zeiger zu übergeben statt per Wert kann insbesondere sinnvoll\n"
+ "sein, wenn es sich bei den Parametern um Strukturen handelt. Strukturobjekte\n"
+ "können mitunter sehr groß sein und viel Speicherplatz beanspruchen. Auch will\n"
+ "man gerade recht oft Strukturobjekte durch Prozeduraufrufe verändern.\n"
+ "\n"
+ "Betrachten wir hierzu ein weiteres Mal die Struktur Punkt. Wir haben\n"
+ "bereits festgestellt, dass bei einer Wertübergabe die \n"
+ "Funktion verschiebe das ursprünglichen Punktobjekt gar \n"
+ "nicht verändert. Jetzt per Übergabe des Punktobjets als Zeiger tritt der\n"
+ "gewünschte Effekt ein:\n"
+ "\n"
+ "\n"
+ "typedef struct {\n"
+ " int x;\n"
+ " int y;\n"
+ "} Punkt;\n"
+ "\n"
+ "void printPunkt(Punkt p){\n"
+ " printf(\"(%i,%i)\n\",p.x,p.y);\n"
+ "}\n"
+ "\n"
+ "void verschiebe(Punkt* p){\n"
+ " p->x=p->x+1;\n"
+ " p->y=p->y+2;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " Punkt p={17,4};\n"
+ " printPunkt(p);\n"
+ " verschiebe(&p);\n"
+ " printPunkt(p);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Bisher vertrauen wir auf die automatische Speicherverwaltung lokaler Variablen\n"
+ "in C. Für eine lokale Variable einer Funktion ebenso wie für einen\n"
+ "Funktionsparameter, wird beim Aufruf einer Funktion Platz im Speicher\n"
+ "angelegt. An dieser Stelle werden die Daten der Variablen gespeichert. Sobald\n"
+ "die Funktion fertig ausgewertet ist und ihr Ergebnis berechnet hat, wird\n"
+ "dieser Speicherplatz nicht mehr benötigt. Die Speicherstelle wird wieder\n"
+ "freigegeben, um sie später für eventuelle andere Variablen zu benutzen.\n"
+ "\n"
+ "Da wir uns explizit mit dem &-Operator die Adresse einer\n"
+ "Speicherzelle geben lassen können, so können wir eine solche Adresse auch als\n"
+ "Funktionsergebnis zurückgeben. Es läßt sich folgende Funktion definieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int* f(int y){\n"
+ " int x= y;\n"
+ " return &x;\n"
+ "}]]>\n"
+ "\n"
+ "Bei einem Aufruf dieser Funktion erhalten wir einen Zeiger auf eine\n"
+ "Speicherzelle, in der während des Aufrufs eine lokale Variable gespeichert \n"
+ "war. Auf die Variable selbst läßt sich nicht mehr zugreifen. Sie existiert\n"
+ "quasi nicht mehr. Wir haben also einen Zeiger in einen Speicherbereich, der\n"
+ "einmal für eine lokale Variable genutzt wurde. Diese Variable gibt es aber gar\n"
+ "nicht mehr.\n"
+ "\n"
+ "\n"
+ "Der Compiler macht uns auch tatsächlich darauf aufmerksam, dass hier etwas\n"
+ "merkwürdig ist:\n"
+ "\n"
+ " gcc DeadVariable.c\n"
+ "DeadVariable.c: In function `f':\n"
+ "DeadVariable.c:5: warning: function returns address of local variable\n"
+ "sep@pc305-3:~/fh/c>]]>\n"
+ "\n"
+ "Da es sich aber nur um eine Warnung handelt, vielleicht wissen wir ja was wir\n"
+ "tun, können wir das Programm übersetzen. Wir vervollständigen es um eine\n"
+ "zweite Funktion und um eine Hauptfunktion.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das Programm kann zu folgender Ausgabe führen:\n"
+ "\n"
+ " ./DeadVariable\n"
+ "17\n"
+ "sep@pc305-3:~/fh/c>]]>\n"
+ "\n"
+ "Hier läßt sich etwas über die interne Speicherverwaltung unseres Compilers\n"
+ "lernen. Wir lassen uns die Speicherzelle der lokalen \n"
+ "Variablen x aus der Funktion f zurückgeben. Anschließend\n"
+ "rufen wir die Funktion f2 auf. Diese hat ihrerseits eine lokale\n"
+ "Variable. Nach Aufruf der Funktion f2 befindet sich ein Wert in der\n"
+ "Speicherzelle die einstmals die lokale Variable x speicherte, der\n"
+ "zuletzt in der lokalen Variablen z der Funktion f2 stand.\n"
+ "\n"
+ "Tatsächlich ist es ziemlicher Zufall, was sich am Speicherplatz einer nicht\n"
+ "mehr existierenden lokalen Variable befindet.\n"
+ "\n"
+ "\n"
+ "Wir haben uns bemüht Variablen immer gleich bei der Deklaration auch einen\n"
+ "initialen Wert zuzuweisen. Wird einer Variablen kein Wert zugewiesen, so hängt\n"
+ "es vom Zufall ab, was für einen Wert man bekommt, wenn auf eine solche noch\n"
+ "nicht initialisierte Variable zugegriffen wird. Dasselbe gilt auch für\n"
+ "Zeigervariablen. Es gibt aber Situationen, in denen man eine Zeigervariable\n"
+ "deklariert, ohne dass schon der zuzuweisende Adresswert vorhanden ist. Oder\n"
+ "umgekehrt: für eine Zeigervariable ist im Laufe des Programms keine gültige\n"
+ "Adresse mehr vorhanden. Um in einer Zeigervariablen nicht einen zufälligen\n"
+ "oder ungültigen Adresswert speichern zu müssen, gibt es in C die Möglichkeit\n"
+ "eines ausgezeichneten null-Zeigers. Er wird durch das \n"
+ "Wort NULL bezeichnet. Man kann mit der Gleichheit testen, ob ein\n"
+ "Zeiger mit NULL belegt ist.\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "int* f(int* y){\n"
+ " static int* x=NULL;\n"
+ " if (NULL!=x) x=y;\n"
+ " else *y=*x;\n"
+ " return x;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " int x= 42;\n"
+ " printf(\"*f(&x)%i\n\",*f(&x));\n"
+ " x=17;\n"
+ " printf(\"*f(&x)%i\n\",*f(&x));\n"
+ " printf(\"x%i\n\",x);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Um den NULL-Zeiger verwenden zu können, ist es notwendig die\n"
+ "Bibliothek stdlib.h zu inkludieren.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir haben gesehen, dass es für jeden Typ, einen entsprechenden Zeigertyp\n"
+ "gibt. So gibt es zu int den Zeigertyp int* und\n"
+ "für eine Struktur struct Punkt den \n"
+ "Zeigertyp struct Punkt*. Technisch gesehen sind alle diese\n"
+ "Zeigertypen eine Zahl, die eine Adresse in unserem Speicherbereich\n"
+ "bezeichnet. Also sind eigentlich alle Zeigertypen Daten von derselben Art. Die\n"
+ "Programmiersprache C bietet einen besonderen allgemeinen Zeigertyp an, der\n"
+ "ausdrückt, dass es sich um irgendeinen Zeiger handelt, der aber nicht angibt,\n"
+ "was in der Speicheradresse gespeichert ist, auf die der Zeiger\n"
+ "verweist. Dieser allgemeine Zeigertyp wird mit void* bezeichnet. Das\n"
+ "Wort void sollte hier so gelesen werden, dass nicht bekannt\n"
+ "ist, was für Daten an der Speicherstelle stehen, auf die verwiesen wird.\n"
+ "\n"
+ "\n"
+ "Zunächst definieren wir als Beispiel eine kleine Struktur:\n"
+ "\n"
+ "\n"
+ "typedef struct {\n"
+ " int x;\n"
+ " int y;\n"
+ "} Punkt;]]>\n"
+ "\n"
+ "Wir wollen einmal einen void-Zeiger benutzen:\n"
+ "\n"
+ "\n"
+ "\n"
+ "In diesem void-Zeiger läßt sich jetzt z.B. die Adresse \n"
+ "einer int-Vatiablen speichern.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Soll die Adresse über den void-Zeiger angesprochen werden und mit der\n"
+ "darin gespeicherte Zahl gerechnet werden, so müssen wir explizit den Zeiger zu\n"
+ "einem Zeiger auf eine int-Zahl umwandeln. Hierzu ist die Notation in\n"
+ "runden Klammern zu benutzen, wie wir sie schon vom Rechnen mit primitiven\n"
+ "Zahlen gewohnt sind. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir können aber auf die gleiche Weise versuchen, in \n"
+ "der void-Zeigervariablen ein Objekt unserer Punktstruktur zu\n"
+ "speichern. Um wieder auf das Objekt zugreifen zu können, ist dann der Zeiger\n"
+ "wieder entsprechend umzuwandeln.\n"
+ "\n"
+ "\n"
+ "x: %i\\n\",((Punkt*)object)->x);\n"
+ "\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Im obigen Beispiel ist mit dem Namen object schon angedeutet, wofür\n"
+ "man den void-Zeiger verwenden kann: für die Möglichkeit, Zeiger auf\n"
+ "Daten belieber Art zu speiechern. In der objektorientierten Programmierung\n"
+ "wird dieses in dem Konzept eines allgemeinen Objektes münden. Am Ende dieses\n"
+ "Kapitels werden wir ein Beispiel sehen, in dem wir void-Pointer\n"
+ "sinnvoll nutzen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Zeiger sind technisch nur die Adressen bestimmter Speicherzellen, also\n"
+ "nichts weiteres als Zahlen. Und mit Zahlen können wir rechnen. So\n"
+ "können tatsächlich arithmetische Operationen auf Zeigern durchgeführt\n"
+ "werden. \n"
+ "\n"
+ "\n"
+ "Wir erzeugen mehrere Zeiger auf ganze Zahlen und betrachten die\n"
+ "Nachbarschaft eines dieser Zeiger.\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " int i = 5;\n"
+ " int j = 42;\n"
+ " int *pI = &i;\n"
+ " int *pJ = &j;\n"
+ " \n"
+ " int *pK;\n"
+ " printf\n"
+ " (\"i:%i,pI:%u,pI+1:%u,*(pI+1):%i,pI-1:%u,*(pI-1):%i,pJ:%u,*pJ:%i\n\"\n"
+ " ,i ,pI ,pI+1 ,*(pI+1) ,pI-1 ,*(pI-1) ,pJ ,*pJ );\n"
+ " *pK = 12;\n"
+ " printf(\"pK: %i, *pK: %i\n\", pK,*pK); \n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Dieses Programm erzeugt z.B.folgende interessante Ausgabe:\n"
+ "\n"
+ " bin/ZeigerArith\n"
+ "i:5,pI:3221219380,pI+1:-1073747912,*(pI+1):-1073747816,pI-1:3221219376,*(pI-1):42,pJ:3221219376,*pJ:42\n"
+ "pK: 1075174352, *pK: 12\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das Ergebnis einer Zeigerarithmetik hängt sehr davon ab, wie die\n"
+ "einzelnen Variablen im Speicher abgelegt werden. In der Regel wird\n"
+ "man die Finger von ihr lassen und nur ganz bewußt auf sie\n"
+ "zurückgreifen, wenn man genau weiß, was man tut, um eventuell\n"
+ "Optimierungen durchführen zu können.\n"
+ "\n"
+ "Erstaunlicher Weise werden wir aber ein C-Konstrukt kennenlernen, das eines\n"
+ "der gebräuchlichsten Konzepte in C überhaupt ist, und tatsächlich auf der\n"
+ "Zeigerarithmetik basiert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Dieses Kapitel trägt den Titel dynamische Daten. Hiervon haben wir vorerst\n"
+ "aber noch nicht wirklich etwas gesehen. Bisher haben wir nur geschaut, in \n"
+ "welchen Speicherzellen die Daten unserer Variablen gespeichert sind.\n"
+ "Damit ist in einem Programm relativ statisch durch die Anzahl der Variablen\n"
+ "festgelegt, wieviel Daten das Programm \n"
+ "speichern kann. Normaler Weise wünscht man sich aber, dass ein Programm\n"
+ "während des Programmdurchlaufs dynamisch neuen Speicher nach Bedarf für\n"
+ "weitere Daten anlegen kann. Hierzu bietet C die Möglichkeit an über,\n"
+ "Speicherallokationsfunktionen neuen Speicher zu reservieren.\n"
+ " \n"
+ "\n"
+ "In der C-Standardbibliothek gibt es zwei ein Funktionen, \n"
+ "mit der Speicher beliebiger\n"
+ "Größe vom Compiler angefordert werden kann. \n"
+ "\n"
+ "\n"
+ "Die Funktion malloc hat einen Parameter. In diesem wird angegeben,\n"
+ "wieviel Speicherplatz bereitgestellt werden soll. Diese Angabe ist die Anzahl\n"
+ "der Byte. \n"
+ "Das Ergebnis ist ein Zeiger\n"
+ "auf diesen neu bereitgestellten Speicher. Da die \n"
+ "Funktion malloc nicht wissen kann, was für Daten einmal in diesem\n"
+ "Speicherbereich gespeichert werden sollen, ist ihr Rückgabetyp der \n"
+ "allgemeine Zeiger void*.\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "int main(){\n"
+ " void* vp = malloc(7);\n"
+ " int* ip=(int*)vp;\n"
+ " *ip=42;\n"
+ " printf(\"%i\n\",*ip);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Im obigen Beispiel haben wir willkürlich sieben Byte Speicher reservieren\n"
+ "lassen, um sie dann zur Speicherung von einer int-Zahl zu benutzen.\n"
+ "Man sollte natürlich am Besten immer genau soviel Speicher anfordern, wie für\n"
+ "die geplanten Zwecke gebraucht wird. Wir kennen bereits die \n"
+ "Funktion sizeof, die Angaben darüber macht, wieviel Speicher ein\n"
+ "bestimmter Typ benötigt. Mit dieser läßt sich genau der erforderlicher\n"
+ "Speicherplatz anfordern:\n"
+ "\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "int main(){\n"
+ " void* vp = malloc(sizeof(int));\n"
+ " int* ip=(int*)vp;\n"
+ " *ip=42;\n"
+ " printf(\"%i\n\",*ip);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es gibt in C eine Variante der Funktion malloc: die \n"
+ "Funktion calloc. \n"
+ "Nur, dass es bei der Funktion calloc() nicht einen, sondern zwei Parameter\n"
+ "gibt. Im Gegensatz zu malloc können wir mit calloc noch \n"
+ "die Anzahl von Speicherobjekten angeben, die reserviert werden soll. \n"
+ "Wird z.B. für 10\n"
+ "Zahlen vom Typ int Speicherplatz benötigt, so kann dies \n"
+ "mit calloc erledigt werden. Auf die mehreren Elemente dieses\n"
+ "Speicherbereichs kann dann mit der Zeigerarithmetik zugegriffen werden: \n"
+ "\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "int main(){\n"
+ " int* xs = (int*)calloc(10,sizeof(int));\n"
+ " int i=0;\n"
+ " for(i=0;i<10;i++) *(xs+i) = 2*i;\n"
+ " for(i=0;i<10;i++) printf(\"%i, \",*(xs+i));\n"
+ " printf(\"\n\");\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wir haben somit ein kleines Programm geschrieben, in dem wir Speicher für 10\n"
+ "Zahlen angefordert haben und mit diesen 10 Speicherplätzen auch gearbeitet\n"
+ "haben:\n"
+ " bin/Calloc\n"
+ "0, 2, 4, 6, 8, 10, 12, 14, 16, 18,\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Zusätzlich werden mit der Funktion calloc alle Werte des \n"
+ "angeforderten \n"
+ "Speichers automatisch mit binären 0-Werten initialisiert. \n"
+ "Bei malloc hat\n"
+ "der reservierte Speicherplatz zunächst einen undefinierten Wert. \n"
+ "Braucht man die zusätzliche Initialisierung des Speichers nicht, so kann \n"
+ "natürlich auch mit malloc Speicher für mehrere Elemente angefordert\n"
+ "werden. Der Aufruf \n"
+ "calloc(10,sizeof(int)); \n"
+ "ist hierzu zu Ersetzen durch: \n"
+ "malloc(10*sizeof(int));\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "to be done\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir haben oben schon gesehen, wie sich Speicher für ganze Reihen von Daten\n"
+ "eines Typs bequem allokieren läßt. Über die Zeigerarithmetik läßt sich dann\n"
+ "auf die einzelnen Elemente in einer solchen Reihung zugreifen.\n"
+ "\n"
+ "\n"
+ "Bisher hatten wir noch keine Möglichkeit mit Zeichenketten, also Wörtern und\n"
+ "Sätzen zu arbeiten. Einzelne Buchstaben ließen sich durch Daten des \n"
+ "Typs char adequatNunja, zumindest wenn man sich auf die 26\n"
+ "Buchstaben des lateinischen Alpabets beschränkt. darstellen. Ein\n"
+ "Wort oder längere Texte können nun als ein zusammen allokierten\n"
+ "Speicherbereich von char-Werten aufgefasst werden. Damit kann ein\n"
+ "Zeiger auf diesen Speicherbereich als Zeiger auf einen ganzen Text verstanden\n"
+ "werden. So können wir Speicher anfordern und darin Wörter speichern:\n"
+ "\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "int main(){\n"
+ " char* hallo = malloc(5*sizeof(char));\n"
+ " *(hallo+0) = 'H';\n"
+ " *(hallo+1) = 'A';\n"
+ " *(hallo+2) = 'L';\n"
+ " *(hallo+3) = 'L';\n"
+ " *(hallo+4) = 'O';\n"
+ " \n"
+ " printf(\"%c\n\",*(hallo+3));\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wir haben ein Problem: wir können der Variablen hallo vom \n"
+ "Typ char* im obigen Beispiel nich ansehen, für wieviel Buchstaben\n"
+ "dort Platz ist. Diese Information müssten wir seperat in einer Variablen\n"
+ "speichern. Also die eigentliche Wortlänge. Mit dieser Information läßt sich\n"
+ "dann auch ein Wort komplett ausdrucken:\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "void printWord(unsigned int l, char* word){\n"
+ " unsigned int i;\n"
+ " for (i=0;i<l;i++)\n"
+ " printf(\"%c\",*(word+i));\n"
+ "\n"
+ " printf(\"\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " unsigned int laenge = 5;\n"
+ "\n"
+ " char* hallo = malloc(laenge*sizeof(char));\n"
+ " *(hallo+0) = 'H';\n"
+ " *(hallo+1) = 'A';\n"
+ " *(hallo+2) = 'L';\n"
+ " *(hallo+3) = 'L';\n"
+ " *(hallo+4) = 'O';\n"
+ " \n"
+ " printWord(laenge,hallo);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Das ist etwas umständlich, wenn man immer die Länge als Zweitinformation mit\n"
+ "sich herumschleppen muß. Deshalb gibt es einen Standardtrick in C, wenn man\n"
+ "mit Reihungen von Zeichen arbeitet. Als letztes Zeichen schreibt man noch den \n"
+ "Nullbuchstaben, der als '\\0' notiert wird. Dann kann man auf eine\n"
+ "vorgegebene Länge verzichten und bei der Ausgabe abbrechen, wenn das\n"
+ "Nullzeichen erreicht wurde.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "void printWord( char* word){\n"
+ " int i;\n"
+ " for (i=0;*(word+i)!='\\0';i++)\n"
+ " printf(\"%c\",*(word+i));\n"
+ "\n"
+ " printf(\"\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " char* hallo = malloc(6*sizeof(char));\n"
+ " *(hallo+0) = 'H';\n"
+ " *(hallo+1) = 'A';\n"
+ " *(hallo+2) = 'L';\n"
+ " *(hallo+3) = 'L';\n"
+ " *(hallo+4) = 'O';\n"
+ " *(hallo+5) = '\\0';\n"
+ " printWord(hallo);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Die oben dargestellte Form Texte zu verarbeiten ist der Standardweg in\n"
+ "C. Dieses ist in die Sprache eingebaut. Wenn ein Text in doppelten\n"
+ "Anführungszeichen steht, wie wir es ja schon oft in den Funktionsaufrufen \n"
+ "von printf gesehen haben, dann wird der Text auf eben diese Weise in\n"
+ "einen über den Typ char* beschriebenen Speicherbereich wie oben \n"
+ "von Hand durchgeführt gespeichert. Somit läßt sich die Erzeugung des obigen\n"
+ "Textes im Speicher viel einfacher schreiben:\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "void printWord( char* word){\n"
+ " int i;\n"
+ " for (i=0;*(word+i)!='\\0';i++)\n"
+ " printf(\"%c\",*(word+i));\n"
+ "\n"
+ " printf(\"\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " char* hallo = \"HALLO\";\n"
+ " printWord(hallo);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Ein weiterer Luxus ist, dass die Funktion printf auch darauf\n"
+ "vorbereitet ist, derartige Zeichenketten auszugeben, so dass wir auf ein\n"
+ "eigenes printWord verzichten können. Hierzu ist der \n"
+ "Funktion printf als Formatierungsanweisung %s mitzugeben:\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "\n"
+ "int main(){\n"
+ " char* hallo = \"HALLO\";\n"
+ " printf(\"%s\n\",hallo);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion int length(char* text), die \n"
+ "angibt, wieviel Zeichen in dem Speicherbereich \n"
+ "von text gespeichert sind.\n"
+ "\n"
+ "\n"
+ "\n"
+ "int length(char* text){\n"
+ " int i;\n"
+ " for (i=0;*(text+i)!='\\0';i++){}\n"
+ "\n"
+ " return i;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"%i\n\",length(\"\"));\n"
+ " printf(\"%i\n\",length(\"A\"));\n"
+ " printf(\"%i\n\",length(\"Hallo\"));\n"
+ " printf(\"%i\n\",length(\"ottos mops kotzt\"));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion bool isPallindrom(char* text), \n"
+ "die testet, ob der übergebene Text vorwärts und rückwärts gelesen identisch\n"
+ "ist. \n"
+ "\n"
+ "\n"
+ "#include \"Bool.h\"\n"
+ "int length(char* text){\n"
+ " int i;\n"
+ " for (i=0;*(text+i)!='\\0';i++){}\n"
+ " return i;\n"
+ "}\n"
+ "\n"
+ "bool isPallindrom(char* text){\n"
+ " int l = length(text);\n"
+ " int i;\n"
+ " for (i=0;i\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion int leseAlsZahl(char* text), die\n"
+ "Zeichenkette als Zahl interpretiert. Für die \n"
+ "Zeichenkette \"42\" soll also die Zahl 42 als Ergebnis\n"
+ "zurückgegeben werden:\n"
+ "\n"
+ "\n"
+ "int leseAlsZahl(char* text){\n"
+ " int result=0;\n"
+ " int i;\n"
+ " for (i=0;*(text+i)!='\\0';i++)\n"
+ " result=result*10+(*(text+i)-'0');\n"
+ " return result;\n"
+ "}\n"
+ "int main(){\n"
+ " printf(\"%i\n\",leseAlsZahl(\"42\"));\n"
+ " printf(\"%i\n\",leseAlsZahl(\"007\"));\n"
+ " printf(\"%i\n\",leseAlsZahl(\"a\"));\n"
+ " printf(\"%i\n\",leseAlsZahl(\"12\"));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion char* concat(char* text1,char* text2), die\n"
+ "eine neue Zeichenkette erzeugt, die aus den beiden Zeichenketten der Parameter\n"
+ "aneinandergehängt besteht.\n";
String skript6 =
"\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "int length(char* text){\n"
+ " int i;\n"
+ " for (i=0;*(text+i)!='\\0';i++){}\n"
+ " return i;\n"
+ "}\n"
+ "char* concat(char* text1,char* text2){\n"
+ " char* result=malloc(sizeof(char)* (length(text1)+length(text2)+1));\n"
+ " int i;\n"
+ " for (i=0;*(text1+i)!='\\0';i++){\n"
+ " *(result+i)=*(text1+i);\n"
+ " }\n"
+ " int j;\n"
+ " for (j=0;*(text2+j)!='\\0';(i++,j++)){\n"
+ " *(result+i)=*(text2+j);\n"
+ " }\n"
+ " *(result+i)='\\0';\n"
+ " return result; \n"
+ "}\n"
+ "int main(){\n"
+ " printf(\"%s\n\",concat(\"otto\",\"reber\"));\n"
+ " printf(\"%s\n\",concat(\"007\",\"008\"));\n"
+ " printf(\"%s\n\",concat (\"john\",\"paul\"));\n"
+ " printf(\"%s\n\",concat (\"george\",\"ringo\"));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Speicher anzufordern ist ein feine Sache, aber wenn wir auch viel davon in\n"
+ "heutugen Rechner haben, so ist der Speicher trotzdem endlich. Wenn wir sorglos\n"
+ "immer wieder neuen Speicher anfordern bekommen wir ein Müllproblem. Wie sich\n"
+ "ein solches auswirkt, läßt sich leicht ausprobieren. Man starte einmal das\n"
+ "folgende Progamm. \n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "int main(){\n"
+ " int i=0;\n"
+ " int* ip;\n"
+ " for (;i<10000000;i++){\n"
+ " ip=(int*)malloc(sizeof(int));\n"
+ " *ip=i;\n"
+ " printf(\"%i \",*ip);\n"
+ " }\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Mit dem Betriebssystembefehl top läßt sich schön beobachten, wie\n"
+ "dieses Programm immer neuen Speicher benötigt und schließlich über 100\n"
+ "Megabyte Speicher reserviert hat (und damit den Rechner schon arg\n"
+ "belastet). Man spricht in einem solchen Fall von einem Speicherleck. Eine der\n"
+ "großen Schwierigkeiten der Programmiersprache C ist es, dass dynamisch\n"
+ "allokierter Speicher explizit wieder frei gegeben werden muß. Hierzu gibt es\n"
+ "die Funktion free. Sie erwartet einen Zeiger und gibt alles mit\n"
+ "diesem Zeiger allokierten Speicherbereich wieder frei zur neuen\n"
+ "Benutzung. Rufen wir nun im obigen Programm am Ende des Schleifenrumpfs die\n"
+ "Funktion free auf, so können wir beobachten, dass das Programm mit\n"
+ "konstanten Speicherbelegung läuft.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "#include <stdlib.h>\n"
+ "int main(){\n"
+ " int i=0;\n"
+ " int* ip;\n"
+ " for (;i<10000000;i++){\n"
+ " ip=(int*)malloc(sizeof(int));\n"
+ " *ip=i;\n"
+ " printf(\"%i \",*ip);\n"
+ " free(ip);\n"
+ " }\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Schon seit Anfang der 60er Jahre gibt es mit Lisp eine \n"
+ "Programmiersprache, in der die dynamische Speicherverwaltung automatisch vom\n"
+ "Compiler durchgeführt wird. Dieses macht die sogenannte garbage \n"
+ "collection. Es ist erstaunlich und schwer erklärlich, dass sich eine\n"
+ "Programmiersprache ohne eine vollkommen automatische Speicherverwaltung wie C\n"
+ "derart durchsetzen und hartnäckig halten konnte. Erst mitte der 90er Jahre\n"
+ "setzte sich mit Java eine Mainstream Programmiersprache durch, die\n"
+ "eine automatische Speicherverwaltung per garbage collection hat.\n"
+ "\n"
+ "Reflexartig wiederholt für alle unangenehmen Eigenschaften von C wird stets\n"
+ "ein Performancevorteil. Allerdings spielt dieser wahrscheinlich für 99\\% der\n"
+ "Anwendungen keine signifikante Rolle.\n"
+ "\n"
+ "Warum hat man aber nicht irgendwann eine automatische Speicherverwaltung in C\n"
+ "integriert. Tatsächlich gibt es C-Compiler die dieses zumindest teilweise\n"
+ "anbieten; allerdings verhindern einige Konzepte von C, insbesondere die\n"
+ "Speicherarithmetik, dass eine garbage collection allgemein in C\n"
+ "eingeführt werden könnte.\n"
+ "\n"
+ "Doch genug des C bashings. Für uns ist sicherlich C eine \n"
+ "wunderbare Sprache, um etwas über den Speicher zu lernen und konfontiert damit\n"
+ "zu werden, dass angeforderter Speicher irgendwie auch wieder frei gesetzt\n"
+ "werden muss. Eine Erfahrung, die wir in Java nie gemacht hätten. \n"
+ "\n"
+ "Ganz allein läßt uns C allerdings auch nicht mit der Speicherverwaltung. Zum\n"
+ "einen wird der Speicher lokaler Variablen nach Ablauf einer\n"
+ "Funktionsberechnung wieder frei gegeben, zum anderen müssen wir dem Compiler\n"
+ "in der Funktion free nicht mitteilen, wieviel Speicher denn wieder\n"
+ "frei gegeben werden soll. Die C-Laufzeit merkt sich für jede dynamisch\n"
+ "angeforderte Speicherzelle, wieviel Speicher angefordert wurde und gibt genau\n"
+ "diese Speichergröße dann auch wieder frei. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es ist Vorteilhaft auch in C schon einen weitgehendst objektorientierten\n"
+ "Programmierstil zu kultivieren. Objektarten werden durch Strukturen\n"
+ "definiert. Objekte dieser Strukturen sollen nur über einen Zeiger benutzt\n"
+ "werden. Wir können eine weitgehendst objektorientiert angelegte Struktur gerne\n"
+ "auch schon als Klasse bezeichnen.\n"
+ "\n"
+ "Hierzu nocheinmal das Beispiel mit den Punkten im zweidimensionaloen\n"
+ "Raum: \n"
+ "\n"
+ "\n"
+ "#include \n"
+ "#include \n"
+ "\n"
+ "typedef struct {\n"
+ " int x;\n"
+ " int y;\n"
+ "} PunktStrukt;\n"
+ "\n"
+ "typedef PunktStrukt* Punkt;]]>\n"
+ "\n"
+ "Es ist sinnvoll eine Funktion vorzusehen, die aus gegebenen Werte ein Objekt\n"
+ "einer Struktur erzeugt. Hierzu ist der entsprechende Speicher zu allokieren\n"
+ "und die einzelnen Felder des Strukturobjekts mit den Werten der Parameter zu\n"
+ "übergeben. Eine solche Funktion wird als Konstruktor bezeichnet. Eine\n"
+ "sinnvolle Namensgebung ist den Konstruktornamen mit new beginnen zu\n"
+ "lassen, gefolgt von dem Namen der Struktur:\n"
+ "\n"
+ "x = x;\n"
+ " this->y = y;\n"
+ " return this;\n"
+ "}]]>\n"
+ "\n"
+ "Ebenso sollte man eine Funktion vorsehen, die es erlaubt ein Objekt wieder aus\n"
+ "dem Speicher zu entfernen. Man spricht dabei von einen Destruktor. Meistens\n"
+ "reicht es aus, hier einfach die Funktion free aufzurufen, wir werden\n"
+ "aber bald auch ein Beispiel sehen, in dem dies nicht ausreicht. Für dbie\n"
+ "Punkt-Klasse erhalten wir den einfachen Destruktor:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Nun können wir Punktobjekte erzeugen und löschen. Jetzt wollen wir mit diesen\n"
+ "Objekten auch gerne etwas vornehmen. Dazu schreiben wir Funktionen, die als\n"
+ "ersten Parameter ein entsprechendes Objekt erhalten. In Hinblick auf\n"
+ "objektorientierte Sprachen ist es sinnvoll diesen ersten Parameter dann mit\n"
+ "den Namen this zu bezeichnen. Funktionen, die als ersten Parameter\n"
+ "Objekte einer bestimmten Klasse erhalten, sollen \n"
+ "als Objektmethode kurz auch\n"
+ "nur Mehtodedieser\n"
+ "Klasse bezeichnet werden.\n"
+ "\n"
+ "So können wir eine einfache Methode schreiben, die ein Punktobjekt auf der\n"
+ "Kommandozeile ausgibt:\n"
+ "\n"
+ "x,this->y);\n"
+ "}]]>\n"
+ "\n"
+ "Mehtoden können auch Rückgaben haben. Die folgende Mehtode errechnet als den\n"
+ "betrag eines Punktes seine Entfernung zum Ursprung. Hierzu wird die Formel von\n"
+ "Pythagoras verwendet:\n"
+ "\n"
+ "x*this->x+this->y*this->y);\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Methoden können nach dem ersten Parameter noch weitere Parameter enthalten.\n"
+ "Die folgende Methode kann ein Punktobjekt um die als weitere Werte angegebenen\n"
+ "Distanzen verschieben.\n"
+ "\n"
+ "x += deltaX;\n"
+ " this->y += deltaY;\n"
+ "}]]>\n"
+ "\n"
+ "Nun können wir tatsächlich schon fast wie in einer objektorientierten Sprache\n"
+ "mit der Punkt-Klasse arbeiten:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das Programm führt zu folgender Ausgabe:\n"
+ " bin/Punkt5\n"
+ "(17,4)\n"
+ "betrag: 17.464249\n"
+ "(19,8)\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eines der komplexesten Konstrukte der Programmiersprache C ist gleichzeitig\n"
+ "auch das wahrscheinlich am häufigsten benutzte. Es handelt sich dabei um\n"
+ "Arrays, auf Deutsch oft als Datenfelder bezeichnet. Innerhalb dieses Skripts\n"
+ "werden wir sie mit dem ebenfalls gebräuchlichen \n"
+ "Ausdruck Reihungen bezeichnen, da wir als Feld auch schon die\n"
+ "Attribute von Strukturen bezeichnen. Bei Reihungen geht es darum, mehrere\n"
+ "Variablen eines uniformen Datentyps in einer Variablen zu speichern.\n"
+ "\n"
+ "Ähnlich wie schon bei Funktionstypen wird der Typ einer Reihung vor und hinter\n"
+ "dem Variablennamen bezeichnet. Vor dem Variablennamen steht der Typ, den die\n"
+ "einzelnen Elemente der Reihung haben und nach dem Variablennamen steht ein\n"
+ "eckiges Klammernpaar, um zu bezeichenen, dass es sich hier im eine\n"
+ "Reihungsvariablen handelt. So bezeichnet int xs [] eine Reihung von\n"
+ "ganzen Zahlen. Eine Reihung kann, ebenso wie wir es von Strukturen kennen,\n"
+ "durch die Aufzählung der Werte in geschweiften Klammern intialisiert werden. \n"
+ "\n"
+ "Zeit unseren ersten Array zu deklarieren. Statt vier einzelner \n"
+ "Variablen (noch einmal als Beispiel drüber) wird eine einzige Variable mit\n"
+ "vier Werten deklariert:\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " int x0 = 1;\n"
+ " int x1 = 2;\n"
+ " int x2 = 3;\n"
+ " int x3 = 4;\n"
+ " int x [] = {1,2,3,4};]]>\n"
+ "\n"
+ "Normale Variablen können wir direkt benutzen, um an ihre Werte zu kommen:\n"
+ "\n"
+ "\n"
+ "In Arrayvariablen verstecken sich mehrere einzelne Werte. Um auf diese einzeln\n"
+ "zugreifen zu können wird ein Index benutzt. Der Index ist in eckigen Klammern\n"
+ "der Variablen anzuhängen. Das erste Element wird über den Index 0\n"
+ "angesprochen. Die vier Werte unserer ersten Reihung sind also wie folgt\n"
+ "anzusprechen: \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Bei der Deklaration einer\n"
+ "Reihungsvariablen muß die Anzahl der Elemente bekannt sein. Im ersten\n"
+ "Beispiel wurde die Anzahl aus der Elementanzahl in der Initialisierung\n"
+ "spezifiziert. Wird eine Reihung nicht direkt initialisiert, so beschwert sich\n"
+ "der Compiler darüber, dass er nicht bestimmen kann, wieviel Elemente die\n"
+ "Reihung benötigt:\n"
+ "\n"
+ "int main(){\n"
+ " int xs [];\n"
+ " return 0;\n"
+ "}\n"
+ "Die Übersetzung dieses Programms führt zu einem Fehler:\n"
+ "\n"
+ " gcc src/UnknownSize.c\n"
+ "src/UnknownSize.c: In function `main':\n"
+ "src/UnknownSize.c:2: error: array size missing in `xs'\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Der C-Übersetzer muß immer in der Lage sein, die Anzahl der Elemente einer\n"
+ "Reihung abzuleiten. Der Grund dafür ist, daß sobald eine Variable deklariert\n"
+ "wird, der C Compiler den Speicherplatz für die entsprechende Variable\n"
+ "anfordern muß. In C bedeutet das bei einer Reihung, daß der Speicher für eine\n"
+ "komplette Reihung angefordert wird. \n"
+ "Hierfür muß die Anzahl der Elemente bekannt sein.\n"
+ "\n"
+ "Man kann die Anzahl der Elemente in den eckigen Klammern bei einer\n"
+ "Arraydeklaration angeben. Im folgenden Beispiel wird ein Array mit 15\n"
+ "Elementen angelegt.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int main(){\n"
+ " int xs [15];\n"
+ " printf(\"%i\n\",xs[2]);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Elementanzahl für eine Reihung muß nicht zur Übersetzungszeit bekannt\n"
+ "sein, sondern kann dynamisch errechnet werden und z.B.als Parameter einer\n"
+ "Funktion übergeben werden:\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "void f(int l){\n"
+ " int xs [l];\n"
+ " int i;\n"
+ " for (i= 0; i<l;i=i+1){\n"
+ " xs[i]=i;\n"
+ " printf(\"%i,\", xs[i]);\n"
+ " }\n"
+ " printf(\"\n\");\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " f(7);\n"
+ " f(2);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wie wir uns in der Ausgabe überzeugen können, werden in diesem Programm in der\n"
+ "Funktion f nacheinander zwei Reihungen unterschiedlicher Länge\n"
+ "erzeugt.Angeblich sollen bestimmte \n"
+ "Compiler derartige Programme nicht übersetzen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Einer Reihung kann man im allgemeinen nicht ansehen, wie viele Elemente sie\n"
+ "enthält. Das ist recht unangenehm, denn somit kann es z.B. passieren, das\n"
+ "versucht wird bei einer Reihung mit 10 Elementen, auf das 100.~Element\n"
+ "zuzugreifen. \n"
+ "#include <stdio.h>\n"
+ "int main(){\n"
+ " int xs [10];\n"
+ " printf(\"%i\n\",xs[100]);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Es gibt niemanden, der uns in diesem Programm auf die Finger haut. Wir können\n"
+ "es comilieren und laufen lassen. Es läuft sogar ohne Fehlermeldung und Programmabbruch:\n"
+ "\n"
+ " bin/WrongIndex\n"
+ "-1073744294\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Allerdings wird hier irgendwo in den Speicher gegriffen und der zufällig dort\n"
+ "gespeicherte Wert gelesen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir können Funktionen schreiben, die Reihungen als Parameter übergeben\n"
+ "bekommen. Diese Reihungen lassen sich im Funktionsrumpf ganz normal als\n"
+ "Reihungen behandeln:\n"
+ "\n"
+ "\n"
+ "int add(int xs [],int argc){\n"
+ " int result=0;\n"
+ " int i=0;\n"
+ " for (;i\n"
+ " \n"
+ "Wir können jetzt einmal in diesem Programm einen Fehler einbauen, um zu sehen,\n"
+ "als was für einen Typ der Compiler unseren Reihungsparameter betrachtet:\n"
+ "\n"
+ "\n"
+ "int add(int xs [],int argc){\n"
+ " int result=0;\n"
+ " int i=0\n"
+ " for (;i\n"
+ "\n"
+ "Auf diese Weise erzwingen wir eine Fehlermeldung des Compilers, in der die\n"
+ "erwarteten Parametertypen der Funktion add angegeben werden. Der\n"
+ "Kompilierversuch führt zu folgender Fehlermeldung:\n"
+ "\n"
+ " gcc -Wall src/WrongArrayParameter.c\n"
+ "src/WrongArrayParameter.c: In function `main':\n"
+ "src/WrongArrayParameter.c:14: warning: passing arg 2 of `add' makes integer from pointer without a cast\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Der Übersetzer ist der Meinung, der zweite Parameter, den wir der Funktion add übergeben, ist ein Zeiger. Der Übersetzer macht aus einem\n"
+ "Reihungsparameter implizit einen Zeiger. Und zwar einen Zeiger auf\n"
+ "das erste Element des Reihungsparameters. Ein Array int xs [] ist\n"
+ "also nichts weiter als ein Zeiger des Typs int* xs.\n"
+ "\n"
+ "Die Schreibweise der eckigen\n"
+ "Klammern für Reihungen für den Elementzugriff, läßt sich somit als \n"
+ "eine versteckte Zeigerarithmetik\n"
+ "interpretieren. xs[i] bedeutet dann: *(xs+i).\n"
+ "\n"
+ "Tatsächlich können wir ohne die Syntax der eckigen Klammern mit Reihungen\n"
+ "arbeiten. Obiges Programm läßt sich auch wie folgt formulieren:\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int add(int xs [],int argc){\n"
+ " int result=0;\n"
+ " int i=0;\n"
+ " for (;i<argc;i++){\n"
+ " result=result + *(xs+i);\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " int xs [] = {1,2,3,4};\n"
+ " printf(\"%i\n\", add(xs,4));\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Auch diese Version hat als Ergebnis die 10.\n"
+ "\n"
+ "Schreiben sie eine Funktion \n"
+ " void reverse(int* xs,int length), \n"
+ "die die Reihenfolge der Elemente des Arrays gerade einmal umdreht.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im letzem Abschnitt haben wir bereits\n"
+ "gesehen, dass bei der Parameterübergabe von Reihungen diese implizit als Zeiger\n"
+ "auf das erste Element behandelt werden. Gleiches gilt auch für die\n"
+ "Rückgabewerte von Funktionen. Dadurch lassen sich einige Probleme\n"
+ "einhandeln. Versuchen wir einmal eine einfache Funktion zu schreiben, die eine\n"
+ "neue Reihungsvariable anlegt und diese als Ergebnis zurückgibt:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int* newArray(int length){\n"
+ " int result [length];\n"
+ " int i=0;\n"
+ " for (;i<5;i++)result[i]=i;\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " int* xs = newArray(5);\n"
+ " int i=0;\n"
+ " for (;i<5;i++){\n"
+ " printf(\"%i \",xs[i]);\n"
+ " }\n"
+ " printf(\"\n\");\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Die Übersetzung dieser Klasse glückt zwar, aber der Übersetzer gibt uns eine\n"
+ " ernst zu nehmende Warnung:\n"
+ " gcc -std=c99 src/WrongArrayReturn.c\n"
+ "src/WrongArrayReturn.c: In function `newArray':\n"
+ "src/WrongArrayReturn.c:7: warning: function returns address of local variable\n"
+ "sep@pc305-3:~/fh/c/student> .]]>\n"
+ "\n"
+ "Die Warnung ist insofern ernst zu nehmen, als daß wir tatsächlich ein\n"
+ " undefiniertes Laufzeitverhalten bekommen:\n"
+ "\n"
+ " ./a.out\n"
+ "0 134518380 808989496 134513155 1074069176\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Den Grund hierfür kennen wir bereits aus unseren Kapitel über Zeiger. Implizit\n"
+ " behandelt der Übersetzer die Zeile return result als eine Rückgabe\n"
+ " des Zeigers auf die lokale Variable result. Eine lokale Variable\n"
+ " innerhalb einer Funktion, wird im Speicher nach Beendigung der Funktion\n"
+ " wieder freigegeben. Zeigen wir noch auf diese Variable, so werden wir an\n"
+ " ihrer Speicherzelle undefinierte Werte sehen.\n"
+ "\n"
+ "Die einzige vernünftigen Möglichkeiten der Rückgabe von Reihungen in C sind\n"
+ " die Rückgabe eines als Parameter überreichten Zeiger auf eine Reihung, oder\n"
+ " aber der Zeiger auf eine dynamisch neu erzeugte Reihung:\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "int* newArray(int length){\n"
+ " int* result = (int*)malloc(sizeof(int[length]));\n"
+ " int i=0;\n"
+ " for (;i<5;i++)result[i]=i;\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " int* xs = newArray(5);\n"
+ " int i=0;\n"
+ " for (;i<5;i++){\n"
+ " printf(\"%i \",xs[i]);\n"
+ " }\n"
+ " printf(\"\n\");\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Dieses Programm übersetzt ohne Warnung und liefert die erwartete Ausgabe.\n"
+ "\n"
+ " ./bin/ReturnArray\n"
+ "0 1 2 3 4\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Tatsächlich findet man es relativ selten in C Programmen, daß eine Funktion\n"
+ " eine Reihung als Rückgabewert hat. Zumeist bekommen Funktionen Reihungen als\n"
+ " Parameter übergeben und manipulieren diese. \n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir haben bereits gesehen, daß Funktionen über Funktionszeigern an andere\n"
+ "Funktionen als Parameter übergeben werden können. Im Zusammenhang mit\n"
+ "Reihungen eröffnet dieses ein mächtiges Programmierprinzip. Fast jede\n"
+ "Funktion, die sich mit Reihungen beschäftigt, wird einmal jedes Element der\n"
+ "Reihung durchgehen und dazu eine for-Schleife benutzen. Wie wäre es\n"
+ "denn, wenn man dieses Prinzip verallgemeinert und eine allgemeine Funktion\n"
+ "schreibt, die einmal jedes Element einer Reihung betrachtet und etwas mit ihm\n"
+ "macht. Was mit jedem Element zu tun ist, wird dieser Funktion als\n"
+ "Funktionszeiger übergeben. \n"
+ "\n"
+ "Funktionen, die als Parameter Funktionen übergeben bekommen, werden auch als\n"
+ "Funktionen höherer Ordnung bezeichnet.\n"
+ "\n"
+ "\n"
+ "Wir schreiben für Reihungen ganzer Zahlen die Funktion map, die jedes\n"
+ "Element einer Reihung mit einer übergebenen Funktion umwandelt: \n"
+ "\n"
+ "\n"
+ "void map(int xs [],int l,int (f) (int)){\n"
+ " int i=0;\n"
+ " for (;i\n"
+ "\n"
+ "Wir stellen drei Funktionen, die eine ganze Zahl als Parameter und Ergebnis\n"
+ "haben\n"
+ "zur Verfügung:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Jetzt können wir die Funktion map verwenden, um für jedes Element\n"
+ "einer Reihung, eine derartige Funktion anzuwenden. Zur Ausgabe von Reihungen\n"
+ "schreiben wir eine kleine Hilfsfunktion:\n"
+ "\n"
+ "\n"
+ "Und hier die Ausgabe des Programms:\n"
+ "\n"
+ " bin/IntMap\n"
+ "[6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]\n"
+ "[36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400]\n"
+ "[72, 98, 128, 162, 200, 242, 288, 338, 392, 450, 512, 578, 648, 722, 800]\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "Zunächst wird auf jedes Element der Reihung 5 aufaddiert. Dann werden die\n"
+ "Elemente quadriert und schließlich verdoppelt.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Funktion fold mit folgender Signatur: \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Spezifikation der Funktion sei durch folgende Gleichung gegeben:\n"
+ "\n"
+ "fold(xs,l,op,st) = op( op(op(op(st,xs[0]),xs[1]),xs[2]),xs[l-1])\n"
+ " \n"
+ "Oder bei Infixschreibweise der Funktion op gleichbedeutend über \n"
+ "folgende Gleichung:\n"
+ "\n"
+ "fold(,,,l,op,st) = opopop\n"
+ "op op\n"
+ "\n"
+ "Testen Sie Ihre Methode fold mit folgenden Programm:\n"
+ "\n"
+ "\n"
+ "#include \"Fold.h\"\n"
+ "\n"
+ "int add(int x,int y){return x+y;}\n"
+ "int mult(int x,int y){return x*y;}\n"
+ "\n"
+ "int fold(int xs [],int length,int (*foldOp)(int,int),int startV);\n"
+ "\n"
+ "int main(){\n"
+ " int xs [] = {1,2,3,4,5};\n"
+ " printf(\"%i\n\",fold(xs,5,add,0));\n"
+ " printf(\"%i\n\",fold(xs,5,mult,1));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Auf Reihungen läßt sich einer der populärsten Algorithmen der Infomatik\n"
+ "implementieren. Es der sogenannte bubble sort zum Sortieren der\n"
+ "Elemente einer Sammlung, in unserem Fall einer Reihung, nach einer bestimmten\n"
+ "Ordnung. \n"
+ "\n"
+ "Der Name bubble sort leitet sich davon\n"
+ "ab, daß Elemente wie die Luftblasen in einem Mineralwasserglass\n"
+ "innerhalb der Reihung aufsteigen, wenn sie laut der Ordnung an einen\n"
+ "höheren Platz gehören. Ein vielleicht phonetisch auch ähnlicher\n"
+ "klingender deutscher Name wäre Blubbersortierung. Dieser Name\n"
+ "ist jedoch nicht in der deutschen Terminologie etabliert und es wird\n"
+ "in der Regel der englische Name genommen.\n"
+ "\n"
+ "Die Idee des bubble sort ist, jeweils nur zwei benachbarte\n"
+ "Elemente einer Reihung zu betrachten und diese gegebenenfalls in ihrer\n"
+ "Reihenfolge zu vertauschen. Eine Reihung wird also von vorne bis hinten\n"
+ "durchlaufen, immer zwei benachbarte Elemente betrachtet und diese,\n"
+ "falls das vordere nicht kleiner ist als das hintere, getauscht. Wenn\n"
+ "die Liste in dieser Weise einmal durchgegangen wurde, ist sie entweder\n"
+ "fertig sortiert oder muß in gleicher Weise nocheinmal durchgegangen\n"
+ "werden, solange, bis keine Vertauschungen mehr vorzunehmen sind.\n"
+ "Allerdings braucht man bei einem weiteren Durchgang, das letzte Element nicht\n"
+ "mehr mit dem vorletzten zu vergleichen, da ein Blubberdurchgang sicherstellt,\n"
+ "dass das größte Element an die oberste Stelle kommt. \n"
+ "\n"
+ "\n"
+ "Betrachten wir ein Beispiel: \n"
+ "Die Reihung (\"z\",\"b\",\"c\",\"a\") wird durch \n"
+ "den bubble sort-Algorithmus in folgender Weise sortiert:\n"
+ "\n"
+ " 1. Bubble-Durchlauf \n"
+ "(\"z\",\"b\",\"c\",\"a\") \n"
+ "(\"b\",\"z\",\"c\",\"a\") \n"
+ "(\"b\",\"c\",\"z\",\"a\") \n"
+ "(\"b\",\"c\",\"a\",\"z\")\n"
+ "Das Element \"z\" ist in diesem Durchlauf an das Ende der Reihung\n"
+ "geblubbert. \n"
+ "\n"
+ "\n"
+ " 2. Bubble-Durchlauf \n"
+ "(\"b\",\"c\",\"a\",\"z\") \n"
+ "(\"b\",\"a\",\"c\",\"z\")\n"
+ "\n"
+ "In diesem Durchlauf ist das Element \"c\" um einen Platz nach\n"
+ "hinten geblubbert.\n"
+ "\n"
+ " 3. Bubble-Durchlauf \n"
+ "(\"b\",\"a\",\"c\",\"z\") \n"
+ "(\"a\",\"b\",\"c\",\"z\")\n"
+ "\n"
+ "Im letzten Schritt ist das Element \"b\" auf seine endgültige\n"
+ "Stelle geblubbert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Blubbersortierung läßt sich relativ leicht für Reihungen implementieren. Eine\n"
+ "Reihung ist dabei nur mehrfach zu durchlaufen und nebeneinanderstehende\n"
+ "Elemente sind eventuell in der Reihenfolge zu vertauschen. Dieses wird so\n"
+ "lange gemacht, bis keine Vertauschung mehr stattfindet, maximal aber um eines\n"
+ "weniger, als die Reihung Elemente hat.\n"
+ "\n"
+ "\n"
+ "typedef enum {false,true} boolean;\n"
+ "\n"
+ "void bubble(int* xs, int i){\n"
+ " boolean changed = true ;\n"
+ " for (;changed && i>1;i--){\n"
+ " changed=false;\n"
+ " int j=0;\n"
+ " for (;j<(i-1);j++){\n"
+ " if (xs[j]>xs[j+1]){\n"
+ " int tmp=xs[j];\n"
+ " xs[j]=xs[j+1];\n"
+ " xs[j+1]=tmp;\n"
+ " changed=true;\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " int xs [] = {321,43,43,5345,54,543,543,543,43,1,4,2};\n"
+ " bubble(xs,12);\n"
+ " int i=0;\n"
+ " for (;i<12;i++)printf(\"%i,\",xs[i]);\n"
+ " printf(\"\n\");\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Wem die obige Implementierung zu komplex innerhalb einer Funktion ist, der\n"
+ " kann sie sinnvoller Weise auf mehrere Funktionen splitten. Drei Aufgabe sind\n"
+ " bei diesem Sotrierverfahren zu berwerkstelligen:\n"
+ "\n"
+ "\n"
+ "jeweils zwei Elemente eines Arrays sind zu vertauschen\n"
+ "ein Array ist einmal vom Anfang bis zu einem vorgegebenen Index zu\n"
+ "durchlaufen. Sind zwei benachbarte Elemente nicht in der richtigen\n"
+ "Reihenfolge, so sind diese zu vertauschen. \n"
+ "es ist so oft durch einen Array immer wieder zu durchlaufen, bis er\n"
+ "sortiert ist.\n"
+ "\n"
+ "\n"
+ "Entsprchend läßt sich die Sortierung mit drei kleinen Teilfunktionen\n"
+ "implementieren:\n"
+ "\n"
+ "Zunächst die kleine Hilfsmethode zum Vertauschen zweier Elemente eines\n"
+ "Arrays. Die Indizes dieser Elemente werden als weitere Parameter übergeben:\n"
+ "\n"
+ "#include \n"
+ "typedef enum {false,true} boolean;\n"
+ "\n"
+ "void swap(int* xs,int i,int j){\n"
+ " int tmp=xs[i];\n"
+ " xs[i]=xs[j];\n"
+ " xs[j]=tmp;\n"
+ "}]]>\n"
+ "\n"
+ "Als zweite Funktion kann das eigentliche bubblen realsiert werden:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Dieses wird nun so oft auf einem Array angewendet, bis dieser sortiert ist.\n"
+ "\n"
+ "1;length--) \n"
+ " changed=bubble(xs,length);\n"
+ "}]]>\n"
+ "\n"
+ "Als kleine Demonstration noch einmal die Testfunktion:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Ein Sortieralgorithmus ist vollkommen unabhängig davon, wonach sortiert werden\n"
+ "soll. Die obige Implementierung kann nur sortieren, dass erst die kleinen und\n"
+ "dann die großen Zahlen kommen. Wenn wir umgekehrt von groß nach klein\n"
+ "sortieren wollen, muß der ganze Algorithmus komplett neu implementiert werden,\n"
+ "nur um ein einziges Zeichen zu ändern, nämlich das Kleinerzeichen ins\n"
+ "Größerzeichen. Das würde zu einer immensen Codeverdoppelung führen. Daher\n"
+ "wäre es doch schön, den bubblesort so zu implementieren, dass\n"
+ "flexibel gehalten ist, nach welcher Sotrierrelation sortiert werden soll. Da\n"
+ "wir in C Funktionen als Parameter übergeben können, können wir der\n"
+ "Sotrierfunktion als zusätzlichen Parameter eine Funktion übergeben, die für\n"
+ "zwei Elemente true zurückgibt, wenn das erste kleiner als das zweite\n"
+ "ist. \n"
+ "\n"
+ "So implementieren wir bubblesort als Funktion höherer Ordnung. Die\n"
+ "Funktion swap ist davon nicht betroffen:\n"
+ "\n"
+ "\n"
+ "\n"
+ "typedef enum {false,true} boolean;\n"
+ "\n"
+ "void swap(int* xs,int i,int j){\n"
+ " int tmp=xs[i];\n"
+ " xs[i]=xs[j];\n"
+ " xs[j]=tmp;\n"
+ "}]]>\n"
+ "\n"
+ "Die Funktion bubble bekommt einen zusätzliche Parameter. Eine\n"
+ "Funktion, die für zwei Zahlen entscheidet, ob die erste kleiner in einer\n"
+ "Relation ist. Diese wird dann verwendet, beim Vergleich der benachbarten zwei\n"
+ "Arrayelemente: \n"
+ "\n"
+ "boolean bubble(int* xs,int i,boolean smaller(int,int)){\n"
+ " boolean result = false;\n"
+ " int j=0;\n"
+ " for (;j<(i-1);j++)\n"
+ " if (!smaller(xs[j],xs[j+1])){\n"
+ " swap(xs,j,j+1);\n"
+ " result=true;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "Ebenso braucht die Funktion bubbleSort diesen Funktionsparameter, um\n"
+ "ihn an die Funktion bubble weiterzureichen:\n"
+ "\n"
+ "void bubbleSort(int* xs, int length,boolean smaller(int,int)){\n"
+ " boolean changed = true;\n"
+ " for (;changed && length>1;length--) \n"
+ " changed=bubble(xs,length,smaller);\n"
+ "}\n"
+ "\n"
+ "Soweit der Bubblesort mit der Sortierrelation als Parameter. Jetzt seien\n"
+ "einmal drei verschiedene Sortierrelationen zu schreiben. Einmal sollen kleine\n"
+ "vor großen Zahlen kommen, einmal umgekehrt, und in der dritten Sortierrelation\n"
+ "seien ungerade Zahlen prinzipiell kleiner als gerade Zahlen:\n"
+ "\n"
+ "y;}\n"
+ "boolean oddFirst(int x,int y){return x%2>y%2 || (x%2==y%2 && x\n"
+ "\n"
+ "Für die nun folgenden Test, sei wieder eine kleine Ausgabefunktion für\n"
+ "Reihungen geschrieben:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Und jetzt können wir ein und dieselbe Sortierfunktion benutzen, um nach ganz\n"
+ "unterschiedlichen Relationen zu sortieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Und tatsächlich wird die Reihung dreifach unterschiedlich sortiert:\n"
+ "\n"
+ " bin/BubbleSort3\n"
+ "[1, 2, 4, 43, 43, 43, 54, 321, 543, 543, 543, 5345]\n"
+ "[5345, 543, 543, 543, 321, 54, 43, 43, 43, 4, 2, 1]\n"
+ "[1, 43, 43, 43, 321, 543, 543, 543, 5345, 2, 4, 54]\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im letzten Abschnitt haben wir die Sortierrelation, über die sortietr werden\n"
+ "variabel gehalten und als Parameter übergeben. Wir haben aber stets nur\n"
+ "Reihungen, die ganze Zahlen als Elemente enthalten haben, sortiert. Im Prinzip\n"
+ "ist einem Sortieralgorithmus auch gleichgültig, um was für Elemente es sich in\n"
+ "einer Reihung handelt, solange eine Sotrierrelation auf diesen Elementen\n"
+ "bekannt ist. \n"
+ "\n"
+ "So soll jetzt der Bubblesort so modifiziert werden, dass er beliebige\n"
+ "Datenobjekte sortieren kann. Die einziege Möglichkeit, die es in C gibt über\n"
+ "beliebige Daten zu sprechen, ist über den void-Zeiger. Um unser\n"
+ "vorgehen noch ein wenig plastischer zu machen, sei der \n"
+ "Typ void* als Object bezeichnet:\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "typedef void* Object;]]>\n"
+ "\n"
+ "Die Funktion swap ändert sich im Prinzip nicht. Wir müssen nur\n"
+ "berücksichtigen, dass nun nicht in einer Reihung mit int-Zahlen zwei\n"
+ "Elemente vertauscht werden, sondern in einer Reihung von Objekten:\n"
+ "\n"
+ "typedef enum {false,true} boolean;\n"
+ "\n"
+ "void swap(Object* xs,int i,int j){\n"
+ " Object tmp=xs[i];\n"
+ " xs[i]=xs[j];\n"
+ " xs[j]=tmp;\n"
+ "}\n"
+ "\n"
+ "Tatsächlich ändert sich auch nichts in der Funktion bubble, bis auf\n"
+ "dass der Typ int durch den Typ Object ersetzt wurde:\n"
+ "\n"
+ "\n"
+ "Das gleiche gilt für die Funktion bubbleSort:\n"
+ "\n"
+ "1;length--) \n"
+ " changed=bubble(xs,length,smaller);\n"
+ "}]]>\n"
+ "\n"
+ "In der Funktion printArray bekommen wir ein kleines Problem. Wir\n"
+ "wissen nicht, um was für Objekte es sich in der Reihung handelt, somit können\n"
+ "wir auch nicht wissen, wie diese Objekte auszugeben sind. Daher benötigen wir\n"
+ "noch eine weitere Funktion als Parameter übergeben. Diese soll ein Objekt auf\n"
+ "der KOmmandozeile ausgeben:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Soweit die Implementierung der allgemeinen Sortierung. Wir haben nun zum einen\n"
+ "die Art der Daten, die sortiert werden sollen, zum anderen die Sortierrelation\n"
+ "offen gelassen. Um die Sortierung zu testen, seien ein paar Klassen\n"
+ "definiert. Zunächst einmal mehr die schon bekannte Punktklasse für Punkte im\n"
+ "zweidimensionalen Raum:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es seien wieder entsprechend Konstruktor und Destruktor definiert, sowie eine\n"
+ "einfache Funktion für das Quadrat der Entfernung zum Nullpunkt:\n"
+ "\n"
+ "x=x; \n"
+ " this->y=y;\n"
+ " return this;\n"
+ "} \n"
+ "\n"
+ "void deletePunkt(Punkt this){\n"
+ " free(this);\n"
+ "}\n"
+ "\n"
+ "int square(int x){return x*x;}\n"
+ "int betragQuadrat(Punkt p){return square(p->x)+square(p->y);}\n"
+ "]]>\n"
+ "\n"
+ "Um Punktobjekte zu sortieren, brauchen wir auf Punktobjekten eine \n"
+ "Sortieerrelation. Punkte, die näher am Nullpunkt liegen, seien kleiner als\n"
+ "weiter entfernte in dieser Relation:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir müssen auch eine Methode zur Ausgabe eines Punktobjektes definieren:\n"
+ "\n"
+ "x,((Punkt)p)->y);\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Als zweites wollen wir auch wieder ganze Zahlen sortieren, allerdings können\n"
+ "wir nur verzeigerte Objekte sortieren. Somit sei aich eine Klasse \n"
+ "die int-Zahlen repräsentiert, definiert. Auch diese Klasse sei gleich\n"
+ "mit Konstruktor, destruktor, der print-Methode und schließlich einer\n"
+ "Kleinerrelation definiert:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Als dritte Klasse von Elemente, die sortiert werden sollen, sei nun noch eine\n"
+ "Klasse für Wahrheitswerte definiert. Auch diese wieder mit Konstruktor,\n"
+ "Destruktor, print-Methode und schließlich einer\n"
+ "Kleinerrelation:\n"
+ "\n"
+ "\n"
+ "Nun kann es losgehen. Zunächst sei eine Reihung von Punkten definiert:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Jetzt können wir dieselbe Sortierfunktion benutzen, um eine Reihung von Zahlen\n"
+ "zu sortieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Und auf die gleiche Weise können wir auch eine Reihung von Wahrheitswerten\n"
+ "sortieren: \n"
+ "\n"
+ "\n"
+ "\n"
+ "Und tatsächlich werden die drei Reihungen mit ihren ganz unterschiedlichen\n"
+ "Daten alle korrekt sortiert:\n"
+ "\n"
+ " bin/BubbleSort4\n"
+ "[(4,2), (43,1), (321,43), (54,543), (543,543), (43,5345)]\n"
+ "[37, 38, 39, 40, 41, 42]\n"
+ "[true, true, false, false, false, false]\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wahrscheinlich die am häufigsten benötigten Daten sind Texte. Texte\n"
+ "unbestimmter Länge, die Wachsen und Schrumpfen können. Texte die editiert\n"
+ "werden sollen und modifiziert. Unglücklicher Weise stellt C für Texte keinen\n"
+ "eigenen Typ zur Verfügung. Damit steht C als Programmiersprache ziemlich\n"
+ "allein dar, denn fast alle neueren Programmiersprachen bieten komfortable\n"
+ "Mittel an, um Texte in Form von Zeichenketten zu bearbeiten.\n"
+ "\n"
+ "Dabei haben wir bereits gesehen, dass auch C nicht gänzlich blind für\n"
+ "Zeichenketten ist, denn es wird ja ein entsprechendes Literal angeboten.\n"
+ "Zeichenketten können in dopppelten Hochkommas eingeschlossen als Literale in C\n"
+ "ausgedrückt werden. Es fragt sich, wenn es nicht wie in anderen\n"
+ "Programmiersprachen einen Typ string für Zeichenketten gibt, als was\n"
+ "für einen Typ werden die string-Literale dann gespeichert? \n"
+ "\n"
+ "Die Antwort liegt in den geraden kennengelernten Reihungen. \n"
+ "Ein string ist eine Reichung vom einzelnen Werten des \n"
+ "Typs char, und da es sich bei Reihungen lediglich um einen Zeiger auf\n"
+ "das erste Reihungselement handelt, sind tatsächlich Zeichenketten als Werte\n"
+ "des Typs char* gespeichert.\n"
+ "\n"
+ "\n"
+ "Intern hat jeder C-String ein Element mehr als Zeichen. In diesem letzten\n"
+ "Element ist mit einer 0 markiert, daß der String jetzt zu Ende\n"
+ "ist. Zeichenketten werden also intern durch ein Nullelement beendet. Dieses\n"
+ "ist unabhängig von der eigentlichen technischen Länge der Reihung. \n"
+ "\n"
+ "Initialisiert man eine Reihung von Zeichen mit der Mengenklammeraufzählung ist\n"
+ "der beendende Nullwert unbedingt zu berücksichtigen. \n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int main(){\n"
+ " char hello[] = \n"
+ " { 'H', 'e', 'l', 'l', 'o', '\\0' };\n"
+ " printf(\"%s\n\",hello);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wird die Endemarkierung bei der Initialisierung vergessen, so blickt C\n"
+ "unkontrolliert weiter in den Speicher und betrachtet alles nachfolgende als\n"
+ "Zeichen der Zeichenkette, bis zufällig ein Nullwert kommt. Das folgende\n"
+ "Programm erzeugt auch eine indeterministische Ausgabe:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " char hello[] = \n"
+ " { 'H', 'e', 'l', 'l', 'o'};\n"
+ " printf(\"%s\n\",hello);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ " ./bin/CString3\n"
+ "Hello¿½@@0G�@\n"
+ " sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "Für C ist eine Zeichenkette immer genau dann beendet, wenn der abschließende\n"
+ "Nullwert auftritt. Dieses gilt auch, wenn er inmitten der Reihung auftritt. \n"
+ "\n"
+ "Im folgenden Programm ist nach der Zuweisung des Nullwerts für das Element am\n"
+ "Index 6 für C der String nach dem Wort hello beendet, obwohl die\n"
+ "Reihung länger ist und noch weitere sinnvolle Zeichen in der Reihung\n"
+ "gespeichert sind.\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int main(){\n"
+ " char hello [] = \"hello world!\";\n"
+ " hello[5] ='\\0';\n"
+ "\n"
+ " printf(\"%s\n\",hello);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "\n"
+ " ./bin/CString4\n"
+ "hello\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "In traditioneller C-Programmierung gibt es typischer Weise Reihungen einer\n"
+ "maximalen Länge, in denen unterschiedlich lange Strings gespeichert sind. Für\n"
+ "das Programm bedeutet das, daß die Stringwerte an diesen Stellen eine\n"
+ "bestimmte Maximallänge nicht überschreiten dürfen. Daher kommt in vielen\n"
+ "älteren Programmen wahrscheinlich die Restriktion, daß bestimmte Namen, Pfade\n"
+ "oder Optionen nur\n"
+ "eine bestimmte Länge haben dürfen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " char* str = \"hallo\";\n"
+ " printf(\"der String \\\"%s\\\" hat %i Zeichen\n\",str,stringLaenge(str));\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "='a' && c <= 'z'){\n"
+ " return c+LOWER_TO_UPPER;\n"
+ " }\n"
+ " return c;\n"
+ "} \n"
+ "\n"
+ "void toUpper(char* source,char* target){\n"
+ " int i=0;\n"
+ " for (;source[i]!='\\0';i++){\n"
+ " target[i] = charToUpper(source[i]);\n"
+ " }\n"
+ " target[i]='\\0';\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " char* str = \"hallo Du da!\";\n"
+ " char erg [stringLaenge(str)+1]; \n"
+ " toUpper(str,erg);\n"
+ " printf(\"der String \\\"%s\\\" in Grossbuchstaben: \\\"%s\\\"\n\",str,erg);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Auch für die\n"
+ "Hauptmethode hat es Auswirkungen, daß C Reihungen ihre Elementanzahl nicht\n"
+ "kennen. Unsere bisherigen Hauptmethoden\n"
+ "hatten keine Argumente. Man kann zur Kommandozeilenübergabe in C aber auch\n"
+ "eine Hauptmethode schreiben, der in einer Reihung die Kommandozeilenoptionen\n"
+ "übergeben werden. Hierzu braucht man dann aber auch ein weiteres Argument, das\n"
+ "die Länge der übergebenen Reihung mit angibt.\n"
+ "\n"
+ "#include <stdio.h>\n"
+ "\n"
+ "int main(int argc, char* argv []){ \n"
+ " int i;\n"
+ " for (i=0;i<argc;i++){\n"
+ " char* arg = argv[i];\n"
+ " printf(\"%s\n\",arg);\n"
+ " }\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "Wie man an dem folgenden Testlauf sieht, wird als erstes Reihungselement der\n"
+ "Name des Programms übergeben. \n"
+ "\n"
+ " ./bin/CommandLineOption hallo C -v gruss\n"
+ "./bin/CommandLineOption\n"
+ "hallo\n"
+ "C\n"
+ "-v\n"
+ "gruss\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Als Summentyp wird in der Informatik ein Typ bezeichnet, der die Vereinigung\n"
+ "einer oder mehrerer Typen darstellt.\n"
+ "Auch hierfür stellt C ein eigenes\n"
+ "Konstrukt zur Verfügung: die Vereinigung von Typen, die union. \n"
+ "\n"
+ "Syntaktisch sieht eine Typvereinigung einer Struktur sehr ähnlich. Lediglich\n"
+ "das Schlüsselwort struct ist durch das Schlüsselwort union zu ersetzen. Somit läßt sich eine Vereinigung der \n"
+ "Typen int und char* definieren \n"
+ "als: \n"
+ "union stringOrInt {int i;char *s;};.\n"
+ "\n"
+ "Daten des Typs union stringOrInt sind nun entweder eine Zahl, die\n"
+ "über das Attribut i angesprochen werden kann, oder der Zeiger auf das\n"
+ "erstes Element einer Zeichenkette, der über das \n"
+ "Attribut s angesprochen werden kann.\n"
+ "\n"
+ "\n"
+ "Im folgenden kleinem Programm wird eine Typsumme definiert und beispielhaft\n"
+ "in einer Hauptmethode benutzt. \n"
+ "\n"
+ "\n"
+ "union stringOrInt {int i;char *s;};\n"
+ "\n"
+ "int main(){\n"
+ " union stringOrInt sOrI;\n"
+ " sOrI.s=\"hallo da draussen\";\n"
+ " printf(\"%s\n\", sOrI.s);\n"
+ "\n"
+ " sOrI.i=42;\n"
+ " printf(\"%i\n\", sOrI.i);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "Wie man der Ausgabe entnehmen kann, wird in der Variablen des Typs stringOrInt entweder ein int oder eine Zeichenkettenreferenz gespeichert:\n"
+ " gcc -o Union1 Union1.c\n"
+ "sep@linux:~/fh/prog3/examples/src> ./Union1\n"
+ "hallo da draussen\n"
+ "42\n"
+ "sep@linux:~/fh/prog3/examples/src>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im letzten Beispiel haben wir die Typsumme gutartig benutzt. Die Typsumme\n"
+ "drückt aus, daß die Daten entweder von einem oder vom anderen Typ sind. Im\n"
+ "Beispiel haben wir uns gemerkt, welchen Typ wir als letztes in der\n"
+ "entsprechenden Variablen gespeichert haben und nur auf diesen Typ\n"
+ "zugegriffen. C bewahrt uns aber nicht davor, dieses immer korrekt zu tun. \n"
+ "\n"
+ "\n"
+ "In diesem Beispiel benutzen wir den selben Summentypen wie im vorhergehenden\n"
+ "Beispiel. Jetzt interpretieren wir aber jeweils die Daten auch als die andere\n"
+ "Typalternative. \n"
+ "\n"
+ "\n"
+ "union stringOrInt {int i;char *s;};\n"
+ "\n"
+ "int main(){\n"
+ " union stringOrInt sOrI;\n"
+ " sOrI.s=\"hallo da draussen\";\n"
+ " printf(\"%s\n\", sOrI.s);\n"
+ " printf(\"%i\n\", sOrI.i);\n"
+ "\n"
+ " sOrI.i=42;\n"
+ " printf(\"%s\n\", sOrI.s);\n"
+ " printf(\"%i\n\", sOrI.i);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Wenn wir jetzt einen Adresse auf einen Zeichenkette in der Variablen sOrI gespeichert haben und auf die Variable mit \n"
+ "ihrem int-Attribut i zugreifen, so bekommen wir die\n"
+ "gespeicherte Adresse als Zahl geliefert.\n"
+ "\n"
+ "Wenn wir umgekehrt eine Zahl gespeichert haben und diese nun als Adresse einer\n"
+ "Zeichenkette lesen wollen, so kommt es in der Regel zu einem Fehler, weil wir\n"
+ "höchstwahrscheinlich versuchen eine Adresse im Speicher zu lesen, auf die wir\n"
+ "nicht zugreifen können.\n"
+ " ./Union2\n"
+ "hallo da draussen\n"
+ "134514920\n"
+ "42\n"
+ "Speicherzugriffsfehler\n"
+ "sep@linux:~/fh/prog3/examples/src>]]>\n"
+ "\n"
+ "\n"
+ "Um Fehler, wie im letzem Beispiel zu vermeiden, ist es ratsam Strukturen,\n"
+ "Aufzählungen und\n"
+ "Typsummen geschachtelt zu verwenden. Hierzu kapselt man die Typsumme in eine\n"
+ "Struktur. In dieser Struktur gibt ein zweites Attribut an, um welchen Typ es\n"
+ "sich gerade in der Typsumme handelt. Für das Attribut, das den Typ\n"
+ "kennzeichnet, bietet sich an, einen Aufzählungstypen zu verwenden.\n"
+ "\n"
+ "\n"
+ "In diesem Beispiel definieren wir eine Typsumme, die in einer Struktur\n"
+ "eingebettet ist. Dazu definieren wir drei Typen: eine Aufzählung, eine\n"
+ "Typsumme und eine Struktur.\n"
+ "\n"
+ "\n"
+ "typedef enum {INT,STRING} TypeMarker;\n"
+ "typedef union {int i;char * s;} stringOrInt;\n"
+ "\n"
+ "typedef struct {TypeMarker type;stringOrInt v;} strOrInt;]]>\n"
+ "\n"
+ "Jetzt können wir uns zwei Funktionen schreiben, die jeweils eine der beiden\n"
+ "Varianten unseres Summentypens konstruieren:\n"
+ "\n"
+ "\n"
+ "Schließlich ein Beispiel für eine Funktion, die den neuen Typen\n"
+ " benutzt. Hierzu ist zunächst zu fragen, von was für einen Typ denn die\n"
+ " gespeichert sind, um dann entsprechend mit den Daten zu verfahren:\n"
+ "\n"
+ "Abschließend ein kleiner Test in einer Hauptmethode:\n"
+ "\n"
+ "\n"
+ "In der Ausgabe können wir uns davon überzeugen, daß tatsächlich immer die\n"
+ "Daten der Typsumme korrekt interpretiert werden:\n"
+ "\n"
+ " gcc -o Union3 Union3.c\n"
+ "sep@linux:~/fh/prog3/examples/src> ./Union3\n"
+ "hallo da draussen\n"
+ "42\n"
+ "sep@linux:~/fh/prog3/examples/src>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schließlich ist noch interessant, wieviel Speicher C für eine Typsumme\n"
+ "reserviert. Auch dieses können wir experimentel erfragen:\n"
+ "\n"
+ "\n"
+ "typedef union {char a1 [15]; char a2 [200];} U1;\n"
+ "typedef union {int a1; char a2 [16];} U2;\n"
+ "\n"
+ "int main(){\n"
+ " U1 u1;\n"
+ " U2 u2;\n"
+ " std::cout << sizeof(u1) << std::endl;\n"
+ " std::cout << sizeof(u2) << std::endl;\n"
+ " }]]>\n"
+ "An der Ausgabe ist zu erkennen, daß der maximal notwendige Speicher für einen\n"
+ " der Typen in der Summe angefordert wird. \n"
+ " bin/Union4\n"
+ "200\n"
+ "16\n"
+ "sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Als ein fundamentales Grundkonzept der Informatik haben wir die Rekursion\n"
+ "kennengelernt. Wir haben rekursive Funktionen definiert. Eine rekursive\n"
+ "Funktion ruft sich selbst in ihrem Rumpf wieder auf. Nicht nur Funktionen,\n"
+ "auch Daten können rekursiv definiert werden. Rekursive Daten sind Daten, die\n"
+ "sich selbst wieder enthalten. \n"
+ "\n"
+ " \n"
+ "Eine der häufigsten Datenstrukturen in der Programmierung sind\n"
+ "Sammlungstypen. In fast jedem nichttrivialen Programm wird es Punkte\n"
+ "geben, an denen eine Sammlung mehrerer Daten gleichen Typs anzulegen\n"
+ "sind. Eine der einfachsten Strukturen, um Sammlungen anzulegen, sind\n"
+ "Listen. Das Wesen einer Liste ist, dass ihre maximale Länge nicht in\n"
+ "vornherein bekannt ist. Es kannt also nicht statisch zuvor ein Array im\n"
+ "Speicher angelegt werden, in dem nach einander die Elemente gespeichert\n"
+ "werden. Eine Liste soll die Möglichkeit haben, ohne vorgesetztes Limit\n"
+ "beliebig wachsen zu können.\n"
+ "\n"
+ "\n"
+ "Wir werden Listen als abstrakten Datentyp formal spezifizieren. Ein abstrakter\n"
+ "Datentyp (ADT) wird spezifiziert über eine endliche Menge von Funktionen. \n"
+ "Hierzu wird\n"
+ "spezifiziert, auf welche Weise Daten eines ADT konstruiert werden\n"
+ "können. Dazu werden entsprechende Konstruktorfunktionen spezifiziert. Dann\n"
+ "wird eine Menge von Funktionen definiert, die wieder Teile aus den\n"
+ "konstruierten Daten selektieren können. Schließlich werden noch Testfunktionen\n"
+ "spezifiziert, die angeben, mit welchem Konstruktor ein Datum erzeugt\n"
+ "wurde.\n"
+ "\n"
+ "Der Zusammenhang zwischen Konstruktoren und Selektoren sowie zwischen den\n"
+ "Konstruktoren und den Testfunktionen wird in Form von Gleichungen spezifiziert.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n";
String skript7 =
"Der Trick, der angewendet wird, um abstrakte Datentypen wie Listen zu\n"
+ "spezifizieren, ist die Rekursion. Das Hinzufügen eines weiteren\n"
+ "Elements zu einer Liste wird dabei als das Konstruieren einer neuen\n"
+ "Liste aus der Ursprungsliste und einem weiteren Element\n"
+ "betrachtet. Mit dieser Betrachtungsweise haben Listen eine rekursive\n"
+ "Struktur: eine Liste besteht aus dem zuletzt vorne angehängten neuen\n"
+ "Element, dem sogenannten Kopf der Liste, und aus der alten Teilliste,\n"
+ "an die dieses Element angehängt wurde, dem sogenannten Schwanz der\n"
+ "Liste. Wie bei jeder rekursiven Struktur bedarf es eines Anfangs der\n"
+ "Definition. Im Falle von Listen wird dieses durch die Konstruktion\n"
+ "einer leeren Liste spezifiziert.Man vergleiche es mit der\n"
+ "Definition der natürlichen Zahlen: die 0 entspricht der leeren\n"
+ "Liste, der Schritt von n nach n+1 dem Hinzufügen eines neuen\n"
+ "Elements zu einer Liste.\n"
+ "\n"
+ "\n"
+ "Abstrakte Datentypen wie Listen lassen sich durch\n"
+ "ihre Konstruktoren spezifizieren. Die\n"
+ "Konstruktoren geben an, wie Daten des entsprechenden Typs konstruiert\n"
+ "werden können.\n"
+ "In dem Fall von Listen bedarf es nach\n"
+ "den obigen Überlegungen zweier Konstruktoren:\n"
+ "\n"
+ "\n"
+ "\n"
+ " einem Konstruktor für neue Listen, die noch leer sind.\n"
+ " einem Konstruktor, der aus einem Element und einer bereits\n"
+ "bestehenden Liste eine neue Liste konstruiert, indem an die\n"
+ "Ursprungsliste das Element vorne angehängt wird.\n"
+ "\n"
+ "\n"
+ "Wir benutzen in der Spezifikation eine mathematische Notation der\n"
+ "Typen von Konstruktoren.Entgegen der Notation in C, in der\n"
+ "der Rückgabetyp kurioser Weise vor den Namen der Funktionen geschrieben\n"
+ "wird. Dem Namen des Konstruktors folgt dabei mit einem\n"
+ "Doppelpunkt abgetrennt der Typ. Der Ergebnistyp wird von den\n"
+ "Parametertypen mit einem Pfeil getrennt.\n"
+ "\n"
+ "Somit lassen sich die Typen der zwei Konstruktoren für Listen wie\n"
+ "folgt spezifizieren:\n"
+ "\n"
+ "\n"
+ "Empty: () List\n"
+ "Cons: (int,List) List\n"
+ "\n"
+ "\n"
+ "Der Konstruktor für leere Listen hat keinen Parameter. Der \n"
+ "Konstruktor Cons hat ein neues Listenelement und eine bestehende Liste\n"
+ "als Parameter. Hier versteckt sich die Rekursion. Um eine neue Liste \n"
+ "mit Cons zu erzeugen, bedient man sich einer bereits bestehenden Liste.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Selektoren können wieder auf die einzelnen\n"
+ "Bestandteile der Konstruktion zurückgreifen. \n"
+ "Der Konstruktor Cons hat zwei Parameter. Für Cons-Listen \n"
+ "werden zwei\n"
+ "Selektoren spezifiziert, die jeweils einen dieser beiden Parameter\n"
+ "wieder aus der Liste selektieren. Die Namen dieser beiden Selektoren\n"
+ "sind traditioneller Weise head und tail.\n"
+ "\n"
+ "\n"
+ "head: (List)int\n"
+ "tail: (List)List\n"
+ "\n"
+ "\n"
+ "Der funktionale Zusammenhang von Selektoren und Konstruktoren läßt\n"
+ "sich durch folgende Gleichungen spezifizieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "head(Cons(x,xs)) x \n"
+ "tail(Cons(x,xs)) xs\n"
+ "\n"
+ "\n"
+ "Die erste Gleichung ist zu lesen: \n"
+ "wenn nach dem head einer Liste\n"
+ "gefragt wird, und diese durch den Cons-Konstruktor mit einem neuen \n"
+ "Element x und einer bestehenden Liste xs erzeugt wurde, dann\n"
+ "ist das Ergebnis das Element x.\n"
+ "\n"
+ "Analog ist die zweite Gleichung zu lesen: \n"
+ "wenn nach dem tail einer Liste\n"
+ "gefragt wird, und diese durch den Cons-Konstruktor mit einem neuen \n"
+ "Element x und einer bestehenden Liste xs erzeugt wurde, dann\n"
+ "ist das Ergebnis die Restliste xs.\n"
+ "\n"
+ "\n"
+ "Wie man sieht gibt es keine Spezifikation \n"
+ "von head und tail für den Fall, dass die fragliche Liste\n"
+ "eine leere Liste ist, also eine Liste, die durch Empty erzeugt wurde. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Um für Listen Algorithmen umzusetzen, ist es notwendig, unterscheiden\n"
+ "zu können, welche Art der beiden Listen vorliegt: die leere Liste oder\n"
+ "eine Cons-Liste. Hierzu bedarf es noch einer Testfunktion, die\n"
+ "mit einem bool'schen Wert als Ergebnis angibt, ob es sich bei der\n"
+ "Eingabeliste um die leere Liste handelte oder nicht. Wir wollen diese\n"
+ "Testfunktion isEmpty nennen. Sie hat folgenden Typ:\n"
+ "\n"
+ "\n"
+ "isEmpty: Listboolean\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das funktionale Verhalten der Testfunktion läßt sich durch folgende\n"
+ "zwei Gleichungen spezifizieren:\n"
+ "\n"
+ "\n"
+ "isEmpty(Empty()) true \n"
+ "isEmpty(Cons(x,xs)) false\n"
+ "\n"
+ "\n"
+ "Somit ist alles spezifiziert, was eine Listenstruktur ausmacht. Listen\n"
+ "können konstruiert werden, die Bestandteile einer Liste wieder einzeln\n"
+ "selektiert und Listen können nach der Art ihrer Konstruktion\n"
+ "unterschieden werden. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Allein diese fünf Funktionen beschreiben den ADT der\n"
+ "Listen. Wir können aufgrund dieser Spezifikation Algorithmen für Listen\n"
+ "schreiben.\n"
+ "\n"
+ "\n"
+ "Es läßt sich durch zwei Gleichungen spezifizieren, was die Länge einer\n"
+ "Liste ist:\n"
+ "\n"
+ "\n"
+ "length(Empty())0 \n"
+ "length(Cons(x,xs))1+length(xs)\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit Hilfe dieser Gleichungen läßt sich jetzt schrittweise die Berechnung einer\n"
+ "Listenlänge auf Listen durchführen. Hierzu benutzen wir die Gleichungen als\n"
+ "Ersetzungsregeln. Wenn ein Unterausdruck in der Form der linken Seite \n"
+ "einer Gleichung gefunden wird, so kann diese durch die entsprechende rechte\n"
+ "Seite ersetzt werden. Man spricht bei so einem Ersetzungsschritt von einem\n"
+ "Reduktionsschritt.\n"
+ "\n"
+ "\n"
+ "Wir errechnen in diesem Beispiel die Länge einer Liste, indem wir die obigen\n"
+ "Gleichungen zum Reduzieren auf die Liste anwenden:\n"
+ "\n"
+ "\n"
+ "length(Cons(a,Cons(b,Cons(c,Empty())))) \n"
+ "1+length(Cons(b,Cons(c,Empty())))\n"
+ " \n"
+ "1+(1+length(Cons(c,Empty())))\n"
+ " \n"
+ "1+(1+(1+length(Empty())))\n"
+ " \n"
+ "1+(1+(1+0))\n"
+ " \n"
+ "1+(1+1)\n"
+ " \n"
+ "1+2 \n"
+ "3\n"
+ "\n"
+ " \n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir können mit einfachen Gleichungen spezifizieren, was wir unter dem letzten\n"
+ "Element einer Liste verstehen.\n"
+ "\n"
+ "\n"
+ "last(Cons(x,Empty()))) x \n"
+ "last(Cons(x,xs)))last(xs)\n"
+ "\n"
+ "\n"
+ "Auch die Funktion last können wir von Hand auf einer Beispielliste\n"
+ "einmal per Reduktion ausprobieren:\n"
+ "\n"
+ "last(Cons(a,Cons(b,Cons(c,Empty())))) \n"
+ "last(Cons(b,Cons(c,Empty())))\n"
+ " \n"
+ "last(Cons(c,Empty()))\n"
+ " \n"
+ "c\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die folgenden Gleichungen spezifizieren, wie zwei Listen aneinandergehängt\n"
+ "werden: \n"
+ "\n"
+ "\n"
+ "concat(Empty(),ys)ys \n"
+ "concat(Cons(x,xs),ys)Cons(x,concat(xs,ys))\n"
+ "\n"
+ "\n"
+ "Auch diese Funktion läßt sich beispielhaft mit der Reduktion einmal\n"
+ "durchrechnen: \n"
+ "\n"
+ "\n"
+ "concat(Cons(i,Cons(j,Empty())),Cons(a,Cons(b,Cons(c,Empty()))))\n"
+ " \n"
+ "Cons(i,concat(Cons(j,Empty()),Cons(a,Cons(b,Cons(c,Empty())))))\n"
+ " \n"
+ "Cons(i,Cons(j,concat(Empty(),Cons(a,Cons(b,Cons(c,Empty()))))))\n"
+ " \n"
+ "Cons(i,Cons(j,Cons(a,Cons(b,Cons(c,Empty())))))\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Listen lassen sich auch sehr schön graphisch visualisieren. Hierzu wird jede\n"
+ "Liste durch eine Schachtel mit zwei Feldern dargestellt. Von diesen beiden\n"
+ "Feldern gehen Pfeile aus. Der erste Pfeil zeigt auf das erste Element der\n"
+ "Liste, dem head, der zweite Pfeil zeigt auf die Schachtel, die für\n"
+ "den Restliste steht dem tail. Wenn eine Liste leer ist, so gehen\n"
+ "keine Pfeile von der Schachtel aus, die sie repräsentiert. \n"
+ "\n"
+ "Die Liste Cons(a,Cons(b,Cons(c,Empty()))) hat somit die\n"
+ "Schachtel- und Zeiger- Darstellung aus Abbildung .\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "In der Schachtel- und Zeiger-Darstellung läßt sich sehr gut nachverfolgen, wie\n"
+ "bestimmte Algorithmen auf Listen dynamisch arbeiten.\n"
+ "Wir können die schrittweise Reduktion der Methode concat in der \n"
+ "Schachtel- und Zeiger-Darstellung gut nachvollziehen:\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "Abbildung zeigt die Ausgangssituation. Zwei Listen sind\n"
+ "dargestellt. Von einer Schachtel, die wir als die Schachtel der \n"
+ "Funktionsanwendung\n"
+ "von concat markiert haben, gehen zwei Zeiger aus. Der erste auf das\n"
+ "erste Argument, der zweite auf das zweite Argument der Funktionsanwendung.\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "Abbildung zeigt die Situation, nachdem die \n"
+ "Funktion concat einmal reduziert wurde. Ein neuer Listenknoten wurde\n"
+ "erzeugt. Dieser zeigt auf das erste Element der ursprünglich \n"
+ "ersten Argumentliste. Der zweite zeigt auf den rekursiven Aufruf der \n"
+ "Funktion concat, diesmal mit der Schwanzliste des ursprünglich ersten Arguments.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "Abbildung zeigt die Situation nach dem zweiten\n"
+ "Reduktionsschritt. Ein weiterer neuer Listenknoten ist entstanden und ein\n"
+ "neuer Knoten für den rekursiven Aufruf ist entstanden.\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "Abbildung zeigt die endgültige Situation. Der letzte\n"
+ "rekursive Aufruf von concat hatte als erstes Argument eine leere\n"
+ "Liste. Deshalb wurde kein neuer Listenknoten erzeugt, sondern lediglich der\n"
+ "Knoten für die Funktionsanwendung gelöscht. Man beachte, daß die beiden\n"
+ "ursprünglichen Listen noch vollständig erhalten sind. Sie wurden nicht\n"
+ "gelöscht. Die erste Argumentliste wurde quasi kopiert. Die zweite\n"
+ "Argumentliste teilen sich gewisser Maßen die neue Ergebnisliste der\n"
+ "Funktionsanwendung und die zweite ursprüngliche Argumentliste.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es ist nun endlich an der Zeit die Spezifikation der Liste auch in C-Code zu\n"
+ "gießen. Tatsächlich ist diese Recht einfach und die Gleichungen lassen sich\n"
+ "ziemlich eins-zu-eins in entsprechende Zeile C-Code umsetzen.\n"
+ "\n"
+ "\n"
+ "Wir werden zwei Implementierungen vornehmen. Zunächst werden wir nur Listen\n"
+ "implementieren, in denen als Elemente int-Zahlen gespeichert werden\n"
+ "können. Anschließend werden wir daraus eine generische Implementierung\n"
+ "ableiten, in der beliebige Elementtypen gespeichert werden können.\n"
+ "\n"
+ "Beginnen wir damit, in der Kopfdatei zu definieren, als welchen Datentypen\n"
+ "unsere liste gespeichert werden sollen.\n"
+ "\n"
+ "Zunächst definieren wir uns einen Typ für Wahrheitswerte: \n"
+ "\n"
+ "\n"
+ "Für Listen wird eine Struktur definiert. Nach unserer Spezifikation muß eine\n"
+ "Liste zwei Bestandteile speichern können. \n"
+ "\n"
+ "Zusätzlich sehen wir noch ein Attribut vor, das kennzeichnet, ob die Liste\n"
+ "eine leere Liste ist.\n"
+ "\n"
+ "\n"
+ "Um ein wenig lesbarer über Listen sprechen zu können, führen wir ein\n"
+ "Typsynonym für die Struktur ein. Damit umgehen wir, dass wir \n"
+ "immer das Wort struct vor dem Typnamen schreiben müssen:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Selektoren der Listen haben wir bereits durch die Felder der \n"
+ "Struktur Liste ausgedrückt. Ebenso dient das \n"
+ "Feld isEmpty als Test.\n"
+ "\n"
+ "Wir müssen nach unserer Spezifikation\n"
+ "noch zwei Konstruktorfunktionen vorsehen. Hierzu definieren wir die \n"
+ "Funktionen cons und nil, wobei nil für die\n"
+ "Konstruktion leerer Liste stehen soll.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir hantieren eifrig mit dynamischen daten. Daher ist es notwendig eine\n"
+ "Funktion zum Löschen der Listen aus dem Speicher vorzusehen. Diese tauchte in\n"
+ "der Spezifikation nicht auf. Sie hat nichts mit der Funktionalität von Listen\n"
+ "zu tun, sondern ist eine technisches Funktion für die explizite\n"
+ "Speicherfreigabe. \n"
+ "\n"
+ "\n"
+ "Soweit die Kopfdatei.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Zur Implementierung der Kopfdatei sind drei Funktionen umzusetzen. zunächst\n"
+ "die beiden Konstruktoren. Beide müssen zunächst Speicher allokieren, in dem\n"
+ "das neue Listenobjekt gespeichert werden kann. Daher sei zunächst eine eigene\n"
+ "kleine Funktion definiert, die diesen Speicherplatz allokiert:\n"
+ " \n"
+ "\n"
+ "#include \n"
+ "\n"
+ "List newList(){return (List)malloc(sizeof (struct Liste));}]]>\n"
+ "\n"
+ "Im Konstruktor für leere Listen ist ein neues Listenobjekt zu erzeugen, und\n"
+ "dann darin zu vermerken, dass es sich um eine leere Liste handelt: \n"
+ "\n"
+ "tail=NULL;\n"
+ " return result;\n"
+ "};]]>\n"
+ "\n"
+ "Der Konstruktor cons hat zwei Parameter, die in den entsprechenden\n"
+ "Feldern des neu erzeugten Listenobjekts abzuspeichern sind:\n"
+ "\n"
+ "head=x;\n"
+ " result->tail=xs;\n"
+ " return result;\n"
+ "};]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "tail==NULL;\n"
+ "};]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Verbleibt nur noch das Löschen einer Liste. Hierzu ist nicht nur das aktuelle\n"
+ "Listenobjekt zu löschen, sondern, sofern es einen tail gibt, also für\n"
+ "nichtleere Liste, ist zunächst der tail im Speicher wieder\n"
+ "freizugeben. Erst anschließend kann das Listenobjekt selbst im Speicher wieder\n"
+ "freigegeben werden. Es ergibt sich folgende rekursive Funktion.\n"
+ "\n"
+ "tail);\n"
+ " free(xs);\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "Zeit, ein paar erste Tests mit unserer listenimplementierung\n"
+ "durchzuführen. Hierzu sei eine Liste aus vier Zahlen erzeugt und drei dieser\n"
+ "Zahlen aus der Liste wieder zugegriffen:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " List xs=cons(1,cons(2,cons(3,cons(4,nil()))));\n"
+ " printf(\"Das erste Element: %i\n\",xs->head);\n"
+ " printf(\"Das zweite Element: %i\n\",xs->tail->head);\n"
+ " printf(\"Das dritte Element: %i\n\",xs->tail->tail->head);\n"
+ " delete(xs);\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "Nun können wir Algorithmen für Listen implementieren. Es sollen vorerst sechs\n"
+ "Algorithmen implementiert werden, darunter die beiden Funktionen, die wir im\n"
+ "vorangegangenen Abschnitt bereits implementiert haben: der Algorithmus zur\n"
+ "Berechnung der Länge einer Liste und der zum Aneinanderhängen zweier \n"
+ "ListeDie in der Spezifikation concat hieß, hier aber unter\n"
+ "den Namen append implementiert ist..\n"
+ "\n"
+ "Hier nun die Kopfdatei, in der die Signaturen der sechs zu implementierenden\n"
+ "Algorithmen definiert sind: \n"
+ "\n"
+ "\n"
+ "\n"
+ "Beginnen wir mit der Implementierung der Längenfunktion. Sie war durch zwei\n"
+ "Gleichungen spezifiziert. Leere Listen haben die Länge 0, nichtleere Listen\n"
+ "sind um eins Länger als der Listentail. Dieses mündet direkt in folgende\n"
+ "Implementierung: \n"
+ "\n"
+ "tail);\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Die Implementierung bedient sich der Rekursion. Da unsere Listen eine rekursiv\n"
+ "definierte Datenstruktur sind, lassen sich die meisten Algorithmen sehr schön\n"
+ "rekursiv implementieren. Allerdings sind iterative Lösungen zumindest leichter\n"
+ "zu effizienten Code kompilierbar. \n"
+ "\n"
+ "tail;\n"
+ " }\n"
+ " return result;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "Da wir bisher nur Zahlen als Listenelemente Speichern können wir diese\n"
+ "natürlich auch auch aufsummieren. Auch die Summenfunktion läßt sich in zwei\n"
+ "Gleichungen spezifizieren: \n"
+ "\n"
+ "\n"
+ "sum(Empty())0 \n"
+ "sum(Cons(x,xs))x+sum(xs)\n"
+ "\n"
+ "\n"
+ "Diese Gleichungen münden wieder direkt in die entsprechende Implementierung,\n"
+ "in der für jede der beiden Gleichungen genau eine Zeile benötigt wird:\n"
+ "\n"
+ "head+length(xs->tail);\n"
+ "}]]>\n"
+ "\n"
+ "Oder aber wir können die Spezifikation auch iterativ implementieren. \n"
+ "\n"
+ "head;\n"
+ " xs=xs->tail;\n"
+ " }\n"
+ " return result;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Wenn die Summe der einzelnen Elemente bekannt ist und die Länge der Liste,\n"
+ "dann läßt sich aus beiden direkt der Durchschnittswert der Elemente\n"
+ "berechnen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Auch die Funktion zur Selektion des letzten Elements einer Liste haben wir\n"
+ "bereits in zwei Gleichungen spezifiziert. Auch diese zwei Gleichungen lassen\n"
+ "sich direkt in entsprechenden Code umsetzen. Allerdings fügen wir als dritten\n"
+ "Fall hinzu, dass für leere Listen die Zahl 0 als letztes Element zurcükgegeben\n"
+ "wird. \n"
+ "\n"
+ "tail)) return xs->head;\n"
+ " return last(xs->tail);\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Eine naheliegende Funktion für Listen, ist ihre Reihenfolge umzudrehen.\n"
+ "Hierzu wird das erste Element zum letzen gemacht. Auch diese Funktion läßt\n"
+ "sich in zwei Gleichungen spezifizieren. In der Spezifikation wird die bereits\n"
+ "spezifizierte Funktion concat.\n"
+ "\n"
+ "\n"
+ "reverse(Empty())Empty() \n"
+ "reverse(Cons(x,xs))concat(reverse(xs),Cons(x,Empty()))\n"
+ "\n"
+ "\n"
+ "Wenn wir einmal davon ausgehen, dass die Funktion concat unter\n"
+ "den Namen append weiter unten implementiert wird, so lassen sich auch\n"
+ "diese Gleichungen direkt in zwei Zeilen umsetzen:\n"
+ "\n"
+ "tail),cons(xs->head,nil()));\n"
+ "}]]>\n"
+ "\n"
+ "Diese Implementierung ist allerdings hoffnungslos ineffizient. Im jeden\n"
+ "rekursiven Aufruf wirds die Funktion append aufgerufen, die\n"
+ "ihrerseits wieder rekursiv ist. Damit haben wir eine ineinander \n"
+ "geschachtelte Rekursion. Der Aufwand hierfür wird quadratisch.\n"
+ "\n"
+ "Die Funktion reverse läßt sich zum vergleich recht einfach und\n"
+ "effizient iterative implementieren. Es wird eine Ergebnisliste in einer \n"
+ "Schleife stückweise zusammengesetzt, indem immer das erste Element der\n"
+ "durchlaufenden Liste vorn an die Ergebnisliste angehängt wird.\n"
+ "\n"
+ "head,result);\n"
+ " xs=xs->tail;\n"
+ " }\n"
+ " return result;\n"
+ "}]]>\n"
+ "\n"
+ "Diese Implementierung ist wesentlich effizienter. Sie durchläuft genau einmal\n"
+ "die Liste, die umzudrehen ist. Nur ist streng genommen noch formal zu\n"
+ "beweisen, dass die Funktion reverse das gleiche Ergebnis hat, wie die\n"
+ "Funktion reverseRecursive. Ein solcher Beweis ist allerdings formal\n"
+ "sehr schwierig. In der Praxis beschränkt man sich daher zumeist darauf, sich\n"
+ "durch ein paar Testbeispiele zu versichern, dass die Implementierung das\n"
+ "gewünschte leistet. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Als nächstes sei eine Funktion implementiert, die eigentlich keine wirkliche\n"
+ "neue Berechnung vornimmt, sondern eine eins-zu-eins Kopie einer Liste \n"
+ "erstellt. Hierzu wird für eine leere Liste eine neue nil-Liste\n"
+ "erzeugt und für nichtleere Liste eine neue cons-Liste.\n"
+ "\n"
+ "head,copy(xs->tail));\n"
+ "}]]>\n"
+ "\n"
+ "Nun endlich soll die Implementierung der Funktion folgen, \n"
+ "die aus zwei Listen eine neue Liste erzeugt. Die neue Liste besteht erst aus\n"
+ "den Elementen des ersten Parameters, dann aus den Elementen des zweiten\n"
+ "Parameters: \n"
+ "\n"
+ "head,append(xs->tail,ys));\n"
+ "}]]>\n"
+ "\n"
+ "Im Falle einer leeren Liste als ersten Parameter begnügen wir uns nicht damit,\n"
+ "die Liste des zweiten Parameters zurückzugeben, sondern erzeugen eine Kopie\n"
+ "von diesem. Die Entscheidung hierfür liegt in der Tatsache, dass es sonst sehr\n"
+ "schwer wird darüber Buch zu führen, welche Listen noch Bestandteil von\n"
+ "Teillisten sind, und welche im Speicher nach Gebrauch wieder frei gegeben\n"
+ "werden können. \n"
+ "\n"
+ "\n"
+ "Zu guter letzt soll noch eine Funktion höherer Ordnung folgen. Die\n"
+ "Funktion map soll eine neue liste erzeugen, in dem auf jedes\n"
+ "Listenelement eine Funktion übergeben wird. Auch die \n"
+ "Funktion map ist schnell durch zwei Gleichungen spezifiziert:\n"
+ "\n"
+ "\n"
+ "map(f,Empty())Empty() \n"
+ "map(f,Cons(x,xs))Cons(f(x),map(f,xs))\n"
+ "\n"
+ "\n"
+ "Und diese zwei Gleichungen lassen sich wieder direkt in entsprechenden Coe\n"
+ "umsetzen: \n"
+ "\n"
+ "head),map(f,xs->tail));\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "Nun sollen die Funktionen endlich auch einmal ausgetestet werden:\n"
+ "\n"
+ "\n"
+ "\n"
+ "int addFive(int x){return x+5;}\n"
+ "\n"
+ "int main(){\n"
+ " List xs=cons(1,cons(2,cons(3,cons(4,nil()))));\n"
+ " printf(\"Das letzte Element von xs ist: %i\n\",last(xs));\n"
+ " printf(\"Die Laenge von xs ist: %i\n\",length(xs));\n"
+ " printf(\"Die Summe von xs ist: %i\n\",sum(xs));\n"
+ " printf(\"Der Durchschnitt von xs ist: %f\n\",avg(xs));\n"
+ "\n"
+ " List xsxs=append(xs,xs);\n"
+ " printf(\"Die Laenge von xsxs ist: %i\n\",length(xsxs));\n"
+ " printf(\"Die Summe von xsxs ist: %i\n\",sum(xsxs));\n"
+ " printf(\"Der Durchschnitt von xsxs ist: %f\n\",avg(xsxs));\n"
+ "\n"
+ " delete(xsxs);\n"
+ "\n"
+ " List sx=reverse(xs);\n"
+ " printf(\"Das erste Element von sx: %i\n\",sx->head);\n"
+ " delete(sx);\n"
+ "\n"
+ " List xs5=map(addFive,xs);\n"
+ " printf(\"Die Summe von xs5 ist: %i\n\",sum(xs5));\n"
+ "\n"
+ " delete(xs5);\n"
+ " delete(xs);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Listen im letzen Abschnitt haben als Listenelemente ganze Zahlen\n"
+ "enthalten. Wollen wir andere Daten in Listen speichern, so wird eine neue\n"
+ "Listenimplementierung notwendig. Das ist etwas schade, denn die meisten\n"
+ "Algorithmen für listen sind unabhängig von den Elementtypen der Listen. Zur\n"
+ "Berechnung der Länge einer Liste, ist ziemlich egal, was für Elemente in den\n"
+ "einzelnen Listenzellen im Feld head gespeichert sind. Es wäre schön,\n"
+ "wenn wir eine Listenstruktur implementieren könnten, die in der Lage ist\n"
+ "unterschiedlichste Daten als Elemente anzuspeichern. Hierzu haben wir schon\n"
+ "früher einen Trick kennengelernt: die Void-Zeiger. Statt jetzt eine Liste von\n"
+ "ganzen Zahlen zu definieren, können wir eine Liste von Void-Zeigern\n"
+ "definieren. Ein Void-Zeiger kann dann auf beliebige Arten von daten zeigen.\n"
+ "\n"
+ "\n"
+ "Wir können die Headerdatei für unsere Listen von ganzen Zahlen weitgehendst\n"
+ "übernehmen, indem wir einfach überall wo der Typ int eingesetzt war,\n"
+ "den Typ void* schreiben. Da wir den Typ void* benutzen\n"
+ "wollen um beliebige Objekte darin speichern zu können, führen wir wie bereits\n"
+ "früher dazu das Typsynonym Object ein.\n"
+ "\n"
+ "Damit erhalten wir die folgende Header-Datei:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es stellt sich heraus, dass wir die Implementierung der Listen von ganzen\n"
+ "Zahlen fast wörtlich übernehmen können, um unsere allgemeinere\n"
+ "Listenimplementierung zu bekommen:\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "PList nil(){\n"
+ " PList result=malloc (sizeof (struct PListe));\n"
+ " result->tail=NULL;\n"
+ " result->head=NULL;\n"
+ " return result;\n"
+ "};\n"
+ "\n"
+ "PList cons(Object x, PList xs){\n"
+ " PList result=malloc (sizeof (struct PListe));\n"
+ " result->head=x;\n"
+ " result->tail=xs;\n"
+ " return result;\n"
+ "};\n"
+ "\n"
+ "bool isEmpty(PList xs){\n"
+ " return xs->tail==NULL;\n"
+ "};\n"
+ "\n"
+ "void delete(PList xs){\n"
+ " if (!isEmpty(xs)) delete(xs->tail);\n"
+ " free(xs);\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "Bevor wir jetzt die einzelnen Algorithmen für Listen implementieren, soll ein\n"
+ "kleiner Test uns davon, dass wir mit ihr tatsächlich in der Lage sind,\n"
+ "beliebige Objekttypen zu speichern.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es sollen zunächst Objekte eines Punkttypens gespeichert\n"
+ "werden. Hierzu sei die entsprechende Struktur zur Darstellung von\n"
+ "Punkten im zweidimensionalen Raum definiert. \n"
+ "Mit einer kleinen Hilfsfunktion sollen solche Punktobjekte auf der\n"
+ "Kummandozeile ausgegeben werden:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "void printPunkt(Punkt* p){\n"
+ " printf(\"(%i,%i)\",p->x,p->y);\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "In der Testfunktion legen wir ein paar Punkte an, und bauen aus diesen eine Liste.\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " Punkt p1 = {0,0};\n"
+ " Punkt p2 = {0,2};\n"
+ " Punkt p3 = {2,2};\n"
+ " Punkt p4 = {2,0};\n"
+ "\n"
+ " PList polygon=cons(&p1,cons(&p2,cons(&p3,cons(&p4,nil()))));]]>\n"
+ "\n"
+ "In gewohnter Weise lässt sich auf diese Liste \n"
+ "mit head und tail zugreifen:\n"
+ "\n"
+ "tail->tail->head);\n"
+ "\n"
+ " delete(polygon);\n"
+ "]]>\n"
+ "\n"
+ "Nun wollen wir die\n"
+ "Listenimplementierung benutzen, um auch weiterhin ganze Zahlen zu\n"
+ "speichern. Jetzt allerdings können wir nur Zeiger auf ganze Zahlen\n"
+ "speichern. \n"
+ "\n"
+ "\n"
+ "head);\n"
+ " printf(\"Das zweite Element: %i\n\",*(int*)xs->tail->head);\n"
+ " printf(\"Das dritte Element: %i\n\",*(int*)xs->tail->tail->head);\n"
+ " delete(xs);\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "Damit ließen sich also mit einer Listenimplementierung Listen ganz\n"
+ "unterschiedlichen Inhalts erzeugen.\n"
+ "\n"
+ "\n"
+ "Nun wollen wir auch die unterschiedlichen Algorithmen auf Listen implementieren:\n"
+ "\n"
+ "\n"
+ "\n"
+ "Tatsächlich ist für die meisten dieser Funktionen gegenüber der bisherigen\n"
+ "Implementierung nichts zu ändern. \n"
+ "\n"
+ "tail);\n"
+ "}\n"
+ "\n"
+ "PList copy(PList xs){\n"
+ " if (isEmpty(xs)) return nil();\n"
+ " return cons(xs->head,copy(xs->tail));\n"
+ "}\n"
+ "\n"
+ "PList append(PList xs,PList ys){\n"
+ " if (isEmpty(xs)) return copy(ys);\n"
+ " return cons(xs->head,append(xs->tail,ys));\n"
+ "}\n"
+ "\n"
+ "PList reverse(PList xs){\n"
+ " PList result=nil();\n"
+ " while (!isEmpty(xs)){\n"
+ " result=cons(xs->head,result);\n"
+ " xs=xs->tail;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "PList map(Object f (Object),PList xs){\n"
+ " if (isEmpty(xs)) return nil();\n"
+ " return cons(f(xs->head),map(f,xs->tail));\n"
+ "}\n"
+ "\n"
+ "void forEach(void f (Object),PList xs){\n"
+ " for (;!isEmpty(xs);xs=xs->tail) f(xs->head);\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "Wir bedienen uns den Punkten im zweidimensionalen Raum, um ein paar Tests mit\n"
+ "diesen Funktionen durchzuführen\n"
+ "\n"
+ "\n"
+ "\n"
+ "void move(Punkt* this){\n"
+ " this->x=2*this->x;\n"
+ " this->y=2*this->y;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " Punkt p1 = {0,0};\n"
+ " Punkt p2 = {0,2};\n"
+ " Punkt p3 = {2,2};\n"
+ " Punkt p4 = {2,0};\n"
+ "\n"
+ " PList polygon=cons(&p1,cons(&p2,cons(&p3,cons(&p4,nil()))));\n"
+ "\n"
+ " printf(\"length(polygon)=$i\n\",length(polygon));\n"
+ " printf(\"polygon: [\"); \n"
+ " forEach(printPunkt,polygon);\n"
+ " printf(\"]\n\");\n"
+ "\n"
+ " PList pol2 = append(polygon,polygon);\n"
+ " printf(\"append(polygon,polygon): [\"); \n"
+ " forEach(printPunkt,pol2);\n"
+ " printf(\"]\n\");\n"
+ "\n"
+ " PList pol3 = reverse(pol2);\n"
+ " printf(\"reverse(pol2): [\"); \n"
+ " forEach(printPunkt,pol3);\n"
+ " printf(\"]\n\");\n"
+ "\n"
+ " map(move,pol3);\n"
+ " printf(\"map(move,pol3): [\"); \n"
+ " forEach(printPunkt,pol3);\n"
+ " printf(\"]\n\");\n"
+ "\n"
+ " delete(polygon);\n"
+ " delete(pol2);\n"
+ " delete(pol3);\n"
+ "\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "]]>\n"
+ "\n"
+ "(3 Punkte) \n"
+ "Erweitern Sie das Bibliothek PList um folgende weitere Funktionen und\n"
+ "testen Sie Ihre Implementierungen an ein paar Beispielen aus:\n"
+ "\n"
+ "\n"
+ "Object last(PList this ); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "last(Cons(x,Nil()))) x \n"
+ "last(Cons(x,xs)))last(xs)\n"
+ "\n"
+ "\n"
+ "Object get(PList this,unsigned int i ); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "get(Cons(x,xs),0) x \n"
+ "get(Cons(x,xs)),i)get(xs,i-1)\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "PList take(PList this,unsigned int i); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "take(xs,0) Nil() \n"
+ "take(Nil(),i) Nil() \n"
+ "take(Cons(x,xs),i))Cons(x,take(xs,i-1))\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "PList drop(PList this,unsigned int i); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "drop(Nil(),i) Nil() \n"
+ "drop(xs,0) xs \n"
+ "drop(Cons(x,xs),i) drop(xs,i-1)\n"
+ "\n"
+ "\n"
+ "PList oddElements(PList this); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "oddElements(Nil()) Nil() \n"
+ "oddElements(Cons(x,Nil())) Cons(x,Nil()) \n"
+ "oddElements(Cons(x,Cons(y,ys))) Cons(x,oddElements(ys))\n"
+ "\n"
+ "PList filter(PList this,bool predicate(Object)); \n"
+ "Die Funktion sei spezifiziert durch folgende Gleichung:\n"
+ "\n"
+ "filter(Nil(),pred) Nil() \n"
+ "filter(Cons(x,xs),pred)\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im letzten Abschnitt haben wir als erste rekursive Datenstruktur Listen\n"
+ "ausgiebig behandelt. Exemplarisch soll in diesem Abschnitt eine weitere häufig\n"
+ "gebrauchte Datenstruktur aus der Informatik implementiert werden. Es sollen\n"
+ "Bäume realisiert werden. Dabei wollen wir uns auf eine bestimmte Art von\n"
+ "Bäumen beschränken: den Binärbäumen. Allgemein sind Bäume so aufgebaut, dass\n"
+ "ein Baumknoten Teilbäume als Kinder hat. Bei Binärbäumen hat jeder Baumknoten\n"
+ "maximal 2 Kinder: einen linken und einen rechten Teilbaum als Kind. \n"
+ "\n"
+ "Die Knoten eines Baumes sollen mit Daten markiert sein. In unserem Fall wollen\n"
+ "wir uns auf Strings als Knotenmarkierungen beschränken. Mit diesen Vorgaben\n"
+ "lassen sich Bäume kurz und knapp in C umsetzen:\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "#include \n"
+ "\n"
+ "struct B {\n"
+ " char* name;\n"
+ " struct B* links;\n"
+ " struct B* rechts;\n"
+ "};\n"
+ "\n"
+ "typedef struct B* Baum;]]>\n"
+ "\n"
+ "Ein genauer Blick auf diese Datenstruktur zeigt, dass sie in \n"
+ "ihrer Art unserer Listenstruktur sehr ähnelt. Das Feld, dass hier den \n"
+ "Namen name trägt, entspricht den Feld head der Listen,\n"
+ "das Feld mit dem Name links entspricht dem \n"
+ "Feld tail unserer Listen. Zusätzlich gibt es ein weiteres Feld, das\n"
+ "Feld rechts, das man quasi als zweiten tail verstehen\n"
+ "kann. Im Grunde genommen waren Listen auch eine Art von Bäumen, man könnte sie\n"
+ "als Unärbäume bezeichnen, weil jeder Listenknoten maximal ein Kind hat, \n"
+ "seinen tail.\n"
+ "\n"
+ "Als nächstes soll eine Konstruktorfunktion geschrieben werden, die die\n"
+ "einfachste Form binärer Bäume konstruiert, nämlich Blätter. Blätter sind\n"
+ "Bäume, die kein Kind haben, was in unserer Implementierung bedeutet, dass\n"
+ "rechtes und linkes Kind beide mit NULL belegt werden.\n"
+ "\n"
+ "name=n;\n"
+ " this->links=NULL;\n"
+ " this->rechts=NULL;\n"
+ " return this;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Binärbäume können dazu genutzt werden, um Objekte effizient gemäß\n"
+ "einer Ordnung zu speichern und effizient nach ihnen unter Benutzung\n"
+ "dieser Ordnung wieder zu suchen. \n"
+ "Die Knotenmarkierungen unserer Bäume sind Strings, für die sich die\n"
+ "lexikographische Ordnung anbietet.\n"
+ "\n"
+ "Binäre Bäume heißen binäre Suchbäume, wenn für jeden\n"
+ "Baumknoten gilt:\n"
+ "\n"
+ "die Knotenmarkierung seines linken Teilbaums ist kleiner oder\n"
+ "gleich als die eigene Knotenmarkierung.\n"
+ "die Knotenmarkierung des rechten Teilbaums ist größer als die\n"
+ "eigene Knotenmarkierung.\n"
+ "\n"
+ "\n"
+ "Um einen binären Suchbaum zu erhalten, müssen \n"
+ "Bäume mit mehr als einen Knoten kontrolliert\n"
+ "konstruiert werden. Hierzu ist es sinnvoll eine Funktion anzubieten, die eine\n"
+ "neue Markierung in einen bestehenden Baum einfügt. Diese Einfügefunktion muß\n"
+ "sicherstellen, dass, wenn der ursprüngliche Baum ein binärer Suhbaum war, dann\n"
+ "der geänderte Baum auch wieder ein binärer Suchbaum ist. \n"
+ "\n"
+ "Algorithmisch kann das wie folgt erreicht werden:\n"
+ "\n"
+ "Gegeben seien ein Baum t und ein Element o.\n"
+ "\n"
+ "Ist o kleiner oder gleich der Markierung von t, so \n"
+ "muß o ins linke Kind von t eingefügt werden.\n"
+ "Ansonsten muß o ins rechte Kind von t eingefügt \n"
+ "werden.\n"
+ "\n"
+ "Hat t kein linkes (bzw. rechtes) Kind, in das eingefügt weden kann,\n"
+ "so ist eine neues Blatt mit o zu erzeugen, welches das \n"
+ "linke (bzw. rechte) Kind von t wird.\n"
+ "\n"
+ "Es bietet sich an, diesen Algorithmus in zwei C Funktionen umzusetzen, die\n"
+ "sich gegenseitig verschränkt rekursiv aufrufen. Eine \n"
+ "Funktion insert, die ein neues Element in einen Baum einfügt, und\n"
+ "eine Funktion insertOrNewChild, die eine Referenz auf ein Kind\n"
+ "hat. Sollte es dieses Kind nicht geben, die Referenz also \n"
+ "auf NULL zeigt, so ist ein neues Blatt auf dieser Referenz\n"
+ "einzutragen, ansonsten ist das Element in dem referenzierten Baum einzufügen.\n"
+ "\n"
+ "Zunächst die zwei Sinaturen der benötigten Funktionen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Umsetzung beider Funktionen ist erschreckend einfach. Beginnen wir mit der\n"
+ "eigentlichen Einfügefunktion insert. Es wird mit der \n"
+ "Standardfunktion strcmp der einzufügende String mit der\n"
+ "Wurzelmarkierung verglichen. Je nach dem ist links oder rechts das Element \n"
+ "einzufügen.\n"
+ "\n"
+ "name)>0) insertOrNewChild(&this->rechts,n);\n"
+ " else insertOrNewChild(&this->links,n);\n"
+ "}]]> \n"
+ "\n"
+ "Ebenso nur mit zwei Zeilen Funktionsrumpf lässt sich die zweite Funktion\n"
+ "umsetzen. Hier wird unterschieden, ob es noch einen Baum gibt, in dem\n"
+ "einzufügen ist, oder ob ein Blatt erzeugt und eingehängt werden soll:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Das war es schon. Zum Testen brauchen wir Funktionen, die einen Baum ausgeben\n"
+ "können, so dass man sich ein Bild von seiner Struktur machen kann. Hierzu die\n"
+ "folgende Funktion, die einen Binärbaum ausgibt.\n"
+ "\n"
+ "0;i--) printf(\" \");}\n"
+ "\n"
+ "int isLeaf(Baum this){return this->links==NULL&&this->rechts==NULL;}\n"
+ "\n"
+ "void printTree(Baum this,int indent){\n"
+ " if (this!=NULL){\n"
+ " printf(\"%s\",this->name);\n"
+ " if (!isLeaf(this)){\n"
+ " nl(indent);\n"
+ " printf(\"[\");\n"
+ " printTree(this->links,indent+1);\n"
+ " nl(indent);\n"
+ " printf(\",\");\n"
+ " printTree(this->rechts,indent+1);\n"
+ " nl(indent);\n"
+ " printf(\"]\");\n"
+ " }\n"
+ " }else\n"
+ " printf(\"NULL\");\n"
+ "}]]>\n"
+ "\n"
+ "Eine der interessanten Eigenschaften eines binären Suchbaums ist es, dass wenn\n"
+ "zunächst die Elemente des linken Kindes, dann die Wurzelmakierung und\n"
+ "schließlich die Elemente des rechten Kindes ausgegeben werden, die Elemente in\n"
+ "sortierter Reihenfolge ausgegeben werden. \n"
+ "\n"
+ "links);\n"
+ " printf(\" %s \",this->name);\n"
+ " printSorted(this->rechts);\n"
+ "}]]>\n"
+ "\n"
+ "Ein minimaler test soll uns von der Arbeitsweise obiger Funktionen überzeugen:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "das Programm erzeugt folgende kleine Ausgabe:\n"
+ "\n"
+ " bin/BinTree\n"
+ "Sven Eric\n"
+ " [Sebastian\n"
+ " [Jan\n"
+ " [Andreas\n"
+ " [NULL\n"
+ " ,Daniel\n"
+ " ]\n"
+ " ,Michael\n"
+ " [NULL\n"
+ " ,Robert\n"
+ " ]\n"
+ " ]\n"
+ " ,NULL\n"
+ " ]\n"
+ " ,Tristan\n"
+ " [Thorsten\n"
+ " [Thobias\n"
+ " ,NULL\n"
+ " ]\n"
+ " ,NULL\n"
+ " ]\n"
+ " ] Andreas Daniel Jan Michael Robert Sebastian Sven Eric Thobias Thorsten Tristan sep@pc305-3:~/fh/c/student>]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "In dieser Projektaufgabe sollen Programme geschrieben werden, die farbige\n"
+ "Bilddateien erzeugen. Als einfachstes Bildformat benutzen wir das \n"
+ "Format bmp, das Bilder als einfache Bitmapdateien speichert.\n"
+ "\n"
+ "Hierzu sei die folgende Header-Datei einer kleinen Bibliothek zum Erzeugen von\n"
+ "Bitmapdateien gegeben. Darin werden zwei einfache Strukturen definiert. Eine\n"
+ "um Farben zu speichern:\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "typedef struct {\n"
+ " unsigned char red;\n"
+ " unsigned char green;\n"
+ " unsigned char blue;\n"
+ "} Color;\n"
+ "\n"
+ "const static Color BLACK = {0,0,0};\n"
+ "const static Color WHITE = {255,255,255};\n"
+ "const static Color RED = {255,0,0};\n"
+ "const static Color GREEN = {0,255,0};\n"
+ "const static Color BLUE = {0,0,255};\n"
+ "const static Color YELLOW= {255,255,0};]]>\n"
+ "\n"
+ "Und eine für die eigentlichen Bitmapdaten. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Bibliothek sieht vier Funktionen vor:\n"
+ "\n"
+ "newBmp: zum Erzeugen eines neuen Bitmapobjektes im \n"
+ "Speicher. \n"
+ "deleteBmp: um ein Bitmapobjekt wieder aus dem Speicher zu\n"
+ "löschen.\n"
+ "writeBmpToFile: um ein \n"
+ "Bitmapobjekt in eine Datei zu schreiben.\n"
+ "setBmpPoint: um einen Punkt mit einer Farbe zu \n"
+ "beschreiben.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Es folgt die Implementierung dieser Bibliothek:\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "Bmp* newBmp(int width,int height){\n"
+ " Bmp* result = (Bmp*)malloc(sizeof(Bmp));\n"
+ " result->imagedata =(Color*)malloc(sizeof(Color)*width*height);\n"
+ " result->width=width;\n"
+ " result->height=height;\n"
+ " return result;\n"
+ "}\n"
+ "void deleteBmp(Bmp* this){\n"
+ " free(this->imagedata);\n"
+ " free(this);\n"
+ "}\n"
+ "\n"
+ "void setBmpPoint(Bmp* this,Color c,int x, int y){\n"
+ " if (!(x<0||x>=this->width||y<0||y>=this->height)){\n"
+ " this->imagedata[y*this->width+x]=c;\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "void putByte(FILE* out,unsigned char ch) {fputc(ch,out);}\n"
+ "\n"
+ "void putShort(FILE* out, unsigned short w) {\n"
+ " putByte(out, w & 0xff);\n"
+ " putByte(out, (w>>8) & 0xff);\n"
+ "}\n"
+ "void putLong(FILE* out, unsigned int l) {\n"
+ " putByte(out, l & 0xff);\n"
+ " putByte(out, (l>>8) & 0xff);\n"
+ " putByte(out, (l>>16) & 0xff);\n"
+ " putByte(out, (l>>24) & 0xff);\n"
+ "}\n"
+ "void putColor(FILE* out, Color c) {\n"
+ " putByte(out,c.blue);\n"
+ " putByte(out,c.green);\n"
+ " putByte(out,c.red);\n"
+ "}\n"
+ "int getBytesPerLines(Bmp* this){\n"
+ " long bytesPerLine = this->width * 3l; /* (for 24 bit images) */\n"
+ " bytesPerLine=bytesPerLine+(4-bytesPerLine%4)%4;\n"
+ " return bytesPerLine;\n"
+ "}\n"
+ "void writeBmpToFile(Bmp* this,char* fileName) {\n"
+ " FILE* out = fopen(fileName, \"wb\");\n"
+ " if (out == NULL){\n"
+ " printf(\"Error opening output file\n\");\n"
+ " exit(1);\n"
+ " }\n"
+ " putByte(out,'B'); // \"BM\"-ID schreiben\n"
+ " putByte(out,'M');\n"
+ "\n"
+ " int headersize = 54L;\n"
+ " int bytesPerLine = getBytesPerLines(this);\n"
+ " int filesize = headersize+bytesPerLine*this->height;\n"
+ " putLong(out,filesize);\n"
+ "\n"
+ " putShort(out,0);\n"
+ " putShort(out,0);\n"
+ " putLong(out, headersize); \n"
+ " putLong(out, 0x28L); //infoSize\n"
+ " putLong(out,this-> width);\n"
+ " putLong(out,this-> height);\n"
+ " putShort(out, 1); //biPlanes\n"
+ " putShort(out, 24); //bits\n"
+ " putLong(out,0); //(no compression)\n"
+ " putLong(out,0);\n"
+ " putLong(out,0);\n"
+ " putLong(out,0);\n"
+ " putLong(out,0);\n"
+ " putLong(out,0);\n"
+ " \n"
+ " int line, colum;\n"
+ " for (line=0;line < this->height;line++){\n"
+ " for (colum=0;colum < this->width;colum++){\n"
+ " putColor(out,this->imagedata[line*this->width+colum]);\n"
+ " }\n"
+ " int missingBytes=bytesPerLine-3*this->width;\n"
+ " for (;missingBytes>0;missingBytes--)\n"
+ " putByte(out,0);\n"
+ " }\n"
+ " fclose(out);\n"
+ "}]]>\n"
+ "\n"
+ "Es folgt eine kleine Beispielanwendung, in der eine erste Bilddatei erzeugt\n"
+ "wird. \n"
+ "\n"
+ "height;y++){\n"
+ " for (x=0;xwidth;x++){\n"
+ " Color c={0,x,0};\n"
+ " setBmpPoint(bh,c,x,y);\n"
+ " }\n"
+ " }\n"
+ " writeBmpToFile(bh,\"test.bmp\");\n"
+ " deleteBmp(bh);\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Nun sind Sie dran, Programme zu schreiben, mit denen Bilder generiert\n"
+ "werden können.\n"
+ " \n"
+ "Schreiben Sie eine kleine Bibliothek, mit deren Hilfe Sie geometrische Figuren \n"
+ "in Bitmapdateien scheiben können.\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur \n"
+ "void background(Bmp* this,Color c) \n"
+ "Sie soll das ganze Bild mit einer Hintergrundfarbe ausfüllen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur\n"
+ " void fillRect(Bmp* this,Color c,int x,int y,int w,int h) \n"
+ "Sie soll ein farbiges Rechteck mit der linken unteren \n"
+ "Ecke (x,y), der Weite w und der Höhe h in die\n"
+ "Bitmapdatei zeichnen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur\n"
+ " void drawLine(Bmp* this,Color c,int fromX,int fromY,int toX,int toY) \n"
+ "Sie soll eine farbiges Linie mit dem \n"
+ "Startpunkt (fromX,fromY) und dem Endpunkt (toX,toY) in \n"
+ "die Bitmapdatei zeichnen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur\n"
+ " void drawRect(Bmp* this,Color c,int x,int y,int w,int h) \n"
+ "Sie soll die farbige Umrandung eines Rechtecks mit der linken unteren \n"
+ "Ecke (x,y), der Weite w und der Höhe h in die\n"
+ "Bitmapdatei zeichnen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur\n"
+ " void drawCircle(Bmp* this,Color c,int x,int y,int radius) \n"
+ "Sie soll die farbige Umrandung eines Kreises mit dem \n"
+ "Mittelpunkt (x,y) und dem Radius radius in die\n"
+ "Bitmapdatei zeichnen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie eine Prozedur\n"
+ " void fillCircle(Bmp* this,Color c,int x,int y,int radius) \n"
+ "Sie soll einen farbigen Kreises mit dem \n"
+ "Mittelpunkt (x,y) und dem Radius radius in die\n"
+ "Bitmapdatei zeichnen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Schreiben Sie ein Programm, das Ihre Bibliothek nutzt und verschiedene\n"
+ "interessante Bilder erzeugt.\n"
+ "\n"
+ "\n"
+ "\n";
String skript8 =
"\n"
+ "\n"
+ "\n"
+ "\n"
+ "#ifndef M_PI\n"
+ "#define M_PI 3.14159265358979323846 \n"
+ "#endif\n"
+ "\n"
+ "void background(Bmp* this,Color c){\n"
+ " for (int y=0;yheight;y++){\n"
+ " for (int x=0;xwidth;x++)\n"
+ " setBmpPoint(this,c,x,y);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "void fillRect(Bmp* this,Color c,int x,int y,int w,int h){ \n"
+ " for (int i=y;iyDiv){\n"
+ " if (fromX>toX){\n"
+ " swapValues(&fromY,&toY);\n"
+ " swapValues(&fromX,&toX);\n"
+ " }\n"
+ " double steigung= ((double)toY-fromY)/(toX-fromX);\n"
+ " for(int x=fromX;x<=toX;x++){\n"
+ " setBmpPoint(this,c,x,fromY+(int)(x-fromX)*steigung);\n"
+ " }\n"
+ " }else{\n"
+ " if (fromY>toY){\n"
+ " swapValues(&fromY,&toY);\n"
+ " swapValues(&fromX,&toX);\n"
+ " }\n"
+ " double steigung= ((double)toX-fromX)/(toY-fromY);\n"
+ " for(int y=fromY;y<=toY;y++){\n"
+ " setBmpPoint(this,c,fromX+(int)(y-fromY)*steigung,y);\n"
+ " }\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "void drawRect(Bmp* this,Color c,int x,int y,int w,int h){\n"
+ " drawLine(this,c,x,y,x+w,y);\n"
+ " drawLine(this,c,x+w,y,x+w,y+h);\n"
+ " drawLine(this,c,x+w,y+h,x,y+h);\n"
+ " drawLine(this,c,x,y+h,x,y);\n"
+ "}\n"
+ "\n"
+ "void drawCircle(Bmp* this,Color c,int x,int y,int radius){\n"
+ " if (radius<0) radius= -radius;\n"
+ " const int maxAlpha=360*(1+radius/100);\n"
+ " for (int alpha=0;alpha\n"
+ "\n"
+ "\n"
+ "#include \"geo.h\"\n"
+ "\n"
+ "int main(){\n"
+ " Bmp* bmp=newBmp(200,100);\n"
+ " background(bmp, BLACK);\n"
+ " fillRect(bmp,RED,20,20,80,30);\n"
+ " fillRect(bmp,BLUE,80,40,80,50);\n"
+ " writeBmpToFile(bmp,\"b1.bmp\");\n"
+ " deleteBmp(bmp);\n"
+ " \n"
+ " bmp=newBmp(200,200);\n"
+ " background(bmp, RED);\n"
+ " drawRect(bmp,BLUE,80,40,80,50);\n"
+ " drawLine(bmp,GREEN,10,10,50,90);\n"
+ " drawLine(bmp,GREEN,10,10,50,190);\n"
+ " drawLine(bmp,GREEN,10,10,90,50);\n"
+ " drawLine(bmp,GREEN,10,10,190,50);\n"
+ " drawLine(bmp,GREEN,10,10,10,150);\n"
+ " drawLine(bmp,GREEN,10,10,150,10);\n"
+ "\n"
+ "\n"
+ " drawLine(bmp,GREEN,50,90,10,10);\n"
+ " drawLine(bmp,GREEN,50,190,10,10);\n"
+ " drawLine(bmp,GREEN,90,50,10,10);\n"
+ " drawLine(bmp,GREEN,190,50,10,10);\n"
+ " drawLine(bmp,GREEN,10,150,10,10);\n"
+ " drawLine(bmp,GREEN,150,10,10,10);\n"
+ "\n"
+ " drawCircle(bmp,BLACK,100,100,100);\n"
+ " drawCircle(bmp,BLACK,100,100,90);\n"
+ " drawCircle(bmp,BLACK,100,100,80);\n"
+ " drawCircle(bmp,BLACK,100,100,70);\n"
+ " drawCircle(bmp,BLACK,100,100,60);\n"
+ " drawCircle(bmp,BLACK,100,100,50);\n"
+ " drawCircle(bmp,BLACK,-900,-900,1500);\n"
+ "\n"
+ " writeBmpToFile(bmp,\"b2.bmp\");\n"
+ " deleteBmp(bmp);\n"
+ "\n"
+ " bmp=newBmp(200,200);\n"
+ " background(bmp, BLUE);\n"
+ " fillCircle(bmp,YELLOW,100,100,80);\n"
+ " fillCircle(bmp,GREEN,1,1,70);\n"
+ " writeBmpToFile(bmp,\"b3.bmp\");\n"
+ "\n"
+ " deleteBmp(bmp);\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "In dieser Aufgabe sollen Sie mit Polynomen rechnen. Polynome bestehen aus der\n"
+ "Summe von Monomen. Ein Monom ist der Form c*x^e, \n"
+ "wobei c der Koeffizient eine reelle Zahl ist und e der Exponent,\n"
+ "eine natürliche Zahl inklusive 0 ist.\n"
+ "\n"
+ "Definieren Sie eine Datenstruktur, mit der Sie Polynome adequat\n"
+ "darstellen können. Tipp: Betrachten Sie hierzu ein Polynom als eine Art\n"
+ "Liste von Monomen. Definieren Sie sich also zunächst ein struct, mit\n"
+ "dem Sie Monome darstellen können. Dann definieren Sie sich \n"
+ "ein struct, mit dem Sie eine Verkettung von Monomen darstellen können.\n"
+ "Schreiben Sie eine Funktion: \n"
+ "double eval(Polynomial this,double x) \n"
+ "die für einen Eingabewert das Polynom ausrechnet.\n"
+ "Schreiben Sie eine Funktion: \n"
+ "Polynomial derivation(Polynomial this); \n"
+ "die für ein Polynom die erste Ableitung erzeugt. \n"
+ "Schreiben Sie eine Prozedur: \n"
+ "void drawPolynomial(Bmp* this,Color c,Polynomial poly \n"
+ " ,int minX,int maxX, int minY);\n"
+ "Sie soll den Graphen des Polynoms in einer Bitmapdatei \n"
+ "zeichnen. minX und maxX geben den gewünschten Wertebereich\n"
+ "auf der x-Achse an. minY den y-Wert in der\n"
+ "untersten Bildzeile an. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "Polynomial newMonoPolynomial(Monomial m){\n"
+ " Polynomial this=(Polynomial)malloc(sizeof(struct PolyStruct));\n"
+ " this->mono=m;\n"
+ " this->next=NULL;\n"
+ " return this;\n"
+ "}\n"
+ "Polynomial newPolynomial(Monomial m,Polynomial p){\n"
+ " Polynomial this=(Polynomial)malloc(sizeof(struct PolyStruct));\n"
+ " this->mono=m;\n"
+ " this->next=p;\n"
+ " return this;\n"
+ "}\n"
+ "void deletePolynomial(Polynomial this){\n"
+ " if (this->next!=NULL) deletePolynomial(this->next);\n"
+ " free(this);\n"
+ "}\n"
+ "double evalMonomial(Monomial m,double x){\n"
+ " return m.coefficient*pow(x,m.exponent);\n"
+ "}\n"
+ "double eval(Polynomial this,double x){\n"
+ " double result=evalMonomial(this->mono,x);\n"
+ " if (this->next!=NULL)result=result+eval(this->next,x);\n"
+ " return result;\n"
+ "}\n"
+ "Monomial derivationMonomial(Monomial m){\n"
+ " Monomial result;\n"
+ " if (m.exponent==0){\n"
+ " result.exponent=0;\n"
+ " result.coefficient=0;\n"
+ " }else {\n"
+ " result.exponent=m.exponent-1;\n"
+ " result.coefficient=m.coefficient*m.exponent;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "Polynomial derivation(Polynomial this){\n"
+ " Monomial mono = derivationMonomial(this->mono);\n"
+ " if (this->next==NULL) return newMonoPolynomial(mono);\n"
+ " return newPolynomial(mono,derivation(this->next));\n"
+ "}\n"
+ "\n"
+ "void drawPolynomial(Bmp* this,Color c,Polynomial poly\n"
+ " ,int minX,int maxX, int minY){\n"
+ " double valuePerPixel = ((double)maxX-minX)/this->width;\n"
+ " for (int x=0;xwidth;x++){\n"
+ " int y1\n"
+ " =(int)((eval(poly,minX+valuePerPixel*x)-minY)/valuePerPixel);\n"
+ " int y2=(int)((eval(poly,minX+valuePerPixel*(x+1))-minY)\n"
+ " /valuePerPixel);\n"
+ " drawLine(this,c,x,y1,x+1,y2);\n"
+ " } \n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "In dieser Aufgabe sollen Sie die Mandelbrotmenge in einer Bitmapdatei\n"
+ "zeichnen. Hierzu brauchen Sie die Bibliothek für komplexe Zahlen aus\n"
+ "Übungsblatt Nummer 5. \n"
+ "\n"
+ "Erzeugen Sie eine Bitmapdatei mit der \n"
+ "Weite 480 und der Höhe 430. Pro Pixel soll dabei ein\n"
+ "reeller Abstand von 0.00625 dargestellt werden. \n"
+ "Die linke untere Ecke soll \n"
+ "der Punkt (x,y)=(-2,-1,35) sein. \n"
+ "Berechnen Sie für jeden \n"
+ "Punkt (x,y) nun die Schwelle der komplexen \n"
+ "Zahl x + y i mit dem Schwellwert 4. Sie dürfen die \n"
+ "Funktion schwelle aus Ihrer Bibliothek \n"
+ "für komplexe Zahlen so abändern, dass sie\n"
+ "maximal 50 als Schwelle berechnet. Ansonsten könnte es passieren, dass die\n"
+ "Funktion schwelle nicht terminiert. \n"
+ "Färben Sie nun die Punkte für unterschiedlichen Schwellwerte\n"
+ "unterschiedlich ein, so dass Sie eine interessante farbige Darstellung der\n"
+ "Mandelbrotmenge erhalten.\n"
+ "\n"
+ "height;y++)\n"
+ " for (int x=0;xwidth;x++){\n"
+ " Complex c={-2+x*0.00625,-1.35+y*0.00625};\n"
+ " unsigned int s=schwelle(c,4)%50;\n"
+ " Color col={5*s%255,s%5*30,s%5*50};\n"
+ " setBmpPoint(result,col,x,y);\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " Bmp* apfel=newApfel();\n"
+ " writeBmpToFile(apfel,\"apfel.bmp\");\n"
+ " deleteBmp(apfel);\n"
+ " return 0;\n"
+ "}\n"
+ "\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "Erzeugen Sie mehrere Bilder, in denen Sie in die in Aufgabe a)\n"
+ "dargestellte Figur hineinzoomen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Nachdem wir nun schon sehr tief in die Programmiersprache C eingetaucht sind,\n"
+ "und bereits Programme mit einer komplexen Funktionalität schreiben können, ist\n"
+ "vielleicht Zeit einmal innezuhalten und sich zu überlegen, was eine\n"
+ "Programmiersprache eigentlich genau ist, wie eine Programmiersprache definiert\n"
+ "und beschrieben wird und wie im Endeffekt ein Compiler arbeitet.\n"
+ "\n"
+ "\n"
+ "Programmiersprachen stellen formale Systeme dar. In einem formalen System\n"
+ "gilt zu unterscheiden:\n"
+ "\n"
+ "\n"
+ " die äußere Struktur, in dem Sätze des Systems\n"
+ "notiert werden, der sogenannten Syntax,\n"
+ " und der Bedeutung dieser Sätze, der\n"
+ "sogenannten Semantik. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Sprachen sind ein fundamentales Konzept nicht nur der Informatik. In\n"
+ "der Informatik begegnen uns als auffälligste Form der Sprache\n"
+ "Programmiersprachen. Es gibt gewisse Regeln, nach denen die Sätze\n"
+ "einen Sprache aus einer Menge von Wörtern geformt werden können. \n"
+ "Die Regeln nennen wir im \n"
+ "allgemeinen eine Grammatik. Ein Satz einer\n"
+ "Programmiersprache nennen wir Programm. Die Wörter sind,\n"
+ "Schlüsselwörter, Bezeichner, Konstanten und Sonderzeichen.\n"
+ " \n"
+ "Ausführlich beschäftigt sich die Vorlesung Informatik 2 mit\n"
+ "formalen Sprachen. Die praktische Umsetzung der Theorie in Form eines\n"
+ "Compilers wird in der Vorlesung Compilerbau behandelt. Wir werden in diesem Kapitel die wichtigsten\n"
+ "Grundkenntnisse hierzu betrachten, wie sie zum Handwerkszeug eines\n"
+ "jeden Informatikers gehören.\n"
+ "\n"
+ "Eine der bahnbrechenden Erfindungen des 20.Jahrhunderts\n"
+ "geht auf den Sprachwissenschaftler Noam \n"
+ "ChomskyChomsky gilt\n"
+ "als der am häufigsten zitierte Wissenschaftler \n"
+ "des 20.Jahrhunderts. Heutzutage tritt Chomsky weniger durch\n"
+ "seine wissenschaftlichen Arbeiten als vielmehr durch seinen Einsatz\n"
+ "für Menschenrechte und bedrohte Völker in Erscheinung. zurück. Er\n"
+ "präsentierte als erster ein formales Regelsystem, mit dem die\n"
+ "Grammatik einer Sprache beschrieben werden kann. Dieses Regelsystem\n"
+ "ist in seiner Idee verblüffend einfach. Es bietet Regeln an, \n"
+ "mit denen mechanisch die Sätze einer Sprache generiert werden können.\n"
+ "\n"
+ "Systematisch wurden Chomsky Ideen zum erstenmal für die Beschreibung\n"
+ "der Syntax der Programmiersprache Algol angewendet. \n"
+ "\n"
+ " \n"
+ "Eine kontextfreie Grammatik besteht aus\n"
+ "\n"
+ "einer Menge T von Wörteren,\n"
+ " den Terminalsymbole.\n"
+ "einer Menge N von Nichtterminalsymbolen.\n"
+ "ein ausgezeichnetes Startsymbol $S\\in$N.\n"
+ "einer endlichen Menge R von Regeln der Form: \n"
+ "nt::= $t_1 t_n$, \n"
+ "wobei nt$\\in$N, $t_i\\in$N$\\cup$T. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit den Regeln einer kontextfreien Grammatik werden Sätze gebildet,\n"
+ "indem ausgehend vom Startsymbol Regel angewendet werden. Bei einer\n"
+ "Regelanwendung wird ein Nichtterminalzeichen $t$ durch die Rechte Seite\n"
+ "einer Regel, die $t$ auf der linken Seite hat, ersetzt.\n"
+ "\n"
+ "Wir geben eine Grammatik an, die einfache Sätze über unser\n"
+ "Sonnensystem auf Englisch\n"
+ "bilden kann:\n"
+ "\n"
+ "T$=\\{$mars,mercury,deimos,phoebus,orbits,is,a,moon,planet$\\}$\n"
+ "N$=\\{$start,noun-phrase,verb-phrase,noun,verb,article$\\}$\n"
+ "$S=$start\n"
+ "start ::= noun-phrase verb-phrase\n"
+ "\n"
+ "noun-phrase ::= noun \n"
+ "noun-phrase ::= article noun\n"
+ "\n"
+ "verb-phrase ::= verb noun-phrase\n"
+ "\n"
+ "noun ::= planet \n"
+ "noun ::= moon \n"
+ "noun ::= mars \n"
+ "noun ::= deimos \n"
+ "noun ::= phoebus\n"
+ "\n"
+ "verb ::= orbits \n"
+ "verb ::= is\n"
+ "\n"
+ "article ::= a\n"
+ "\n"
+ "\n"
+ "Wir können mit dieser Grammatik Sätze in der folgenden Art bilden:\n"
+ "\n"
+ "start \n"
+ "noun-phrase verb-phrase \n"
+ "article noun verb-phrase \n"
+ "article noun verb noun-phrase \n"
+ " a noun verb noun-phrase \n"
+ " a moon verb noun-phrase \n"
+ " a moon orbits noun-phrase \n"
+ " a moon orbits noun \n"
+ " a moon orbits mars \n"
+ "\n"
+ "start \n"
+ "noun-phrase verb-phrase \n"
+ "noun verb-phrase \n"
+ " mercury verb-phrase \n"
+ " mercury verb noun-phrase \n"
+ " mercury is noun-phrase \n"
+ " mercury is article noun \n"
+ " mercury is a noun \n"
+ " mercury is a planet \n"
+ "\n"
+ "Mit dieser einfachen Grammatik lassen sich auch Sätze bilden,\n"
+ "die weder korrektes Englisch sind, noch eine vernünftige inhaltliche\n"
+ "Aussage machen:\n"
+ "start \n"
+ " noun-phrase verb-phrase \n"
+ "noun verb-phrase \n"
+ " planet verb-phrase \n"
+ " planet verb noun-phrase \n"
+ " planet orbits noun-phrase \n"
+ " planet orbits article noun \n"
+ " planet orbits a noun \n"
+ " planet orbits a phoebus \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Eine Grammatik beschreibt die Syntax einer Sprache im Gegensatz zur\n"
+ "Semantik, der Bedeutung, einer Sprache. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Grammatik aus dem letzen Beispiel kann nur endlich viele Sätze\n"
+ "generieren. Will man mit einer Grammatik unendlich viele Sätze\n"
+ "beschreiben, so wie eine Programmiersprache unendlich viele Programme\n"
+ "hat, so kann man sich dem Trick der Rekursion bedienen. Eine Grammatik\n"
+ "kann rekursive Regeln enthalten; das sind Regeln, in denen auf der\n"
+ "rechten Seite das Nichtterminalsymbol der linken Seite wieder\n"
+ "auftaucht. \n"
+ "Die folgende Grammatik erlaubt es arithmetische Ausdrücke zu\n"
+ "generieren: \n"
+ "\n"
+ "T$=\\{0,1,2,3,4,5,6,7,8,9,+,-,*,/\\}$\n"
+ "N$=\\{$start,expr,op,integer,digit,$\\}$\n"
+ "$S=$start\n"
+ "start ::= expr\n"
+ "\n"
+ "expr ::= integer \n"
+ "expr ::= integer op expr\n"
+ "\n"
+ "integer ::= digit\n"
+ "integer ::= digit integer\n"
+ "\n"
+ "op ::= + \n"
+ "op ::= - \n"
+ "op ::= * \n"
+ "op ::= /\n"
+ "\n"
+ "digit ::= 0 \n"
+ "digit ::= 1 \n"
+ "digit ::= 2 \n"
+ "digit ::= 3 \n"
+ "digit ::= 4 \n"
+ "digit ::= 5 \n"
+ "digit ::= 6 \n"
+ "digit ::= 7 \n"
+ "digit ::= 8 \n"
+ "digit ::= 9\n"
+ "\n"
+ "\n"
+ "Diese Grammatik hat zwei rekursive Regeln: eine für das \n"
+ "Nichtterminal expr und eines für das \n"
+ "Nichtterminal integer. \n"
+ "\n"
+ "Folgende Abeleitung generiert einen arithmetischen Ausdrucke mit dieser\n"
+ "Grammatik:\n"
+ "\n"
+ "start \n"
+ "expr \n"
+ "integer op expr \n"
+ "integer op integer op expr \n"
+ "integer op integer op integer op expr \n"
+ "integer op integer op integer op integer \n"
+ "integer + integer op integer op integer \n"
+ "integer + integer $*$ integer op integer \n"
+ "integer + integer $*$ integer $-$\n"
+ "integer \n"
+ "digit integer + integer $*$ integer $-$ integer \n"
+ " $1$ integer + integer $*$ integer\n"
+ "$-$ integer \n"
+ " $1$ digit integer + integer $*$\n"
+ "integer $-$ integer \n"
+ " $12$ integer + integer $*$\n"
+ "integer $-$ integer \n"
+ " $12$ digit + integer $*$ integer\n"
+ "$-$ integer \n"
+ " $129+$ integer $*$ integer $-$\n"
+ "integer \n"
+ " $129+$ digit $*$ integer $-$\n"
+ "integer \n"
+ " $129+4*$ integer $-$ integer \n"
+ " $129+4*$ digit integer $-$ integer \n"
+ " $129+4*5$integer $-$ integer \n"
+ " $129+4*5$digit $-$ integer \n"
+ " $129+4*53-$ integer \n"
+ " $129+4*53-$ digit integer \n"
+ " $129+4*53-8$ integer \n"
+ " $129+4*53-8$ digit \n"
+ " $129+4*53-87$ \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Erweitern Sie die obige Grammatik so, dass sie mit ihr auch\n"
+ "geklammerte arithmetische Ausdrücke ableiten können. Hierfür gibt es\n"
+ "zwei neue Terminalsymbolde: ( und ). \n"
+ "Schreiben Sie\n"
+ "eine Ableitung für den Ausdruck: 1+(2*20)+1\n"
+ "\n"
+ "\n"
+ "\n"
+ "Kontextfreie Grammatiken sind ein einfaches und dennoch mächtiges \n"
+ "Beschreibungsmittel für Sprachen. Dennoch gibt es viele Sprachen, die\n"
+ "nicht durch eine kontextfreie Grammatik beschrieben werden können. \n"
+ "\n"
+ "\n"
+ "Es gibt syntaktisch recht einfache Sprachen, die sich nicht durch eine\n"
+ "kontextfreie Grammatik beschreiben lassen. Eine sehr einfache solche\n"
+ "Sprache besteht aus drei Wörtern: T$=\\{$a,b,c$\\}$. Die\n"
+ "Sätze dieser Sprache sollen so gebildet sein, daß für eine Zahl $n$\n"
+ "eine Folge von $n$ mal dem Zeichen a, $n$ mal das Zeichen b und\n"
+ "schließlich $n$ mal das Zeichen c folgt, \n"
+ "also \n"
+ "$\\{a^nb^nc^n|n\\in I\\!\\!N\\}$. \n"
+ "\n"
+ "Die Sätze dieser Sprache lassen sich aufzählen:\n"
+ "\n"
+ "abc \n"
+ "aabbcc \n"
+ "aaabbbccc \n"
+ "aaaabbbbcccc \n"
+ "aaaaabbbbbccccc \n"
+ "aaaaaabbbbbbcccccc \n"
+ "\n"
+ "Es gibt formale Beweise, daß derartige Sprachen sich nicht mit\n"
+ "kontextfreie Grammatiken bilden lassen. Versuchen Sie einmal das\n"
+ "Unmögliche: eine Grammatik aufzustellen, die diese Sprache erzeugt.\n"
+ "\n"
+ "Eine weitere einfache Sprache, die nicht durch eine kontextfreie auszudrücken\n"
+ "ist, hat zwei Terminalsymbole und verlangt, daß in jedem Satz die\n"
+ "beiden Symbole gleich oft vorkommen, die Reihenfolge jedoch beliebig\n"
+ "sein kann.\n"
+ "\n"
+ "\n"
+ "Über die Syntax hinaus, haben Sprachen noch weitere Einschränkungen,\n"
+ "die sich nicht in der Grammatik ausdrücken lassen. Die meisten\n"
+ "syntaktisch korrekten C-Programme werden trotzdem vom C-Übersetzer\n"
+ "als inkorrekt zurückgewiesen. Diese Programme verstoßen gegen\n"
+ "semantische Beschränkungen, wie z.B.gegen die\n"
+ "Zuweisungskompatibilität. Das Programm:\n"
+ "int i = \"1\";\n"
+ "ist syntaktisch nach den Regeln der C-Grammatik korrekt gebildet,\n"
+ "verletzt aber die Beschränkung, daß einem Feld vom \n"
+ "Typ int kein Objekt des Typs String zugewiesen\n"
+ "werden darf.\n"
+ "\n"
+ "Aus diesen Grund besteht ein Übersetzer aus zwei großen Teilen. Der\n"
+ "syntaktischen Analyse, die prüft, ob der Satz mit den Regeln der\n"
+ "Grammatik erzeugt werden kann und der semantischen Analyse, die\n"
+ "anschließend zusätzliche semantische Bedingungen prüft.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Manchmal will man in einer Grammatik ausdrücken, dass in\n"
+ "Nichtterminalsymbol auch zu einem leeren Folge von Symbolen reduzieren\n"
+ "soll. Hierzu könnte man die Regel \n"
+ "$t$::=\n"
+ "mit leerer rechter Seite schreiben. Es ist eine Konvention ein\n"
+ "spezielles Zeichen für das leere Wort zu benutzen. Hierzu bedient man\n"
+ "sich des griechischen Buchtstabens $\\epsilon$. Obige Regel würde man\n"
+ "also schreiben als:\n"
+ "$t$::=$\\epsilon$\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Bisher haben wir uns keine Gedanken gemacht, woher die Wörter unserer\n"
+ "Sprache kommen. Wir haben bisher immer eine gegebene Menge\n"
+ "angenommen. Die Wörter einer Sprache bestimmen ihre lexikalische\n"
+ "Struktur. In unseren obigen Beispielen haben wir sehr unterschiedliche\n"
+ "Arten von Wörtern: einmal Wörter der englischen Sprache und einmal\n"
+ "Ziffernsymbole und arithmetische Operatorsymbole. Im Kontext von\n"
+ "Programmiersprachen spricht man von Token. \n"
+ "\n"
+ "Bevor wir testen können, ob ein Satz mit einer Grammatik erzeugt\n"
+ "werden kann, sind die einzelnen Wörter in diesem Satz zu\n"
+ "identifizieren. Dieses geschieht in einer lexikalischen Analyse. Man\n"
+ "spricht auch vom Lexer und Tokenizer. Um zu\n"
+ "beschreiben, wie die einzelnen lexikalischen Einheiten einer Sprache\n"
+ "aussehen, bedient man sich eines weiteren Formalismus, den regulären\n"
+ "Ausdrücken. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Die in diesem Kapitel vorgestellten Grammatiken \n"
+ "heißen kontextfrei, weil eine Regel für ein\n"
+ "Nichtterminalzeichen angewendet wird, ohne dabei zu betrachten, was\n"
+ "vor oder nach dem Zeichen für ein weiteres Zeichen steht, der Kontext\n"
+ "also nicht betrachtet wird. Läßt man auch Regeln zu, die auf der\n"
+ "linken Seite nicht ein Nichtterminalzeichen stehen haben, so kann man\n"
+ "mächtigere Sprachen beschreiben, als mit einer kontextfreien\n"
+ "Grammatik.\n"
+ "Wir können mit der folgenden nicht-kontextfreien Grammatik\n"
+ "die Sprache beschreiben, in der jeder Satz gleich oft die beiden\n"
+ "Terminalsymbole, eber in beliebiger Reihenfolge enthält.\n"
+ "\n"
+ "T$=\\{$a,b$\\}$\n"
+ "N$=\\{$start,A,B$\\}$\n"
+ "$S=$start\n"
+ "start ::= ABstart \n"
+ "start ::= $\\epsilon$\n"
+ "\n"
+ "AB ::= BA\n"
+ "BA ::= AB\n"
+ "\n"
+ "A ::= a\n"
+ "\n"
+ "B ::= b\n"
+ "\n"
+ "\n"
+ "\n"
+ "start \n"
+ "ABstart \n"
+ "ABABstart \n"
+ "ABABABstart \n"
+ "ABABABABstart \n"
+ "ABABABABABstart \n"
+ "ABABABABAB \n"
+ "AABBABABAB \n"
+ "AABBBAABAB \n"
+ "AABBBABAAB \n"
+ "AABBBABABA \n"
+ "AABBBABBAA \n"
+ "AABBBBABAA \n"
+ "AABBBBBAAA \n"
+ " aABBBBBAAA \n"
+ " aaBBBBBAAA \n"
+ " aabBBBBAAA \n"
+ " aabbBBBAAA \n"
+ " aabbbBBAAA \n"
+ " aabbbbBAAA \n"
+ " aabbbbbAAA \n"
+ " aabbbbbaAA \n"
+ " aabbbbbaaA \n"
+ " aabbbbbaaa\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "Auch die nicht durch eine kontextfreie Grammatik darstellbare Sprache:\n"
+ "$\\{a^nb^nc^n|n\\in I\\!\\!N\\}$. \n"
+ "läßt sich mit einer solchen Grammatik generieren:\n"
+ "\n"
+ "S::=abTc \n"
+ "T::=AbTc$\\epsilon$ \n"
+ "bA::=Ab \n"
+ "aA::=aa \n"
+ "\n"
+ "Eine Ableitung mit dieser Grammatik sieht wie folgt aus:\n"
+ "S \n"
+ " abTc \n"
+ " abAbTcc \n"
+ " abAbAbTccc \n"
+ " abAbAbAbTcccc \n"
+ " abAbAbAbAbTccccc \n"
+ " abAbAbAbAbccccc \n"
+ " abAAbbAbAbccccc \n"
+ " abAAbAbbAbccccc \n"
+ " abAAAbbbAbccccc \n"
+ " abAAAbbAbbccccc \n"
+ " abAAAbAbbbccccc \n"
+ " abAAAAbbbbccccc \n"
+ " aAbAAAbbbbccccc \n"
+ " aAAbAAbbbbccccc \n"
+ " aAAAbAbbbbccccc \n"
+ " aAAAAbbbbbccccc \n"
+ " aaAAAbbbbbccccc \n"
+ " aaaAAbbbbbccccc \n"
+ " aaaaAbbbbbccccc \n"
+ " aaaaabbbbbccccc\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Als Preis dafür, daß man sich nicht auf kontextfreie Grammatiken\n"
+ "beschränkt, kann nicht immer leicht und eindeutig erkannt werden, ob\n"
+ "ein bestimmter vorgegebener Satz mit dieser Grammatik erzeugt werden\n"
+ "kann. \n"
+ "\n"
+ "\n"
+ "Im letzten Kapitel haben wir uns ausführlich mit Bäumen\n"
+ "beschäftigt. Eine Grammatik stellt in naheliegender Weise nicht nur\n"
+ "ein Beschreibung einer Sprache dar, sondern jede Generierung eines\n"
+ "Satzes dieser Sprache entspricht einem Baum, dem Ableitungsbaum. \n"
+ "Die Knoten des Baumes\n"
+ "sind mit Terminal- und Nichtterminalzeichen markiert, wobei Blätter\n"
+ "mit Terminalzeichen markiert sind. Die Kinder eines Knotens sind die\n"
+ "Knoten, die mit der rechten Seite einer Regelanwendung markiert sind.\n"
+ "\n"
+ "Liest man die Blätter eines solchen Baumes von links nach rechts, so\n"
+ "ergibt sich der generierte Satz.\n"
+ "Die Ableitungen der Sätze unserer ersten Grammatik haben\n"
+ "folgende Baumdarstellung: \n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Gegenüber unserer bisherigen Darstellung der Ableitung eines Wortes\n"
+ "mit den Regeln einer Grammatik, ist die Reihenfolge, in der die Regeln\n"
+ "angewendet werden in der Baumdarstellung nicht mehr ersichtlich. Daher\n"
+ "spricht man häufiger auch vom Syntaxbaum des Satzes.\n"
+ "\n"
+ "Betrachten Sie die einfache Grammatik für arithmetische\n"
+ "Ausdrücke aus dem letzen Abschnitt. Zeichnen Sie einen Syntaxbaum für den \n"
+ "Audruck 1+1+2*20.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die Ausdrucksmöglichkeit einer kontextfreien Grammatik ist auf wenige\n"
+ "Konstrukte beschränkt. Das macht das Konzept einfach. Im Kontext von\n"
+ "Programmiersprachen gibt es häufig sprachliche Konstrukte, wie die\n"
+ "mehrfache Wiederholung oder eine Liste von bestimmten Teilen, die zwar\n"
+ "mit einer kontextfreien Grammatik darstellbar ist, für die aber\n"
+ "spezielles zusätzliche Ausdrucksmittel in der Grammatik eingeführt\n"
+ "werden. In den nächsten Abschnitten werden wir diese Erweiterungen\n"
+ "kennenlernen, allerdings werden wir in unseren Algorithmen für Sprachen\n"
+ "und Grammatiken diese Erweiterungen nicht berücksichtigen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Wenn es für ein Nichtterminalzeichen mehrere Regeln gibt, so werden\n"
+ "diese Regeln zu einer Regel umgestaltet. Die unterschiedlichen rechten\n"
+ "Seiten werden dann durch einen vertikalen \n"
+ "Strich gestrennt.\n"
+ "\n"
+ "Durch diese Darstellung verliert man leider den direkten Zusammenhang\n"
+ "zwischen den Regeln und einen Ableitungsbaum. \n"
+ "\n"
+ "Die Regeln unserer ersten Grammatik können damit wie folgt geschrieben\n"
+ "werden:\n"
+ "\n"
+ "start ::= noun-phrase verb-phrase\n"
+ "\n"
+ "noun-phrase ::= nounarticle noun\n"
+ "\n"
+ "verb-phrase ::= verb noun-phrase\n"
+ "\n"
+ "noun ::= planetmoonmarsdeimosphoebus\n"
+ "\n"
+ "verb ::= orbitsis\n"
+ "\n"
+ "article ::= a\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "Bestimmte Teile der rechten Seite einer Grammatik können durch\n"
+ "Klammern gruppiert werden. In diesen Klammern können wieder durch\n"
+ "einen vertikalen Strich getrennte \n"
+ "Alternativen stehen.\n"
+ "\n"
+ "Die einfache Grammatik für arithmetische Ausdrücke läßt sich damit\n"
+ "ohne das Nichtterminalzeichen op schreiben:\n"
+ "\n"
+ "start ::= expr\n"
+ "\n"
+ "expr ::= integerinteger\n"
+ "$(+$$-$$*$$/)$ expr\n"
+ "\n"
+ "integer ::= digitdigit integer\n"
+ "\n"
+ "digit ::=\n"
+ "0123456789\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Ein typische Konstrukt in Programmiersprachen ist, daß bestimmte\n"
+ "Kontrukte wiederholt werden können. So stehen \n"
+ "z.B. in C im Rumpf einer\n"
+ "Funktion mehrere Anweisungen. Solche Sprachkonstrukte lassen sich mit einer\n"
+ "kontextfreien Grammatik ausdrücken.\n"
+ "\n"
+ "Eine Zahl besteht aus einer Folge von $n$ Ziffern\n"
+ "($n>0$). Dieses läßt sich durch folgende Regel ausdrücken:\n"
+ "\n"
+ "Zahl ::= Ziffer ZahlZiffer\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im obigen Beispiel handelt es sich um eine 1 bis $n$-fache\n"
+ "Wiederholung des Zeichens Ziffer. Hierzu gibt es eine\n"
+ "abkürzende Schreibweise. Dem zu wiederholenden Teil wird das \n"
+ "Zeichen + nachgestellt.\n"
+ "\n"
+ "Obige Regel für das Nichtterminal Zahl läßt sich mit dieser\n"
+ "abkürzenden Schreibweise schreiben als:\n"
+ "Zahl ::= Ziffer+\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Soll ein Teil in einer Wiederholung auch keinmal vorkommen, so wird\n"
+ "statt des Zeichens + das Zeichen * genommen.\n"
+ "\n"
+ "\n"
+ "Folgende Regel drückt aus, daß ein Ausdruck eine durch Operatoren\n"
+ "getrennte Liste von Zahlen ist.\n"
+ "\n"
+ "expr ::= Zahl\n"
+ " (OpZahl)* \n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "Ein weiterer Spezialfall der Wiederholung ist die, in der der\n"
+ "entsprechende Teil keinmal oder einmal vorkommen darf, d.h.~der Teil\n"
+ "ist optional. Optionale Teile werden in der erweiterten Form in eckige\n"
+ "Klammern gesetzt.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Grammatiken geben an, wie Sätze einer Sprache gebildet werden\n"
+ "können. In der Informatik interessiert der umgekehrte Fall. Ein Satz\n"
+ "ist vorgegeben und es soll geprüft werden, ob dieser Satz mit einer\n"
+ "bestimmten Grammatik erzeugt werden kann. Ein C-Übersetzer \n"
+ "prüft z.B.ob ein vorgegebenes Programm syntaktisch zur durch\n"
+ "die Javagrammatik beschriebenen Sprache gehört.\n"
+ "Hierzu ist ein Prüfalgorithmus anzugeben, der testet, ob die Grammatik\n"
+ "einen bestimmten Satz generieren kann. Ein solches Programm \n"
+ "wird Parser genannt. Ein ParserLateinisch Pars\n"
+ "bedeutet Teil. zerteilt einen Satz in seine syntaktischen\n"
+ "Bestandteile. \n"
+ "\n"
+ "\n"
+ "\n"
+ "Man kann unterschiedliche Algorithmen benutzen, um zu testen, daß ein\n"
+ "zu der Sprache einer Grammatik gehört. Eine Strategie geht primär von\n"
+ "der Grammatik aus, die andere betrachten eher den zu prüfenden Satz.\n"
+ "\n"
+ "\n"
+ "Eine einfache Idee zum Schreiben eines Parsers ist es, einfach\n"
+ "nacheinander auszuprobieren, ob die Regelanwendung nach und nach einen\n"
+ "Satz bilden kann. Hierzu startet man mit dem Startsymbol und wendet\n"
+ "nacheinander die Regeln an. Dabei wendet man immer eine Regel auf das\n"
+ "linkeste Nichtterminalzeichen an, so lange, bis der Satzanfang\n"
+ "abgelitten wurde, oder aber ein falscher Satzanfang abgelitten\n"
+ "wurde. Im letzteren Fall hat man offensichtlich bei einer Regel die\n"
+ "falsche Alternative gewählt. Man ist in eine Sackgasse geraten. \n"
+ "Nun geht man in seiner Ableitung zurück bis\n"
+ "zu der letzten Regelanwendung, an der eine andere Alternative hätte\n"
+ "gewählt werden können. Man spricht dann von backtracking; auf\n"
+ "deutsch kann man treffend von Zurückverfolgen sprechen.\n"
+ "\n"
+ "Auf diese Weise gelangt man entweder zu einer Ableitung des Satzes,\n"
+ "oder stellt irgendwann fest, daß man alle Alternativen ausprobiert hat\n"
+ "und immer in eine Sackgasse geraten ist. Dann ist der Satz nicht in\n"
+ "der Sprache.\n"
+ "\n"
+ "Diese Strategie ist eine Suche. Es wird systematisch aus allen\n"
+ "möglichen Ableitungen in der Grammatik nach der Ableitung gesucht, die\n"
+ "den gewünschten Satz findet.\n"
+ "\n"
+ "Wir suchen mit dieser Strategie die Ableitung des \n"
+ "Satzes a moon orbits mars in dem\n"
+ "Sonnensystembeispiel. Sackgassen sind durch einen Punkt $\\bullet$\n"
+ "markiert. \n"
+ "
\n"
+ "(0)start \n"
+ "(1) aus 0noun-phrase verb-phrase\n"
+ "(2a) aus 1noun verb-phrase \n"
+ "\n"
+ "(2a3a) aus 2aplanet verb-phrase$\\bullet$ \n"
+ "(2a3b) aus 2amoon verb-phrase$\\bullet$ \n"
+ "(2a3c) aus 2amars verb-phrase$\\bullet$ \n"
+ "(2a3d) aus 2adeimos verb-phrase$\\bullet$\n"
+ "(2a3e) aus 2aphobus verb-phrase$\\bullet$ \n"
+ "\n"
+ "(2b) aus 1article noun verb-phrase \n"
+ "(3) aus 2ba noun verb-phrase \n"
+ "\n"
+ "(4a) aus 3a planet verb-phrase$\\bullet$\n"
+ "(4b) aus 3a moon verb-phrase \n"
+ "\n"
+ "(5) aus 4ba moon verb noun-phrase \n"
+ "\n"
+ "(6) aus 5a moon orbits noun-phrase \n"
+ "\n"
+ "(7) aus 6a moon orbits noun \n"
+ "\n"
+ "(8a) aus 7a moon orbits planet$\\bullet$ \n"
+ "(8b) aus 7a moon orbits moon$\\bullet$ \n"
+ "(8c) aus 7a moon orbits mars \n"
+ "
\n"
+ "\n"
+ "\n"
+ "Alternativ könnte man diese Strategie auch symetrisch nicht von links\n"
+ "sondern von der rechten Seite an ausführen, also immer eine Regel für\n"
+ "das rechtestes Nichtterminalsymbol anwenden.\n"
+ "\n"
+ "\n"
+ "Die Strategie des rekursiven Abstiegs auf der linken Seite\n"
+ "funktioniert für eine bestimmte Art von Regeln nicht. Dieses sind\n"
+ "Regeln, die in einer Alternative als erstes Symbol wieder das\n"
+ "Nichtterminalsymbol stehen haben, das auch auf der linken Seite\n"
+ "steht. Solche Regeln heißen linksrekursiv. Unsere Strategie terminiert\n"
+ "in diesen Fall nicht.\n"
+ "\n"
+ "\n"
+ "Gegeben sei eine Grammatik mit einer linksrekursiven Regel:\n"
+ "expr::=expr+zahlzahl\n"
+ "\n"
+ "Der Versuch den Satz\n"
+ "zahl+zahl\n"
+ "mit einem links rekursiv absteigenden Parser abzuleiten, führt zu\n"
+ "einem nicht terminierenden rekursiven Abstieg. Wir gelangen nie in\n"
+ "eine Sackgasse:\n"
+ "\n"
+ "
\n"
+ "\n"
+ "\n"
+ "Es läßt sich also nicht für alle Grammatiken mit dem Verfahren des\n"
+ "rekursiven Abstiegs entscheiden, ob ein Satz mit der Grammatik erzeugt\n"
+ "werden kann; aber es ist möglich, eine Grammatik mit linksrekursiven\n"
+ "Regeln so umzuschreiben, daß sie die gleiche Sprache generiert, jedoch\n"
+ "nicht mehr linksrekursiv ist. Hierzu gibt es ein einfaches Schema:\n"
+ "\n"
+ "\n"
+ "Eine Regel nach dem Schema:\n"
+ "A ::= Arestalt2\n"
+ "ist zu ersetzen durch die zwei Regeln:\n"
+ "\n"
+ "A::= alt2R \n"
+ "R::= restR$\\epsilon$\n"
+ "wobei R ein neues Nichtterminalsymbol ist.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Die rekursiv absteigende Strategie hat einen weiteren Nachteil. Wenn\n"
+ "sie streng schematisch angewendet wird, führt sie dazu, daß bestimmte\n"
+ "Prüfungen mehrfach durchgeführt werden. Diese mehrfache Ausführung\n"
+ "kann sich bei wiederholter Regelanwendung multipizieren und zu einem\n"
+ "sehr ineffizienten Parser führen. Grund dafür sind Regeln, die zwei\n"
+ "Alternativen mit gleichem Anfang haben:\n"
+ "\n"
+ " S ::= ABA\n"
+ "\n"
+ "Beide Alternativen für das Nichtterminalzeichen S starten mit\n"
+ "dem Zeichen A. Für unseren Parser bedeutet das soweit:\n"
+ "versuche nach der ersten Alternative zu parsen. Hierzu parse erst nach\n"
+ "dem Symbol A. Das kann eine sehr komplexe Berechnung\n"
+ "sein. Wenn sie gelingt, dann versuche anschließend weiter nach dem\n"
+ "Symbol B zu parsen. Wenn das fehlschlägt, dann verwerfe die\n"
+ "Regelalternative und versuche nach der zweiten Regelalternative zu\n"
+ "parsen. Jetzt ist wieder nach dem Symbol A zu parsen, was wir\n"
+ "bereits gemacht haben.\n"
+ "Regeln der obigen Art wirken sich auf unsere Parsstrategie ungünstig\n"
+ "aus. Wir können aber ein Schema angegeben, wie man solche Regeln aus\n"
+ "der Grammatik eleminiert, ohne die erzeugte Sprache zu ändern:\n"
+ "\n"
+ "Regeln der Form\n"
+ " S ::= ABA\n"
+ "sind zu ersetzen durch\n"
+ " \n"
+ " S ::= AT \n"
+ " T ::= B$\\epsilon$\n"
+ " \n"
+ "wobei T ein neues Nichtterminalzeichen ist.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im letzten Abschnitt haben wir gesehen, wie wir die Grammatik\n"
+ "umschreiben können, so daß nach dem Zurücksetzen in unserem\n"
+ "Algorithmus es nicht vorkommt, bereits ausgeführte Regeln ein weiteres\n"
+ "Mal zu durchlaufen. Schöner noch wäre es, wenn wir auf das\n"
+ "Zurücksetzen ganz verzichten könnten. Dieses läßt sich allgemein nicht\n"
+ "erreichen, aber es gibt Grammatiken, in denen man durch Betrachtung\n"
+ "des nächsten zu parsenden Zeichens erkennen kann, welche der\n"
+ "Regelalternativen als einzige Alternative in betracht kommt. Hierzu\n"
+ "kann man für eine Grammatik für jedes Nichtterminalzeichen in jeder\n"
+ "Regelalternative berechnen, welche Terminalzeichen als linkestes\n"
+ "Zeichen in einem mit dieser Regel abgelittenen Satz auftreten\n"
+ "kann. Wenn die Regelalternativen disjunkte solche Menge des ersten\n"
+ "Zeichens haben, so ist eindeutig bei Betrachtung des ersten Zeichens\n"
+ "eines zu parsenden Satzes erkennbar, ob und mit welcher Alternative\n"
+ "dieser Satz nur parsbar sein kann.\n"
+ "Die gängigsten Parser benutzen diese Entscheidung, nach dem erstem\n"
+ "Zeichen. Diese Parser sind darauf angewiesen, daß die Menge der ersten\n"
+ "Zeichen der verschiedenen Regelalternativen disjunkt sind.\n"
+ "Der Vorteil an diesem Verfahren ist, daß die Token nach und nach von\n"
+ "links nach rechts stückweise konsumiert werden. Sie können durch einen\n"
+ "Datenstro, relisiert werden. Wurden sie einmal konsumiert, so werden\n"
+ "sie nicht mehr zum Parsen benötigt, weil es kein Zurücksetzen gibt. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Der rekursiv absteigende Parser geht vom Startsymbol aus und versucht\n"
+ "durch Regelanwendung den in Frage stehenden Satz abzuleiten. \n"
+ "Eine andere Strategie ist die des Schiebens-und-Reduzierens\n"
+ "(shift-reduce). Diese\n"
+ "geht vom im Frage stehenden Satz aus und versucht die Regeln rückwärts\n"
+ "anzuwenden, bis das Startsymbol erreicht wurde. Hierzu wird der Satz\n"
+ "von links nach rechts (oder symmetrisch von rechts nach links)\n"
+ "betrachtet und versucht, rechte Seiten von Regeln zu finden und durch\n"
+ "ihre linke Seite zu ersetzen. Dazu benötigt man einen Marker, der angibt, bis\n"
+ "zu welchen Teil man den Satz betrachtet. Wenn links des Markers keine\n"
+ "linke Seite einer Regel steht, so wird der Marker ein Zeichen weiter\n"
+ "nach rechts verschoben.\n"
+ "\n"
+ "Wir leiten im folgenden unserer allseits bekannten Beispielsatz aus\n"
+ "dem Sonnensystem durch Schieben und Reduzieren ab. Als Marker benutzen\n"
+ "wir einen Punkt.\n"
+ "\n"
+ "
\n"
+ "\n"
+ "Wir werden im Laufe dieser Vorlesung diese Parserstrategie nicht\n"
+ "weiter verfolgen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Auch Die Turingmaschine besteht aus\n"
+ "einem unendlich langen Speicherband mit unendlich vielen sequentiell\n"
+ "angeordneten Feldern. In jedem dieser Felder kann genau ein Zeichen\n"
+ "gespeichert werden. (Man darf sich das unendliche lange Band auch als ein\n"
+ "endliches vorstellen, es muss jedoch lang genug sein, um die aktuelle\n"
+ "Berechnung ungehindert ausführen zu können, d.h. der Lese- und Schreibkopf\n"
+ "darf nicht an das Ende stoßen.) \n"
+ "einem programmgesteuerten Lese- und Schreibkopf, der sich auf dem Speicherband\n"
+ "feldweise bewegen und die Zeichen verändern kann. \n"
+ "\n"
+ "Eine Turingmaschine modifiziert die Eingabe auf dem Band nach einem gegebenen\n"
+ "Programm. Ist die Berechnung beendet, so befindet sich das Ergebnis auf dem\n"
+ "Band. Es wird somit jedem Eingabewert ein Ausgabewert zugeordnet. Eine\n"
+ "Turingmaschine muss aber nicht für alle Eingaben stoppen. In diesem Fall ist\n"
+ "die Funktion für die Eingabe undefiniert. \n"
+ "\n"
+ "Als Ergebnis der Berechnung wird manchmal diejenige Zeichenfolge definiert,\n"
+ "die nach dem Anhalten auf dem Band steht. Die Turingmaschine wird jedoch\n"
+ "meistens (wie viele andere Automaten auch) für Entscheidungsprobleme\n"
+ "eingesetzt, also für Fragen, die mit ja oder nein zu beantworten sind. Hierbei werden zum Beispiel zwei\n"
+ "Zeichen vereinbart, wobei das eine als ja und das andere\n"
+ "als nein interpretiert wird. Nach dem Anhalten der\n"
+ "Turingmaschine liegt die Antwort als eines der beiden Zeichen auf dem\n"
+ "Ausgabeband vor. Zu beachten ist dabei, dass sich jedes Problem als\n"
+ "Entscheidungsproblem formulieren lässt, indem man fragt, ob ein bestimmter\n"
+ "Wert eine Lösung für ein konkretes Problem ist. wenn Programmiersprachen als formales System auf dem Papier entworfen\n"
+ "werden könnten, \n"
+ "ist die Entwicklung von Programmiersprachen stark an der zur\n"
+ "Verfügung stehenden Hardware gekoppelt. Aber es gibt durchaus auch\n"
+ "formale Systeme, die im weitesten Sinne als Programmiersprachen bezeichnet\n"
+ "werden können, die vollkommen unabhängig zu irgendeiner Hardware entwickelt\n"
+ "wurden. Die zwei einflußreichsten hiervon sind:\n"
+ "\n"
+ "\n"
+ "die Turingmaschine\n"
+ "der Lambdakalkül \n"
+ "Diese beiden formalen Systeme sind in etwa der gleichen Zeit entstanden, um\n"
+ "mathematisch dem Begriff der Berechenbarkeit auf die Spur zu kommen (fest\n"
+ "gemacht an dem sogenannten Entscheidungsproblem, das, wie man den\n"
+ "Zitaten entnehmen kann, auch in der englischsprachigen Literatur auf deutsch\n"
+ "bezeichnet wurde). Sie sind\n"
+ "entwickelt worden, lange bevor programmierbare elektronische Rechensysteme\n"
+ "überhaupt zu bauen erwogen wurden. Beide Systeme sind vollkommen\n"
+ "unterschiedlich und ermöglichen die Programmierung der gleichen Klasse\n"
+ "mathematischer Funktionen.\n"
+ "\n"
+ "\n"
+ "Der Lambda-Kalkül ist eine formale Sprache zur Untersuchung von Funktionen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "Der Lambda-Kalkül wurde von Alonzo Church und Stephen Kleene in den 1930er\n"
+ "Jahren eingeführt. Church benutzte den Lambda-Kalkül, sowohl um 1936 eine\n"
+ "negative Antwort auf das Entscheidungsproblem zu geben, als auch eine\n"
+ "Fundierung eines logischen Systems zu finden, wie es Russells und Whiteheads\n"
+ "Principia Mathematica zugrunde lag. Mittels des untypisierten Lambda-Kalküls\n"
+ "kann man klar definieren, was eine berechenbare Funktion ist. Die Frage, ob\n"
+ "zwei Lambda-Ausdrücke (s.u.) äquivalent sind, kann im Allgemeinen nicht\n"
+ "algorithmisch entschieden werden. In seiner typisierten Form kann der Kalkül\n"
+ "benutzt werden, um Logik höherer Stufe darzustellen. Der Lambda-Kalkül hat die\n"
+ "Entwicklung funktionaler Programmiersprachen, die Forschung um Typsysteme von\n"
+ "Programmiersprachen im Allgemeinen als auch moderne Teildisziplinen in der\n"
+ "Logik wie Typtheorie wesentlich beeinflusst. \n"
+ "\n"
+ "Meilensteine der Entwicklung waren im Einzelnen:\n"
+ "\n"
+ "\n"
+ "Nach der Einführung die frühe Entdeckung, dass der Lambda-Kalkül im Sinne des\n"
+ "Konzepts der Berechenbarkeit ebenso mächtig ist wie die Turing-Maschine oder\n"
+ "jede moderne Programmiersprache. \n"
+ "\n"
+ "Konrad Zuse hat Ideen aus dem Lambda-Kalkül 1942 bis 1946 in seinen Plankalkül\n"
+ "einfließen lassen. \n"
+ "\n"
+ "John McCarthy hat sie Ende der fünfziger Jahre verwendet und damit die\n"
+ "minimalen Funktionen der Programmiersprache LISP definiert. \n"
+ "\n"
+ "Die typisierten Varianten des Lambda-Kalküls führten zu modernen\n"
+ "Programmiersprachen wie ML oder Haskell. \n"
+ "\n"
+ "Als überaus fruchtbar erwies sich die Idee, Ausdrücke des typisierten\n"
+ "Lambda-Kalküls zur Repräsentation von Termen einer Logik zugrunde zu legen,\n"
+ "den Lambda-Kalkül also als Meta-Logik zu verwenden. Erstmals von Church 1940\n"
+ "in seiner Theory of Simple Types präsentiert, führte sie einerseits zu\n"
+ "modernen Theorembeweisern für Logiken höherer Stufe und\n"
+ "\n"
+ "andererseits in den 70er und 80er Jahren zu Logiken mit immer mächtigeren\n"
+ "Typsystemen, in dem sich z.B. logische Beweise an sich als Lambda-Ausdruck\n"
+ "darstellen lassen.\n"
+ "\n"
+ "In Anlehnung an den Lambda-Kalkül wurde für die Beschreibung nebenläufiger\n"
+ "Prozesse der Pi-Kalkül von Robin Milner in den 90er Jahren entwickelt. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Im Lambda-Kalkül steht jeder Ausdruck für eine Funktion mit nur einem\n"
+ "Argument; sowohl Argumente als auch Resultate solcher Funktionen sind wiederum\n"
+ "Funktionen. Eine Funktion kann anonym durch eine so genannte\n"
+ "Lambda-Abstraktion definiert werden, die die Zuordnung des Arguments zum\n"
+ "Resultat beschreibt. Zum Beispiel wird die erhöhe-um-2 Funktion $f(x) = x + 2$\n"
+ "im Lambda-Kalkül durch die \n"
+ "Lambda-Abstraktion $\\lambda x. x + 2$ (oder äquivalent\n"
+ "durch $\\lambda y. y + 2$; der Name des formalen Parameters ist unerheblich)\n"
+ "beschrieben; $f(3)$ (die so genannte Funktionsanwendung) kann daher als\n"
+ "$(\\lambda x. x + 2) 3$ geschrieben werden. \n"
+ "Die Funktionsanwendung ist linksassoziativ: $f x y$\n"
+ "ist syntaktisch gleichbedeutend mit $(f x) y$. Offensichtlich sollten die\n"
+ "Ausdrücke: $(\\lambda x. x +3) ((\\lambda x. x+2) 0 )$ und $(\\lambda x. x + 3) 2$ und $3 +\n"
+ "2$ äquivalent sein - dies motiviert die $\\beta$-Kongruenzregel. \n"
+ "Eine\n"
+ "zweistellige Funktion kann im Lambda-Kalkül durch eine einstellige Funktion\n"
+ "dargestellt werden, die eine einstellige Funktion als Resultat zurückgibt\n"
+ "(siehe auch Schönfinkeln bzw. Currying). Die Funktion $f(x, y) = x - y$ kann \n"
+ "zum\n"
+ "Beispiel durch $\\lambda x. \\lambda y. x - y$ dargestellt werden. \n"
+ "Denn mit der $\\beta$-\n"
+ "Kongruenzregel sind die Ausdrücke $(\\lambda x. \\lambda y. x - y)7~2$,\n"
+ "$(\\lambda y. 7 - y) 2$ und $7 - 2$ äquivalent. \n"
+ " \n"
+ " \n"
+ "\n"
+ "\n"
+ "Erstaunlicher Weise spiegeln sich die beiden Systeme noch heute in den großen\n"
+ "Klassen der gängigen Programmiersprachen wieder: währen die Turingmaschine als\n"
+ "der theoretische Urvater aller imperativen Sprachen betrachtet werden kann,\n"
+ "legt der Lamda-Kalkül die theoretische Grundlage für funktionale Sprachen. \n"
+ "\n"
+ "Ebenso wie in C geht die Turingmaschine von einem sequentiellen Speicher\n"
+ "aus. Speicherzellen können gelesen und neu überschrieben werden, alles\n"
+ "Konzepte, wie wir sie aus C kennen. Dieses Modell der Programmierung ist eng\n"
+ "an der noch heute gebräuchlichen Hardware gebunden.\n"
+ "\n"
+ "Der Lambda-Kalkül hingegen kennt keine Speicherzellen. Die einzige Form von\n"
+ "Datenhaltung sind die Parameter von Funktionen. Funktionen sind können dabei\n"
+ "auch wieder als Argumente anderer Funktionen verwendet werden. Mit der\n"
+ "Programmiersprache Lisp wurden diese Konzepte direkt in\n"
+ "eine Programmiersprache umgesetzt und damit eine lange Tradition von\n"
+ "Programmiersprachen begründet auf deren Weg sich z.B.~die \n"
+ "Sprachen Scheme, ML, Miranda, Haskell, Clean, F\\#, Pizza und Scala befinden. \n"
+ "\n"
+ "Man kann auch die Turingmaschine als den Versuch das Konzept der\n"
+ "Berechenbarkeit über eine operationale Semantik zu definieren und den\n"
+ "Lambdakalkül als den Versuch die Berechenbarkeit über eine denotationale\n"
+ "Semantik zu definieren, auffassen.\n"
+ "\n"
+ "Aber noch lange vor den theoretische formalen Systemen zur Berechenbarkeit und\n"
+ "weit vor der Erfindung von elektronischen Rechenanlagen, gab es \n"
+ "eine Die Analytical Engine, die einen wichtigen Schritt in der\n"
+ "Geschichte der Computer darstellt, ist der Entwurf einer mechanischen\n"
+ "Rechenmaschine für allgemeine Anwendungen von Charles Babbage, einem\n"
+ "britischen Professor der Mathematik. Sie wurde 1837 zum ersten Mal\n"
+ "beschrieben, Babbage setzte die Arbeit an dem Entwurf aber bis zum Ende seines\n"
+ "Lebens (1871) fort. Bedingt durch finanzielle und technische Probleme wurde\n"
+ "die Analytical Engine nie gebaut. Es ist mittlerweile allerdings allgemein\n"
+ "anerkannt, dass der Entwurf korrekt war und dass das Gerät funktioniert\n"
+ "hätte. Vergleichbare Computer für allgemeine Anwendungen wurden erst 100 Jahre\n"
+ "später wirklich konstruiert. \n"
+ "\n"
+ "Babbage begann mit der Konstruktion seiner Difference Engine, einem\n"
+ "mechanischen Computer, der speziell für die Lösung polynomialer Funktionen\n"
+ "konzipiert war. Als ihm klar wurde, dass eine viel allgemeinere Bauweise\n";
String skript9 =
"möglich wäre, begann er mit der Arbeit an der Analytical Engine. \n"
+ "\n"
+ "Diese sollte von einer Dampfmaschine angetrieben werden und wäre über 30 Meter\n"
+ "lang und 10 Meter breit gewesen. Die Eingabe (Befehle und Daten) sollte über\n"
+ "Lochkarten erfolgen, eine Methode, die in der damaligen Zeit der Steuerung\n"
+ "mechanischer Webstühle diente. Für die Ausgabe waren ein Drucker, ein\n"
+ "Kurvenplotter und eine Glocke geplant. Die Maschine sollte außerdem Zahlen in\n"
+ "Lochkarten oder wahlweise Metallplatten stanzen können. Sie benutzte dezimale\n"
+ "Fließkommaarithmetik und es war Speicher für 1000 Wörter zu 50 Dezimalstellen\n"
+ "vorgesehen. Die Recheneinheit (Mühle genannt) sollte in der Lage \n"
+ "sein, die vier Grundrechenarten durchzuführen. \n"
+ "\n"
+ "Die vorgesehene Programmiersprache war ähnlich den heute verwendeten\n"
+ "Assemblersprachen. Schleifen und bedingte Verzweigungen waren möglich. Drei\n"
+ "verschiedene Arten von Lochkarten wurden benutzt: eine für arithmetische\n"
+ "Operationen, eine für numerische Konstanten und eine für Lade- und\n"
+ "Speicheroperationen, um Zahlen aus dem Speicher in die Recheneinheit und\n"
+ "wieder zurück zu transferieren. Es gab wahrscheinlich drei separate\n"
+ "Lochkartenleser für die drei Kartenarten. \n"
+ "\n"
+ "1842 schrieb der italienische Mathematiker Menabrea, der den reisenden Babbage\n"
+ "in Italien getroffen hatte, eine Beschreibung der Analytical Engine auf\n"
+ "französisch, die von Lady Ada Augusta, Countess of Lovelace ins Englische\n"
+ "übersetzt und (nach einer Anfrage von Babbage, warum sie denn nicht eine\n"
+ "eigene Abhandlung verfasst hätte) ausführlich kommentiert wurde. Ihr Interesse\n"
+ "für die Engine war bereits zehn Jahre früher geweckt worden. Ihre Anmerkungen\n"
+ "zu Menabreas Beschreibung haben ihr später den \n"
+ "Titel erste Programmiererin eingebracht. \n"
+ "\n"
+ "1878 empfahl ein Komitee der British Association for the Advancement of\n"
+ "Science, die Analytical Engine nicht zu bauen. \n"
+ "\n"
+ "1910 berichtete Babbages Sohn, Henry P. Babbage, dass ein Teil der\n"
+ "Recheneinheit und der Drucker gebaut und dazu benutzt worden wären, eine\n"
+ "(fehlerhafte) Liste von Vielfachen von pi auszurechnen. Dies war nur ein\n"
+ "kleiner Teil der ganzen Engine, nicht programmierbar und ohne Speicher. \n"
+ "\n"
+ "Danach geriet die Maschine in Vergessenheit. Die Pläne für ihren Aufbau gelten\n"
+ "jedoch als funktionsfähig, und erst um 1960 realisierten Computer die von\n"
+ "Babbage geplante Rechengenauigkeit (50 Dezimalstellen sind ca. 166 Bit bzw. 20\n"
+ "Byte). Howard Hathaway Aiken, der später die elektrische Rechenmaschine Havard\n"
+ "Mark I baute, wurde durch ihren Aufbau beeinflusst. \n"
+ "\n"
+ "Aus Babbages \n"
+ "Autobiographie: Sobald eine Analytical Engine existiert, \n"
+ "wird sie notwendigerweise der\n"
+ "Wissenschaft die zukünftige Richtung \n"
+ "weisen. Programmiersprache: Es handelt sich um eine Art\n"
+ "Assemblersprache, zur Programmierung der von Charles Babbage \n"
+ "erdachten analytical machine. Hierbei handelte es sich um eine rein\n"
+ "mechanische Maschine, die die in gleicher Weise areiten sollte, wie wir es von\n"
+ "einem heutigen Computer kennen. Tatsächlich ist die Maschine zu Babbages\n"
+ "Lebzeiten nie gebaut worden. Theoretisch wurde die Arbeit unterstützt \n"
+ "durch Ada Lovelace.Ada Lovelace (auch \n"
+ "Ada Augusta Byron, Ada King oder Countess of Lovelace) ($\\ast$10. Dezember 1815 in London; $\\dagger$27. November 1852 in London; eigentlich\n"
+ "Augusta Ada King Byron, Countess of Lovelace) war eine britische\n"
+ "Mathematikerin. Sie war die Tochter Lord Byrons und Mitarbeiterin Charles\n"
+ "Babbages.\n"
+ "\n"
+ " \n"
+ "\n"
+ "Lord Byron hatte drei Kinder von drei Frauen, nur Ada war ehelich\n"
+ "geboren. Doch Adas Mutter Anne Isabella (Annabella) Milbanke ließ sich von dem\n"
+ "Dichter scheiden, als Ada erst einige Wochen alt war, und so lernte diese\n"
+ "ihren berühmten Vater nie kennen. Ihre mathematisch interessierte Mutter\n"
+ "ermöglichte Ada eine naturwissenschaftliche Ausbildung, in deren Verlauf sie\n"
+ "Charles Babbage und die Mathematikerin Mary Somerville kennenlernte. \n"
+ "\n"
+ "Mit 19 Jahren heiratete Ada Byron William King, 8. Baron King (1805-1893), der\n"
+ "1838 zum 1. Earl of Lovelace erhoben wurde. Sie gebar drei Kinder in sehr\n"
+ "kurzen Abständen, eine ihrer Töchter war Annabelle Isabella Blunt. In ihrer\n"
+ "Korrespondenz mit Mary Somerville schrieb sie, dass sie eine unglückliche Ehe\n"
+ "führe, weil ihr neben Schwangerschaften und Kinderbetreuung so wenig Zeit für\n"
+ "ihr Studium der Mathematik und ihrer zweite Leidenschaft, der Musik,\n"
+ "blieben. Um sich abzulenken stürzte sie sich ins Gesellschaftsleben und hatte\n"
+ "mehrere Affären. Mit großer Begeisterung wettete sie auf Pferde. Die letzten\n"
+ "Jahre ihres Lebens, bereits mit Krebs ans Bett gefesselt, soll sie mit der\n"
+ "Entwicklung eines mathematisch \n"
+ "ausgefeilten sicheren Wettsystems verbracht\n"
+ "haben. Ada Lovelace starb im jungen Alter von 36 Jahren. \n"
+ "\n"
+ " \n"
+ "1843 übersetzte sie die durch den italienischen Mathematiker Luigi Menebrea\n"
+ "auf Französisch angefertigte Beschreibung von Babbages Analytical Engine ins\n"
+ "Englische und ergänzte eigene Notizen und Überlegungen zur Maschine. Babbages\n"
+ "Maschine wurde zu seinen Lebzeiten niemals erbaut, da ihm das britische\n"
+ "Parlament die Finanzierung versagte. Dessen ungeachtet legte Ada Lovelace\n"
+ "einen schriftlichen Plan vor, wie man Bernoulli-Zahlen mit der Maschine\n"
+ "berechnen könnte. Dieser Plan brachte ihr den Ruhm ein, nicht nur die erste\n"
+ "Programmiererin, sondern auch der erste Programmierer überhaupt gewesen zu\n"
+ "sein. Auch deswegen wurde die Programmiersprache Ada nach ihr benannt. \n"
+ "\n"
+ "Einige Biographen vertreten die Meinung, dass Ada Lovelace trotz ihrer\n"
+ "Ausbildung einige Schwierigkeiten mit Mathematik hatte, und äußern deshalb\n"
+ "Zweifel, ob sie Babbages Maschine wirklich vollständig verstanden hatte, oder\n"
+ "nicht eher von Babbage als Aushängeschild zu Zwecken der Öffentlichkeitsarbeit\n"
+ "missbraucht wurde. Diese Frage wird wohl mit den heute zur Verfügung stehenden\n"
+ "Informationen niemals abschließend geklärt werden. \n"
+ "\n"
+ "Auf jeden Fall muss man Ada Lovelace anrechnen, dass ihre Vorstellungskraft\n"
+ "weit über die Babbages hinausging; so formulierte sie z. B. in ihren Notizen\n"
+ "zur Analytical Engine die Idee, dass ein Nachfolger der Maschine eines Tages\n"
+ "auch in der Lage sei, Musik zu komponieren oder Grafiken zu \n"
+ "zeichnen.\n"
+ "\n"
+ " Sie hat Kommentare zu Babbage arbeiten geschrieben, die\n"
+ "länger, genauer und ausführlicher als sein Abhandlungen sind. In diesen\n"
+ "Artikeln entwirft sie auch Programme zur Steuerung von Babbages Maschine, so\n"
+ "dass ihr zu Recht der Titel als erste Programmierin zuerkannt wird. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Wir haben in den meisten Aufgaben bereits Wert darauf gelegt, Softwarekomponenten zu\n"
+ "erstellen, die in verschiedenen Programmen wiederverwendet werden kann, wie\n"
+ "z.B. die kleine Bibliothek zum Erzeugen von Bitmapdateien oder die Bibliothek\n"
+ "zur Bearbeitung von einfachen Listen mit beliebigen Objekten. Bisher haben wir\n"
+ "für solche Bibliotheken mit der Option -c der Compilers Objektdateien\n"
+ "erzeugt, die wir dann manuell zu unserem Programm hinzugelinkt haben. \n"
+ "\n"
+ "Dabei entstehen ausführbare Programme, die den kompletten Objektode der benutzten\n"
+ "Bibliothek enthalten. Man spricht davon, dass die Bibliothek statisch zum\n"
+ "Programm hinzugefügt wird. Dabei wird quasi der Objektcode der Bibliothek aus\n"
+ "der .o-Datei mehr oder weniger in den Code des ausführbaren Programms\n"
+ "hineinkopiert. Wenn mehrere Programme die gleiche Bibliothek benutzen, dann\n"
+ "wird dieser Code in mehrere Programme hineinkopiert. Er existiert dann also in\n"
+ "mehreren ausführbaren Programmen. Werden diese Pragramme gleichzeitig auf\n"
+ "einem Betriebssystem egstartet, dann defindet sich der Code der Bibliothek\n"
+ "mehrfach im Hauptspeicher. \n"
+ "\n"
+ " \n"
+ "\n"
+ "Der eigentliche Idee einer Bibliothek ist, dass sie einmal für ganz viele\n"
+ "Programme existiert und nicht für jedes Programm, das sie benutzen will, neu\n"
+ "kopiert wird. Hierzu bieten die Betriebssysteme \n"
+ "sogenannte shared libraries an. Dabei ist die Idee, den Objektcode\n"
+ "für die Bibliothek nur einmal im Betriebssystem vorzuhalten. Die ausführbaren\n"
+ "Pragramme enthalten nur einen Hinweis darauf, dass sie diese Bibliothek\n"
+ "benötigen. \n"
+ "\n"
+ "\n"
+ "\n"
+ "gcc -fPIC -c PList.c\n"
+ " gcc -fPIC -c PListUtil.c\n"
+ " gcc -fPIC -c PListUtil2.c\n"
+ " gcc -shared -o libplist.so PList.o PListUtil.o PListUtil2.o\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "gcc -o PlistTest -L. -lplist PListTest.c\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Mit die aufwendigsten und größten Bibliotheken beschäftigen sich mit dem\n"
+ "Erstellen einer graphischen Benutzeroberfläche kurz GUI. Eine\n"
+ "deratige Bibliothek für die Programmiersprache C ist die Bibliothek GTK. \n"
+ "\n"
+ "In diesem Abschnitt soll\n"
+ "exemplarisch ein minimaler Einstieg in die Bibliothek GTK gezeigt \n"
+ "werden.\n"
+ "\n"
+ "Jedes GUI-Programm der Bibliothek GTK benötigt einen Aufruf an die \n"
+ "Initialisierung der Bibliothek, hierzu dient die Funktion gtk_init. \n"
+ "Anschließend können Funktionen aufgerufen werden, die einzelne Gui-Komponenten\n"
+ "erzeugen. Eine solche Funktion ist die Funktion gtk_window_new, die\n"
+ "eine neues Fensterobjekt erzeugt. Sie kann mit einem Parameter aufgerufen\n"
+ "werden, der die Art des Fensters genauer spezifiziert.\n"
+ "\n"
+ "Folgende Konstanten können Sie für den Fenstertypen (GtkWindowType) verwenden:\n"
+ "\n"
+ "GTK_WINDOW_TOPLEVEL Wird meist für typische Applikationen verwendet \n"
+ "GTK_WINDOW_DIALOG Für Fenster die für Nachrichten oder Dialoge verwendet \n"
+ "GTK_WINDOW_POPUP Ein Fenster für Popup's\n"
+ "\n"
+ "\n"
+ "Schließlich können Komponenten mit dem Aufruf der\n"
+ "Funktion gtk_widget_show sichtbar gemacht werden. Wenn auf diese\n"
+ "Weise alle Komponenten erzeugt sind, kann die GUI-Anwendung mit dem Aufruf der\n"
+ "Funktion gtk_main gestartet werden. Insgesamt läßt sich folgendes\n"
+ "minimale Programm schreiben, welches ein kleines Fenster öffnet:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "int main(int argc, char **argv){\n"
+ " gtk_init(&argc, &argv);\n"
+ "\n"
+ " GtkWidget* fenster = gtk_window_new(GTK_WINDOW_TOPLEVEL);\n"
+ " gtk_widget_show(fenster);\n"
+ "\n"
+ " gtk_main();\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "Wie man sieht, beginnen alle Funktionen der Bibliothek mit dem \n"
+ "Präfix gtk_. Dies soll einiegermaßen sicherstellen, dass nicht zwei\n"
+ "unterschiedliche Bibliotheken ein und denselben Funktionsnamen benutzen. Es\n"
+ "ist nicht davon auszugehen, dass eine andere Bibliothek vernünftiger Weise\n"
+ "eine Funktion mit dem Präfix gtk_ definietr hat.\n"
+ "\n"
+ "Das obige Programm ist gar nicht so leicht zu kompilieren. Versuchen wir\n"
+ "dieses kommt es gleich schon am Anfang zu einer Fehlermeldung, weil die\n"
+ "Headerdateien nicht gefunden werden:\n"
+ "\n"
+ " gcc -c src/GtkWindow.c\n"
+ "src/GtkWindow.c:1:21: gtk/gtk.h: Datei oder Verzeichnis nicht gefunden\n"
+ "src/GtkWindow.c: In function `main':\n"
+ "src/GtkWindow.c:6: error: `GtkWidget' undeclared (first use in this function)\n"
+ "src/GtkWindow.c:6: error: (Each undeclared identifier is reported only once\n"
+ "src/GtkWindow.c:6: error: for each function it appears in.)\n"
+ "src/GtkWindow.c:6: error: `fenster' undeclared (first use in this function)\n"
+ "src/GtkWindow.c:6: error: `GTK_WINDOW_TOPLEVEL' undeclared (first use in this function)\n"
+ "sep@pc305-3:~/fh/c/tutor>]]> \n"
+ "\n"
+ "\n"
+ "Tatsächlich beinhaltet die Bibliothek GTK sehr viele Headerdateien\n"
+ "und inkludiert weitere Subbibliotheken. Diese liegen aufgrund der Anzahl nicht\n"
+ "direkt in den Standardpfaden des Compilers. Daher sind sehr viele Optionen\n"
+ "anzugeben, die dem Compiler sagen, wo er überall Headerdateien findet. Da im\n"
+ "Falle der Bibliothek GTK diese unterschiedlichen Inkludierungsordner\n"
+ "so viele sind, das niemand diese alle im Kopf behalten kann, bedient sich die\n"
+ "Bibliothek eines kleinen Tools, das die Information verwaltet. Dieses Tool\n"
+ "heißt: gtk-config. Dieses kann mit der Option --cflags nach \n"
+ "den zusätzlichen Optionen für den Compiler gefragt werden und \n"
+ "mit --libs nach den zusätzlichen Optionen für den Linkvorgang geragt \n"
+ "werden.\n"
+ "\n"
+ "Für die Kompilerflags gibt es dabei z.B. die folgende Ausgabe:\n"
+ "\n"
+ " gtk-config --cflags\n"
+ "-I/opt/gnome/include -I/opt/gnome/include/gtk-1.2\n"
+ "-I/opt/gnome/include/glib-1.2 \n"
+ "-I/opt/gnome/lib/glib/include -I/usr/X11R6/include\n"
+ "]]>\n"
+ "\n"
+ "Für die Linkerflags die nachfolgende Ausgabe:\n"
+ " gtk-config --libs\n"
+ "-L/opt/gnome/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic -lgmodule -lglib \n"
+ "-ldl -lXi -lXext -lX11 -lm]]>\n"
+ "\n"
+ "\n"
+ "Diese Flags sind nun eigentlich dem Compiler beim Übersetzen und beim Linken\n"
+ "jeweils anzugeben. Auf Unix-artigen Betriebssystemen gibt es eine schöne\n"
+ "Möglichkeit auf der Kommandozeile, den Aufruf an gtk-config im Aufruf\n"
+ "des Übersetzers zu integrieren. Hierzu ist der Aufruf in rückwertige einfache\n"
+ "Anführungszeichen einzuschließen, wie in den folgenden zwei Aufrufe zu sehen\n"
+ "ist. \n"
+ "\n"
+ " gcc `gtk-config --cflags` -c src/GtkWindow.c\n"
+ "sep@pc305-3:~/fh/c/tutor> gcc -o GtkWindow `gtk-config --libs` GtkWindow.o]]>\n"
+ "\n"
+ "\n"
+ "Beispielhaft soll im folgenden eine kleine eigene Gui-Komponente erzeugt\n"
+ "werden, die ein Eingabetextfeld, ein Ausgabetextfeld und einen Knopf\n"
+ "enthält. Beim Drücken des Knopfes soll der Text des Eingabefeldes als Eingabe\n"
+ "einer Funtion genutzt werden. Diese Funktion wird als Funktionszeiger\n"
+ "gespeichert. Das Ergebnis des Funktionsaufrufs wird in Ausgabefeld angezeigt.\n"
+ "\n"
+ "Hierzu definieren wir eine Struktur für derartige Dialogobjekte. Diese\n"
+ "Struktur enthalte die drei oben genannten Bestandteile, den benötigten\n"
+ "Funktionszeiger und zusätzlich ein GtkVBox-Objekt, das die drei\n"
+ "Gui-Komponenten des Dialogs zusammenfasst:\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "typedef struct {\n"
+ " GtkVBox super;\n"
+ " GtkEntry* eingabe;\n"
+ " GtkButton* okKnopf;\n"
+ " GtkLabel* ausgabe;\n"
+ " char* (*f)(char*);\n"
+ "} GtkDialogue;\n"
+ "\n"
+ "GtkWidget* dialogue_new(char* f(char*));\n"
+ "\n"
+ "#endif]]>\n"
+ "\n"
+ "Es folgt die Implementierung der Klasse Dialogue. Im Rahmen dieser\n"
+ "Vorlesung soll nicht weiter auf diese Implementierung eingegangen werden.\n"
+ "\n"
+ "\n"
+ "#include \"StringLength.h\"\n"
+ "\n"
+ "void reagiere(GtkObject* object){\n"
+ " GtkDialogue* dialogue = (GtkDialogue*)object;\n"
+ " char* eingabeText = gtk_entry_get_text(dialogue->eingabe);\n"
+ " char* ausgabeText = dialogue->f(eingabeText);\n"
+ " gtk_label_set_text(dialogue->ausgabe,ausgabeText);\n"
+ " free(ausgabeText);\n"
+ "}\n"
+ "\n"
+ "void addToBox(GtkBox* box,GtkWidget* item){\n"
+ " gtk_box_pack_start(box,GTK_WIDGET(item), FALSE, FALSE, 0);\n"
+ "}\n"
+ "\n"
+ "GtkWidget* dialogue_new(char* f(char*)){\n"
+ " GtkDialogue* dialogue = (GtkDialogue*) malloc(sizeof(GtkDialogue));\n"
+ "\n"
+ " GtkVBox* tmp = GTK_VBOX(gtk_vbox_new (FALSE, 0));\n"
+ " dialogue->super = *tmp;\n"
+ " gtk_widget_destroy(GTK_WIDGET(tmp));\n"
+ " \n"
+ " dialogue->f = f;\n"
+ " dialogue->eingabe = (GtkEntry*)gtk_entry_new();\n"
+ " addToBox(GTK_BOX(dialogue), GTK_WIDGET(dialogue->eingabe));\n"
+ "\n"
+ " dialogue->okKnopf = (GtkButton*)gtk_button_new_with_label(\"Run\");\n"
+ " addToBox(GTK_BOX(dialogue), GTK_WIDGET(dialogue->okKnopf));\n"
+ "\n"
+ " dialogue->ausgabe = (GtkLabel*)gtk_label_new(\"\");\n"
+ " addToBox(GTK_BOX(dialogue), GTK_WIDGET(dialogue->ausgabe));\n"
+ "\n"
+ " gtk_signal_connect_object\n"
+ " (GTK_OBJECT(dialogue->okKnopf), \"clicked\"\n"
+ " ,GTK_SIGNAL_FUNC(reagiere) , GTK_OBJECT(dialogue));\n"
+ "\n"
+ " return GTK_WIDGET(dialogue);\n"
+ "}]]>\n"
+ "\n"
+ "Die somit entstandene Komponente von einfachen Dialogfenster ist nun recht\n"
+ "einfach zu benutzen. Hierzu braucht nur die Funktion angegeben zu werden, die\n"
+ "ausgeführt wird, wenn auf dem Knopf gedrückt wird. In unserem Beispiel soll\n"
+ "dabei der Eingabetext in Großbuchstaben umgewandelt werden.\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "#include \"Dialogue.h\"\n"
+ "\n"
+ "unsigned int length(char* text){\n"
+ " unsigned int i;for (i=0;*(text+i)!='\\0';i++){}return i;\n"
+ "}\n"
+ "\n"
+ "#define LOWER_TO_UPPER 'A'-'a';\n"
+ "\n"
+ "char charToUpper(char c){\n"
+ " if (c>='a' && c <= 'z') return c+LOWER_TO_UPPER;\n"
+ " return c;\n"
+ "} \n"
+ "\n"
+ "char* toUpper(char* text){\n"
+ " char* result = malloc((length(text)+1)*sizeof(char));\n"
+ " int i=0;\n"
+ " for(;text[i]!='\\0';i++) result[i]=charToUpper(text[i]);\n"
+ " result[i]='\\0';\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "int main(int argc, char **argv){\n"
+ " gtk_init(&argc, &argv);\n"
+ " GtkWidget* fenster = gtk_window_new (GTK_WINDOW_TOPLEVEL);\n"
+ " \n"
+ " GtkWidget* dialogue = dialogue_new(toUpper);\n"
+ " gtk_container_add (GTK_CONTAINER(fenster), dialogue);\n"
+ "\n"
+ " gtk_widget_show_all(fenster);\n"
+ " gtk_main();\n"
+ " return 0;\n"
+ "}]]>\n"
+ "\n"
+ "\n"
+ "Klassischer Weise sind GUI-Bibliotheken in einer objektorientierten\n"
+ "Programmiesprache geschrieben, da sich die graphischen Komponenten sehr\n"
+ "anschaulich als abgeschlossene eigenständige Objekte betrachten\n"
+ "lassen. Tatsächlich hält die Bibliothek Gtk sich weitgehendst an einen\n"
+ "objektorientierten Entwurf. Im nächsten Semester werden wir in\n"
+ "GUI-Bibliohteken für C++ kennenlernen.\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Hiermit endet das erste Semester.\n"
+ "\n"
+ "Ein paar einzelne kleinere Themen sind im Laufe der Vorlesung schlichtweg\n"
+ "vergessen worden oder absichtlich übergangen worden. In diesem Kapitel werden\n"
+ "diese Themen kurz an Beispielen angesprochen.\n"
+ "\n"
+ "\n"
+ "C Programmieren sind geradezu verliebt in den Präprozessor. Er wird nicht nur\n"
+ "zum Deklarieren von Konstanten und Inkludieren von Kopfdateien benutzt,\n"
+ "sondern für noch recht unterschiedliche Aufgaben ge-(miß-)braucht.\n"
+ "\n"
+ "\n"
+ "Ein Problem der Programmiersprache C ist, dass die Kommentare nicht\n"
+ "verschachtelt möglich sind. Die Zeichenfolge */ beendet einen\n"
+ "Kommentarblock. Nach dieser Zeichenfolde werden die nachfolgenden Zeichen\n"
+ "garantiert als Code und nicht als Kommentar betrachtet. Dieses ist\n"
+ "unabhängig davon wie of zuvor die Zeichenfolge /*, die den Kommentar\n"
+ "eröffnet, aufgetreten ist.\n"
+ "\n"
+ "Dieses führt dazu, dass man nicht beliebigen Code, schnell einmal\n"
+ "auskommentieren kann, wenn innerhalb dieses Codes bereits \n"
+ "Kommentare stehen. Hierzu\n"
+ "betrachte man folgendes Codefragment:\n"
+ " /* Dieser Code soll auskommentiert werden */\n"
+ " if (a != 0) {\n"
+ " b = 0; /* setze b auf 0 */\n"
+ " }\n"
+ "\n"
+ "Schließt man diesen Code nun komplett in einen Kommentar ein, so endet der\n"
+ "Kommentar sobald die Zeichenkette */ auftritt, also schon am Ende des\n"
+ "ersten Kommentars.\n"
+ "\n"
+ "/*\n"
+ " /* Dieser Code soll auskommentiert werden */\n"
+ " if (a != 0) {\n"
+ " b = 0; /* setze b auf 0 */\n"
+ " }\n"
+ "*/\n"
+ "\n"
+ "Mit der if-Anweisung des Präprozessors, kann man Blöcke unabhängig\n"
+ "von ihrer C-Bedeutung komplett aus der Übersetzung \n"
+ "ausschließen: #if 0 bedeutet, das der nun folgende Text vom\n"
+ "Präprozessor nicht zu berücksichtigen ist. Die so eingeleitete Kommentierung\n"
+ "endet mit #endif.\n"
+ "\n"
+ "So läßt sich der obige Code wie folgt von der Kompilierung ausschließen:\n"
+ "\n"
+ "#if 0 \n"
+ " /* Dieser Code soll auskommentiert werden */\n"
+ " if (a != 0) {\n"
+ " b = 0; /* setze b auf 0 */\n"
+ " }\n"
+ "#endif\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "Artikel .\n"
+ "\n"
+ "\n"
+ "\n"
+ "double power1(double x,unsigned int e){\n"
+ " double result=1;\n"
+ " while (e>0){\n"
+ " result=result*x;\n"
+ " e=e-1;\n"
+ " }\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "double power2(double x,unsigned int e){\n"
+ " double result=1;\n"
+ " startWhile: \n"
+ " if (e==0) goto endWhile;\n"
+ " result=result*x;\n"
+ " e=e-1;\n"
+ " goto startWhile;\n"
+ " endWhile:\n"
+ " return result;\n"
+ "}\n"
+ "\n"
+ "\n"
+ "int main(){\n"
+ " printf(\"%f\n\",power2(2,10));\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "In computer science and related disciplines, considered \n"
+ "harmful is a phrase\n"
+ "popularly used in the titles of diatribes and other critical essays. It\n"
+ "originates with Edsger Dijkstra's letter Go To Statement Considered\n"
+ "Harmful, published in the \n"
+ "March 1968 Communications of the ACM, in which\n"
+ "he criticized the excessive use of the GOTO statement in programming languages\n"
+ "of the day and advocated structured programming instead. The original title of\n"
+ "the letter, as submitted to ACM was A Case Against the Goto \n"
+ "Statement, but\n"
+ "ACM editor, Niklaus Wirth changed the title to the now immortalized Go To\n"
+ "Statement Considered Harmful. \n"
+ "\n"
+ "Frank Rubin published a criticism of Dijkstra's letter in the March \n"
+ "1987 Communications of the ACM \n"
+ "titled ``GOTO Considered Harmful'' Considered Harmful. Donald Moore et\n"
+ "al. published \n"
+ "a reply in the May 1987 CACM titled ` ``GOTO Considered \n"
+ "Harmful'' Considered Harmful' Considered Harmful?. (Dijkstra's \n"
+ "own response to this controversy was thankfully \n"
+ "titled On a somewhat disappointing correspondence.)\n"
+ "\n"
+ "Some variants with replacement adjectives (considered silly, \n"
+ "etc.) have been noted in hacker jargon. The phrase is also occasionally\n"
+ "capitalized for effect. \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "void printNTimes(char* text,int i){\n"
+ " if (i==0) return;\n"
+ " printf(\"%s\n\",text);\n"
+ " printNTimes(text,i-1);\n"
+ "}\n"
+ "\n"
+ "void printNTimesOpt(char* text,int i){\n"
+ " start:\n"
+ " if (i==0) return;\n"
+ " printf(\"%s\n\",text);\n"
+ " \n"
+ " text=text;\n"
+ " i=i-1;\n"
+ " goto start;\n"
+ "}\n"
+ "\n"
+ "int main(){\n"
+ " printNTimes(\"hallo\",5);\n"
+ " printNTimesOpt(\"hallo\",5);\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "#include \n"
+ "\n"
+ "int laenge(PList xs){\n"
+ " if (isEmpty(xs)) return 0;\n"
+ " return 1+laenge(xs->tail);\n"
+ "}\n"
+ "\n"
+ "int lengthAcc(int result,PList xs){\n"
+ " if (isEmpty(xs)) return result;\n"
+ " return lengthAcc(result+1,xs->tail);\n"
+ "}\n"
+ "\n"
+ "int lengthAccOpt(int result,PList xs){\n"
+ " start:\n"
+ " if (isEmpty(xs)) return result;\n"
+ " result=result+1;\n"
+ " xs=xs->tail;\n"
+ " goto start;\n"
+ "}\n"
+ "\n"
+ "int size(PList xs){return lengthAccOpt(0,xs);}\n"
+ "\n"
+ "int main(){\n"
+ " int x=42;\n"
+ " PList xs=cons(&x,cons(&x,cons(&x,cons(&x,cons(&x,nil())))));\n"
+ " printf(\"%i\n\",laenge(xs));\n"
+ " printf(\"%i\n\",lengthAcc(0,xs));\n"
+ " printf(\"%i\n\",lengthAccOpt(0,xs));\n"
+ " printf(\"%i\n\",size(xs));\n"
+ " return 0;\n"
+ "}\n"
+ "]]>\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "
\n"
+ "OperatorBeschreibung\n"
+ "()Gruppierung und Funktionsanwendung\n"
+ "[]Array Index\n"
+ ".Auswahl von Objekteigenschaft\n"
+ "->Auswahl von Objekteigenschaft über Zeiger\n"
+ "++~--einstelliges Increment und Decrement\n"
+ "+~-einstelliges plus und minus\n"
+ "!~~logische Negation, bitweises Komplement\n"
+ "(type)Typkonversion\n"
+ "*Dereferenzierung\n"
+ "&Adressoperator\n"
+ "sizeofBerechnung der Länge in Bytes\n"
+ "*~/~%Multiplikation,\n"
+ "Division, Modulo\n"
+ "+~-Addition, Subtraktion\n"
+ "<<~>>bitweise links-/Rechts-Shift\n"
+ "<~<=kleiner- und\n"
+ "kleiner-gleich-Relation\n"
+ ">~>=größer- und größer-gleich-Relation\n"
+ "==~!=gleich- und ungleich-Relation\n"
+ "&bitweises Und\n"
+ "^bitweises exklusives\n"
+ "Oder\n"
+ "|bitweises inklusives Oder\n"
+ "&&logisches Und\n"
+ "||logisches Oder\n"
+ "?:dreistelliger Bedingungsoperator\n"
+ "=Zuweisung\n"
+ "+=~-=Additions-/Subtraktions-Zuweisung\n"
+ "*=~/=Multiplikations-/Divisions-Zuweisung\n"
+ "%=~&=Modulo-/bitweise-Und-Zuweisung\n"
+ "^=~|=bitweise\n"
+ "exklusive/inklusive Oder-Zuweisung\n"
+ "<<=~>>=Bitshift-Zuweisung\n"
+ ",Folge von Ausdrücken\n"
+ "
\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "\n"
+ "";
String skript = skript1+skript3+skript4+skript5+skript2+skript6+skript7+skript8+skript9;
static void parse( String s, DefaultHandler handler) throws Exception {
SAXParserFactory
.newInstance()
.newSAXParser()
.parse(new InputSource(new StringReader(s)), handler);
}
@Before
public void setUp() throws Exception {
}
@Test
public void test1() {
handler = new GetCode("c", "Li");
try {
parse(skript, handler);
} catch (Exception e) {
e.printStackTrace();
}
assertEquals("Falsch extrahiert. class=\"Li\" existiert nicht in code Tag.","", handler.result.toString());
}
@Test
public void test2() {
handler = new GetCode("c", "VoidPointer");
try {
parse(skript, handler);
} catch (Exception e) {
e.printStackTrace();
}
String result ="#include \n"
+ "\n"
+ "typedef struct {\n"
+ " int x;\n"
+ " int y;\n"
+ "} Punkt;int main(){\n"
+ " void* object; int x = 17;\n"
+ " object = &x; *(int*)object += 4; \n"
+ " *(int*)object *= 2; \n"
+ " printf(\"x = %i\\n\",x); Punkt p = {2,6};\n"
+ " object = &p;\n"
+ "\n"
+ " printf(\"p.x: %i\\n\",p.x);\n"
+ " printf(\"((Punkt*)object)->x: %i\\n\",((Punkt*)object)->x);\n"
+ "\n"
+ " return 0;\n"
+ "}";
assertEquals("Falsch extrahiert. class=\"VoidPointer\" mit lang=\"c\" wurde gesucht. Findet sich in mehreren code Tags.", result, handler.result.toString());
}
@Test
public void test3() {
handler = new GetCode("h", "Answer4");
try {
parse(skript, handler);
} catch (Exception e) {
e.printStackTrace();
}
String result ="#include \n"
+ "\n"
+ "int getAnswer();";
assertEquals("Falsch extrahiert. class=\"Answer4\" mit lang=\"h\" wurde gesucht.",result, handler.result.toString());
}
String simple = "\n"
+ "murx\n"
+ "sieb (x:xs) = x:sieb[y|y<-xs,y `mod` x /=0]\n"
+ "\n"
+ "\n"
+ "";
@Test
public void test4() {
handler = new GetCode("h", "Answer4");
try {
parse(simple, handler);
} catch (Exception e) {
e.printStackTrace();
}
assertEquals("Falsch extrahiert. class=\"Answer4\" existiert nicht in code Tag.","", handler.result.toString());
}
@Test
public void test5() {
handler = new GetCode("hs", "Era");
try {
parse(simple, handler);
} catch (Exception e) {
e.printStackTrace();
}
String result ="sieb (x:xs) = x:sieb[y|y<-xs,y `mod` x /=0]";
assertEquals("Falsch extrahiert.",result, handler.result.toString());
}
String complicated = "\n"
+ "sieb (x:xs) = x:sieb[y|y<-xs,y `mod` x /=0]";
@Test
public void test6() {
handler = new GetCode("hs", "Era");
try {
parse(complicated, handler);
} catch (Exception e) {
e.printStackTrace();
}
String result ="sieb (x:xs) = x:sieb[y|y<-xs,y `mod` x /=0]";
assertEquals("Dieses ist echt ein schwerer Test. Aus \nsieb (x:xs) = x:sieb[y|y<-xs,y `mod` x /=0]\n das Programm Era.hs extrahieren.",result, handler.result.toString());
}
}