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" + "Im Zuge dieses Kurse wollen wir zunächst einmal alle Überlegungen theoretischer\n" + "Natur auf später verschieben und direkt mit dem ersten Programm\n" + "anfangen. Hierzu betrachten wir zunächst das leere Programm, das erst einmal\n" + "nichts tut:\n" + "\n" + "int main(){\n" + " return 0;\n" + "}\n" + "\n" + "\n" + "Programme der Programmiersprache C sind in einer Textdatei zu speichern, deren\n" + "Dateiendung .c ist. Obiges Programm ist also in einer Textdatei \n" + "namens DasLeereProgramm.c zu speichern. Um Textdateien zu schreiben\n" + "und zu speichern benötigt man ein weiteres Programm, einen Editor. Es gibt viele\n" + "Editoren, mit denen man dies bewerkstelligen kann. Es reichen hierzu\n" + "primitivste Editorprogramme. Unter dem Windows-Betriebssysteme findet sich in\n" + "der\n" + "Regel das Editorprogramm notepad. Ein schwer zu bedienender jedoch\n" + "auch sehr mächtiger und auf allen Plattformen verfügbarer Editor ist das\n" + "Programm vi. Auf Linux-Betriebssystemen findet sich zumeist das\n" + "Editorprogramm kate. Ich persönlich benutze seit Jahrzenten das\n" + "Editorprogramm emacs. Wählen Sie auf Ihrer Platform einen einfachen\n" + "Texteditor und schreiben und speichern mit ihm das obige \n" + "Programm: DasLeereProgramm.c.\n" + "\n" + "\n" + "Obwohl das Programm nichts tut, besteht es schon immerhin aus drei Zeilen. Man\n" + "hätte das Programm auch in einer Zeile schreiben können als:\n" + "\n" + "int main(){return 0;}\n" + "\n" + "Ob und wo man Zeilenumbrüche macht, ist im Prinzip für die Programmiersprache\n" + "C egalEs gibt tatsächlich Programmiersprachen, in denen der Beginn\n" + "einer neuen Zeile eine Bedeutung hat, in C aber nicht.. Trotzdem hat man sich auf\n" + "Konventionen verständigt, wann man eine neue Zeile beginnt und wann man eine\n" + "Zeile einrückt. Diese Konventionen erlauben es einen menschlichen Leser\n" + "Programme besser zu verstehen. Deshalb ist das Beachten solcher Konventionen in den meisten\n" + "Firmen Pflicht; und auch in unserem Programmierkurs werden wir ein paar wenige\n" + "Konventionen verbindlich machen. Die erste Konvention lautet:\n" + "\n" + "Wann immer die Programmiersprache C verlangt, das Code zwischen einer\n" + "öffnenden und einer schließenden geschweifte Klammer zu stehen hat, ist der Code\n" + "zwischen den Klammern ein Stückchen weiter einzurücken. \n" + "Nach der öffnenden Klammer ist eine neue\n" + "Zeile zu beginnen. Die schließende Klammer steht auf einer neuen Zeile.\n" + "\n" + "In wieweit eingerückt wird, ist einheitlich zu handhaben.\n" + " \n" + "\n" + "Die Programme dieses Skriptes halten sich weitgehendst \n" + "an diese Regel. Es wird dabei\n" + "jeweils um zwei Leerzeichen eingerückt.Es wird bei den\n" + "Beispielprogrammen bei Einzeilern davon abgewichen.\n" + "\n" + "\n" + "Das Programm AuchDasLeereProgramm.c verletzt diese\n" + "Konvention.Und würde bei einer Benotung deshalb Abzüge bekommen.\n" + "\n" + "\n" + "\n" + "Auch wenn die beiden obigen Programme noch nichts tun, wollen wir sie einmal\n" + "ausführen. Um ein Programm auf dem Computer ausführen zu können, muss es\n" + "in eine Form übersetzt werden, die der Computer versteht. Der Programmtext,\n" + "wie er mit dem Texteditor geschrieben wurde, kann nicht direkt vom Computer\n" + "ausgeführt werden. Er ist erst in eine Folge von Nullen und Einsen zu\n" + "übersetzen, die vom Betriebssystem aus gestartet und als Prozessorbefehle\n" + "direkt den Prozessor des Computers zum Ausführen gegeben werden können. Das\n" + "Übersetzen eines Programmtextes in ein ausführbares Programm unternimmt ein\n" + "weiteres Programm, der Übersetzer für eine bestimmte\n" + "Programmiersprache. Dieser wird auf \n" + "Englisch compilerCompiler bezeichnet. Auf\n" + "Deutsch wird eher von einem Übersetzer als von \n" + "einem Kompilierer gesprochen.Während sich im Französischen\n" + "das Wort compilateur durchgesetzt hat. Meist benutzt man\n" + "auf Deutsch auch das englische Wort compiler.\n" + "\n" + "\n" + "Wir müssen also unseren obigen Programmtext, den wir mit einem Texteditor\n" + "geschrieben haben, einen Kompilierer für die Programmiersprache C geben, damit\n" + "wir ein ausführbares Programm erhalten. Den Programmtext den wir mit dem\n" + "Editor geschrieben haben, bezeichnen wir als Quelltext.Quelltext\n" + "\n" + "\n" + "Für die Programmiersprache C gibt es eine ganze Reihe von Kompilierern. Im\n" + "Laufe dieses Kurses werden wir den \n" + "Kompilierer gcc von gnu benutzen. Sein Vorteil ist, dass er\n" + "für unterschiedliche Rechnertypen und Betriebssysteme zur Verfügung steht. Wir\n" + "können somit unseren Quelltext mit der gcc-Version für Windows in ein\n" + "ausführbares Programm übersetzen lassen, das unter dem\n" + "Windows-Betriebssystemen läuft und den gleichen Quelltext mit \n" + "der gcc-Version für Linux in ein ausführbares Programm übersetzen\n" + "lassen, das auf dem Linux-Betriebssystem läuft. Allerdings lassen sich die\n" + "ausführbaren Programme nicht austauschen. Das Windowsprogramm kann nicht auf\n" + "Linux gestartet werden, und umgekehrt.\n" + "\n" + "\n" + "Um jetzt ein C-Programm mit dem Kompilierer gcc zu übersetzen, öffnen\n" + "wir eine Kommandozeile, gehen mit dem cd-Befehl des Betriebssystems \n" + "zum Wechseln in ein anderes Verzeichnis, in das Verzeichnis, in dem die\n" + "Quelltextdatei gespeichert wurde. Dann rufen wir dort den \n" + "Kompilierer gcc auf, geben mit -o gefolgt von einen\n" + "beliebigen Namen den Namen an, den das ausführbare Programm haben soll, und\n" + "geben schließlich noch an, welche Quelltextdatei übersetzt werden soll.\n" + "\n" + "Anschließen gibt es das erzeugte ausführbare Programm, das ausgeführt \n" + "werden kann.\n" + "\n" + "\n" + "Alles in allem erhalten wir folgende kleine Sitzung auf der Kommandozeile:\n" + "\n" + " cd fh/c/student/src\n" + "sep@pc305-3:~/fh/c/student/src> gcc -o dasLeereProgramm DasLeereProgramm.c\n" + "sep@pc305-3:~/fh/c/student/src> ./dasLeereProgramm\n" + "sep@pc305-3:~/fh/c/student/src>]]> \n" + "\n" + "Man sieht nicht sehr viel bei dieser Ausführung. Der Aufruf des Kompilierers \n" + "mit gcc -o dasLeereProgramm DasLeereProgramm.c führt zu keiner\n" + "weiteren Ausgabe auf dem Bildschirm. Das ist auch gut so, denn wenn der Aufruf\n" + "des Kompilerers zu Ausgaben auf der Kommandozeile führt, bedeutet das zumeist,\n" + "dass etwas faul ist, nämlich der Quelltext Fehler enthält, aufgrund derer der\n" + "Kompilierer kein ausführbares Programm erzeugen kann. Es ist gut sich mit\n" + "solchen Fehlermeldungen frühzeitig auseinander zu setzen, denn in der Praxis\n" + "werden wir sehr oft Fehler machen und manchmal verzweifelt nach einer Lösung\n" + "suchen. \n" + "\n" + "\n" + "
    \n" + "
  • Quelltextdatei mit Editor schreiben

  • \n" + "
  • Kompilierer (gcc) erzeugt ausführbaren Code

  • \n" + "
  • ausführbares Programm ist Hardware und Betriebssystem abhängig

  • \n" + "
\n" + "
\n" + "\n" + "Schauen wir uns einmal an, was alles passiert, wenn wir in unser erstes leeres\n" + "Programm Fehler einbauen.\n" + "\n" + "Zunächst sei die schließende Klammer am Ende des Programms vergessen: \n" + "\n" + "int main(){\n" + " return 0;\n" + "\n" + "\n" + "Versuchen wir dieses zu übersetzen, so beschwert sich der Kompilierer über\n" + "einen syntaktischen Fehler:\n" + "\n" + " gcc -o leeresProg DasFalscheLeereProgramm1.c\n" + "DasFalscheLeereProgramm1.c: In function `int main()':\n" + "DasFalscheLeereProgramm1.c:2: error: syntax error at end of input\n" + "sep@pc305-3:~/fh/c/student/src>]]>\n" + "\n" + "Ihm fehlt etwas am Ende des Programms.\n" + "\n" + "\n" + "Als nächstes sei einmal die Zeile return 0; die 0 weggelassen:\n" + "\n" + "int main(){\n" + " return;\n" + "}\n" + "\n" + "\n" + " gcc DasFalscheLeereProgramm2.c\n" + "DasFalscheLeereProgramm2.c: In function `int main()':\n" + "DasFalscheLeereProgramm2.c:2: error: return-statement with no value, in\n" + " function declared with a non-void return type\n" + "sep@pc305-3:~/fh/c/student/src>]]>\n" + "\n" + "Auch dieses führt zu einen Fehlermeldung. Diese ist für uns noch schwer zu\n" + "verstehen. Eine Funktion, die nicht void sei, soll einen Wert mit \n" + "dem return liefern. Später werden wir sehen, was \n" + "ein void-Funktion ist.\n" + "\n" + "\n" + "Ein weiterer simpler Fehler ist, das Semikolon am Ende \n" + "des return-Befehls zu vergessen\n" + "\n" + "int main(){\n" + " return 0\n" + "}\n" + "\n" + "Hier meldet uns der Compiler, dass ihm irgendetwas vor der schließenden\n" + "Klammer zu fehlen scheint.\n" + "\n" + " gcc -Wall DasFalscheLeereProgramm3.c\n" + "DasFalscheLeereProgramm3.c: In function `int main()':\n" + "DasFalscheLeereProgramm3.c:3: error: syntax error before `}' token\n" + "sep@pc305-3:~/fh/c/student/src>]]>\n" + "\n" + "Leider kommt der Compiler nicht auf die Idee, uns auf das fehlenden Semikolon\n" + "explizit hinzuweisen.\n" + "\n" + "\n" + "Eine weitere interessante Fehlermeldung erhalten wir, wenn wir mit etwas zu\n" + "viel norddeutschen Blut in den Adern, die \n" + "Funktion main zu moin umbenennen:\n" + "\n" + "int moin(){\n" + " return 0;\n" + "}\n" + "\n" + "Nun werden wir in einer Fehlermeldung auf das Nichtvorhandensein der \n" + "Funktion main aufmerksam gemacht.\n" + "\n" + " gcc -o leeresProg DasFalscheLeereProgramm4.c\n" + "/usr/lib/gcc-lib/i586-suse-linux/3.3.3/../../../crt1.o(.text+0x18): In function `_start':\n" + "../sysdeps/i386/elf/start.S:98: undefined reference to `main'\n" + "collect2: ld returned 1 exit status\n" + "sep@pc305-3:~/fh/c/student/src>]]>\n" + "\n" + "Das Interessante an dieser Fehlermeldung ist, dass sie nicht auf einen\n" + "syntaktischen Fehler hinweist, sondern auf das Fehlen einer Fuktionalität. Wie\n" + "wir später sehen werden, kommt dieser Fehler auch nicht direkt vom Compiler,\n" + "sondern einem kleinen Freund des Compilers, dem Linker.\n" + "\n" + "\n" + "\n" + "\n" + "
    \n" + "
  • braucht eine Funktion mit Namen main

  • \n" + "
  • main-Funktion beginnt mit int main()

  • \n" + "

    \n" + "

  • main-Funktion endet mit: return 0;

  • \n" + "
  • Befehle enden mit einem Semikolon

  • \n" + "
  • Konvention: tiefer Einrücken innerhalb geschweifter Klammern

  • \n" + "
  • Kompilierer liefert Fehlermeldungen

  • \n" + "
\n" + "
\n" + "\n" + "\n" + "Es ist schließlich an der Zeit auch Programme zu schreiben, die den ersten\n" + "sichtbaren Effekt erzielen. Hierzu wollen wir einen Text auf der Kommandozeile\n" + "ausgeben. Beliebige Texte können in C in Anführungszeichen eingeschlossen\n" + "geschrieben werden. Es gibt eine eingebaute Funktion \n" + "namens printf, der solche Texte übergeben werden können, damit sie\n" + "die Texte auf der Kommandozeile ausgibt. Um diese Standardfunktion benutzen zu\n" + "können, muß eine entsprechende Bibliothek für standard Ein- und Ausgabe, in\n" + "das Programm mit eingebunden werden.\n" + "\n" + "Das einfachste Programm, welches eine Ausgabe auf den Bildschirm tätigt, sieht\n" + "damit wie folgt aus:\n" + "\n" + "\n" + "int main(){\n" + " printf(\"Hallo Freunde!\");\n" + " return 0;\n" + "}]]>\n" + "\n" + "Die anfängliche Zeile #include <stdio.h> bindet die\n" + "Standardbibliothek zur Ein- und Ausgabe ein. In der \n" + "Funktion main wird vor dem Befehl return ein weiterer\n" + "Befehl ausgeführt: der Aufruf der Funktion printf, mit dem \n" + "Text Hallo Freunde! als Argument. Argumente werden Funktionen in\n" + "runden Klammern übergeben.\n" + "\n" + "Texte werden in C als Zeichenketten in Anführungszeichen eingeschlossen. Diese\n" + "Zeichenketten werden in Programmiersprachen \n" + "als String bezeichnet.Nicht zu verwechseln mit einem knappen\n" + "Teil Unterwäsche für junge Damen, wie einmal ein Student von mir \n" + "bemerkte.\n" + "\n" + "In der Regel dürfen Zeichenketten keinen Zeilenumbruch beinhalten.\n" + "Das folgende Programm führt zu einem syntaktischen Fehler.\n" + "\n" + "int main(){\n" + " printf(\"Hallo Freunde!\n" + " Hallo Illja!\");\n" + " return 0;\n" + "}]]>\n" + "\n" + "Der Compiler beschwert sich mit einer ganzen Reihe von Fehlern\n" + "\n" + "\n" + "\n" + "Will man trotzdem einen String aus Layout- oder Platzgründen auf mehrere\n" + "Zeilen verteilen, so ist am Ende der Zeile ein rückwärtiger Schrägstrich anzubringen: \n" + "\n" + "\n" + "int main(){\n" + " printf(\"Hallo Freunde!\\n" + " Hallo Illja!\");\n" + " return 0;\n" + "}]]>\n" + "\n" + "Dieses Programm übersetzt, hat aber bei der Ausgabe keinen Zeilenumbruch:\n" + "\n" + " bin/HalloIllja2\n" + "Hallo Freunde! Hallo Illja!sep@pc305-3:~/fh/c/student>]]>\n" + "\n" + "Möchte man, das in einer Zeichenkette ein Zeilenumbruch auftritt, so ist\n" + "dieses mit einer sogenannten Fluchtsequenz zu markieren. Fluchtsequenzen\n" + "bestehen in einem String aus einem rückwärtigen Schrägstrich und einem\n" + "Buchstaben. Für einen Zeilenumbruch ist es die Sequenz \n. Um eine\n" + "Ausgabe mit Zeilenumbrüchen zu erzielen, ist also in einem String die\n" + "entsprechende Fluchtsequenz einzufügen:\n" + "\n" + "\n" + "\n" + "int main(){\n" + " printf(\"Hallo Freunde!\nHallo Illja!\n\");\n" + " return 0;\n" + "}]]>\n" + "\n" + "Dieses Programm gibt dann die folgende Ausgabe:\n" + " bin/HalloIllja3\n" + "Hallo Freunde!\n" + "Hallo Illja!\n" + "sep@pc305-3:~/fh/c/student>]]>\n" + "\n" + "Wir haben bisher die Funktion printf immer genau einmal\n" + "aufgerufen. In einem C Programm können mehrere Befehle nacheinander folgen und\n" + "werden dann von oben nach unten abgearbeitet.\n" + "\n" + "\n" + "int main(){\n" + " printf(\"Hallo Freunde!\n\");\n" + " printf(\"Hallo Illja!\n\");\n" + " printf(\"Licht aus!\n\");\n" + " printf(\"Spot an!\n\");\n" + " return 0;\n" + "}]]>\n" + "\n" + "\n" + "Bisher haben wir nur Zeichenketten in Form eines Textes auf der Konsole\n" + "ausgegeben. Die Funktion printf in C ist ein ziemlich vertrackter und\n" + "mächtiger Befehl. Um eine ganze Zahl mit ihr ausgeben zu können, übergebe man\n" + "der Funktion zwei Argumente: zunächst eine Zeichenkette, in der mit den \n" + "Folge %i markiert wird, wo die ganze Zahl stehen soll, und dann nach\n" + "einem Komma, die Zahl die auszugeben ist.\n" + "\n" + "\n" + "int main(){\n" + " printf(\"Die Antwort lautet: \");\n" + " printf(\"%i\",42);\n" + " return 0;\n" + "}]]>\n" + "\n" + "Dieses Spielchen läßt sich beliebig weitertreiben und so in einer Zeile mehrere\n" + "Zahlen innerhalb eines Textes integriert ausgeben. \n" + "\n" + "\n" + "int main(){\n" + " printf(\"Die Antwort: %i wenn man %i und %i verdoppelt.\n\",42,17,4);\n" + " return 0;\n" + "}]]>\n" + "
\n" + "\n" + "\n" + "\n" + "
  • inkludieren der Standardbibliothek für Ein-/Ausgabe

    \n" + "]]>

\n" + "
  • printf -Befehl zur Ausgabe auf Kommandozeile

    \n" + "\n" + "

\n" + "
  • mit %i können Zahlen auf Kommandozeile ausgegeben werden

    \n" + "

\n" + "
\n" + "\n" + "\n" + "Nachdem wir nun einen Fuß in der Tür haben, werden wir in unserer Vorlesung in\n" + "Kapitel~ die Grundbausteine zur\n" + "Strukturierung von Programmen kennenlernen: Ausdrücke, Funktionen und\n" + "zusammengesetzte Befehle. Anschließend \n" + "in Kapitel~ wird es dann Zeit, \n" + "sich\n" + "einige Gedanken über die Softwareentwicklung zu machen. \n" + "Kapitel~ ist dann vollständig Daten gewidmet. In\n" + "weiteren Kapiteln werden dann die gelernten Konzepte an Beispielprogrammen und\n" + "durch Benutzung von Bibliotheken vertieft.\n" + "\n" + "\n" + "Stellen Sie sich den anderen Kommilitonen und dem Dozenten Ihrer\n" + "Praktikumsstunde vor. Nennen Sie dabei grob Ihre Vorkenntnisse und äußern Sie\n" + "Ihre Erwartungen an die Veranstaltung.\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "Starten Sie den C-Compiler mit dem Befehl gcc --version auf der\n" + "Kommandozeile. \n" + "\n" + "\n" + "\n" + "Compilieren und starten Sie die Programme aus Kapitel~1.\n" + "\n" + "\n" + "Schreiben \n" + "Sie ein erstes eigenes C Programm, das Ihren Namen auf der\n" + "Kommandozeile ausgibt. Übersetzen Sie das Programm auf der Kommandozeile und\n" + "führen Sie es aus.\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" + " Typname\n" + " Länge\n" + " Wertebereich\n" + "\n" + "\n" + " byte\n" + " 8 Bit \n" + " -128=-27 bis 127=27-1\n" + " \n" + "\n" + "\n" + " short\n" + " 16 Bit\n" + " -32768=-215 bis 32767=215-1\n" + " \n" + "\n" + "\n" + " int\n" + " 32 Bit\n" + " -2147483648=-231 bis 2147483647=232-1\n" + " \n" + "\n" + "\n" + " long\n" + " 64 Bit\n" + " -9223372036854775808 bis 9223372036854775807\n" + " \n" + "\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(x0,x1,,,xl-1,l,op,st) = stopx0opx1op\n" + "x2op opxl-1\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: List boolean\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" + "expr\n" + "expr+zahl\n" + "expr+zahl+zahl\n" + "expr+zahl+zahl+zahl\n" + "expr+zahl+zahl+zahl+zahl\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" + ". a moon orbits mars\n" + "(shift) a . moon orbits mars\n" + "(reduce) article . moon orbits mars\n" + "(shift) article moon . orbits mars\n" + "(reduce) article noun . orbits mars\n" + "(reduce) noun-phrase . orbits mars\n" + "(shift) noun-phrase orbits . mars\n" + "(reduce) noun-phrase verb . mars\n" + "(shift) noun-phrase verb mars .\n" + "(reduce) noun-phrase verb noun .\n" + "(reduce) noun-phrase verb noun-phrase .\n" + "(reduce) noun-phrase verb-phrase .\n" + "(reduce) start.\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()); } }