Beruflich Dokumente
Kultur Dokumente
GDI01
$
GDI01
Werden Personenbezeichnungen aus Gründen der besseren Lesbarkeit nur in der männlichen oder
weiblichen Form verwendet, so schließt dies das jeweils andere Geschlecht mit ein.
Falls wir in unseren Studienheften auf Seiten im Internet verweisen, haben wir diese nach sorgfältigen
Erwägungen ausgewählt. Auf die zukünftige Gestaltung und den Inhalt der Seiten haben wir jedoch
keinen Einfluss. Wir distanzieren uns daher ausdrücklich von diesen Seiten, soweit darin rechtswid-
rige, insbesondere jugendgefährdende oder verfassungsfeindliche Inhalte zutage treten sollten.
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
1219K09
Vorwort ....................................................................................................................... 1
3 Rechner ..................................................................................................................... 36
3.1 Die Analytische Maschine ......................................................................... 36
3.2 Die Turing-Maschine .................................................................................. 38
3.3 Die Von-Neumann-Architektur ................................................................. 42
3.4 Moderne Programmiersprachen und Betriebssysteme ............................ 46
4 Dualzahlen ................................................................................................................ 50
4.1 Zahlensysteme ............................................................................................. 50
4.2 Umrechnung ................................................................................................ 52
4.3 Addition und Subtraktion .......................................................................... 55
4.4 Hexadezimalzahlen und Oktalzahlen ....................................................... 60
4.5 ASCII-Code und Unicode ........................................................................... 63
Anhang
A. Lösungen der Übungen im Text ................................................................. 77
B. Literaturverzeichnis .................................................................................... 89
C. Abbildungsverzeichnis ............................................................................... 90
D. Tabellenverzeichnis ..................................................................................... 91
E. Sachwortverzeichnis ................................................................................... 92
F. Einsendeaufgabe Typ A .............................................................................. 95
1219K09
GDI01
© HfB, 02.12.20, Berk, Eric (904709)
GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
GDI01Einführung in die Informatik1219K09
Welche Ausbildung auch immer Sie durchlaufen, für welchen Studiengang Sie sich auch
entschieden haben, Sie werden in jedem Fall in mehr oder weniger starkem Ausmaß mit
Computern und daher auch mit Informatik zu tun haben. Nun ist aber die Informatik
ein Fach mit vielen Einzeldisziplinen, und es kann daher nicht schaden, sich erst einmal
einen Überblick darüber zu verschaffen, was man eigentlich unter Informatik versteht
und welche Fragestellungen dabei auftreten können, bevor man sich in die Tiefen der
Einzelheiten begibt.
Diesem Zweck dient genau das Studienheft, das Sie gerade in der Hand halten. Sie wer-
den zunächst das Grundprinzip der Informatik kennenlernen und sich mit dem Algo-
rithmenbegriff beschäftigen. Diese Grundbegriffe werden dann konkretisiert, indem Sie
sich mit zwei grundlegenden Ideen der Programmierung befassen, nämlich der struktu-
rierten und der objektorientierten Programmierung, und sich Gedanken machen über
die Frage, wie komplex Algorithmen werden können. Dann gehen wir zu konkreten
Rechnern über: Sie werden etwas darüber lernen, wie man sich den inneren Aufbau frü-
herer Computer vorgestellt hat und wie sehr diese Vorstellungen noch heute nachwir-
ken.
Da sämtliche Operationen innerhalb eines Computers auf dem dualen Zahlensystem be-
ruhen, bleibt es nicht aus, dass Sie einiges über den Umgang mit Dualzahlen und ihre
Verwendung in einem Rechner lernen, und am Ende werden Sie sehen, wie man diese
abstrakten Kenntnisse in konkrete logische Schaltungen bis hin zum Volladdierer um-
setzt. Kurz gesagt: Wir werden einen Weg gehen vom allgemeinen Begriff der Informa-
tik bis zur technischen Umsetzung innerhalb des Rechners.
Beim Bearbeiten dieses Studienhefts wünschen wir Ihnen viel Spaß und Erfolg!
Ihre Studienleitung
GDI01 1
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
2 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Wenn Sie sich mit Informatik beschäftigen wollen, dann sollten Sie eine Vorstellung da-
von haben, was so ein Computer kann und was er nicht kann, und Sie sollten ihn weder
über- noch unterschätzen. Beides habe ich allerdings immer wieder erlebt. Das vielleicht
interessanteste Beispiel hat mir vor vielen Jahren eine Informatikstudentin des ersten Se-
mesters geliefert, die sich mit der Aufgabe plagte, die bekannte und beliebte p,q-Formel
zur Lösung quadratischer Gleichungen in der Programmiersprache Pascal zu program-
mieren. Nun will ich Ihnen nicht gleich mit Formeln auf die Nerven fallen und nur sa-
gen, dass man bei dieser Formel aus den beiden gegebenen Werten p und q die beiden
Lösungen einer quadratischen Gleichung ausrechnen kann. Das zu programmieren hat
die Studentin dann auch versucht; ihr Programm sorgte dafür, dass der Benutzer die
Zahlen p und q eingab, das war in Ordnung. Und dann ließ sie den armen Benutzer auch
gleich noch die Lösung eingeben und war der Meinung, das Problem wäre jetzt gelöst.
Vielleicht haben Sie schon einmal das Wort Softwarekrise gehört. Damit bezeichnet
man eine Phase etwa in den 1960er Jahren, in der die Informatiker merkten, dass plan-
loses Vor-sich-hin-Programmieren ziemlich schnell ins Chaos führt und man etwas ko-
ordinierter vorgehen sollte. Diese Softwarekrise fand ihre endgültige Lösung in dem
dreizeiligen Programm meiner Studentin, denn wenn man das gewünschte Ergebnis
gleich selbst eingibt, dann kann das Programm selbst nichts mehr falsch machen ... Aber
wieder im Ernst: Indem sie die Möglichkeiten des Computers gleichzeitig unterschätzte
und überschätzte, hatte sie einen doppelten Anfängerfehler gemacht, den ich Ihnen ger-
ne ersparen möchte. Die Überschätzung lag darin, dass ihr nicht klar war, wie genau
man dem Rechner mitteilen muss, was er im Einzelnen zu tun hat – wenn Sie dem Com-
puter kein genaues Rechenverfahren angeben, wird er entweder gar nichts oder nur Un-
sinn ausrechnen. Und weil die Studentin anscheinend ihrem eigenen Optimismus dann
doch nicht mehr so traute, ging sie gleich zu einer folgenschweren Unterschätzung über:
Man kann ja über Computer denken, was man will, aber wenn man ihnen die Ergebnisse
selbst eingeben müsste, könnte man sich auch den Strom sparen, mit dem man sie be-
treibt. Übrigens hatte sie dann auch noch vergessen, die Ergebnisse am Bildschirm aus-
zugeben, aber ich will hier nicht kleinlich werden.
So etwas findet man häufiger, vom Studenten des ersten Semesters bis hin zu Chefpro-
grammierern großer Firmen. Der Grund für solche Fehleinschätzungen liegt wohl oft
darin, dass die Leute nichts über das Innenleben von Computern wissen und keine Vor-
stellung davon haben, wie wichtig und unverzichtbar Algorithmen und Programme für
den erfolgreichen Einsatz von Computern sind. Damit Ihnen nicht das Gleiche passiert,
will ich in diesem Kapitel über Algorithmen reden, über Programme und über Soft-
ware. Zuerst sollten wir uns darüber verständigen, was man eigentlich mit Computern
anstellt und wie man davon ausgehend den Begriff Informatik verstehen kann.
GDI01 3
© HfB, 02.12.20, Berk, Eric (904709)
4 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Die Situation verändert sich, wenn der Chef sich endlich einen Ruck gibt und den einen
oder anderen Computer einschließlich der nötigen Programme anschafft – vornehm
ausgedrückt spricht man allerdings nicht von Programmen, sondern von Software. Wie
sieht jetzt die Lohnbuchhaltung aus? Die Anwesenheitsdaten der Mitarbeiter werden
jetzt vermutlich maschinell erfasst, zum Beispiel von Eingabestationen am Eingangstor
zum Firmengelände, und automatisch an einen zentralen Computer weitergegeben. Das
kann vollautomatisch geschehen, sofern die Eingabestationen mit dem Zentralrechner
vernetzt sind. Es kann aber auch noch menschliche Hilfe gebraucht werden, indem ein
Mitarbeiter die Daten von der Eingabestation holt und dann zu Fuß zum Computer trägt
– das hängt von der eingesetzten Technik ab. In jedem Fall ist der erste Schritt der Lohn-
buchhaltung die Eingabe der nötigen Daten. Sind die Daten erst einmal im Computer
angekommen, muss er mit ihnen arbeiten, also die nötigen Verarbeitungsschritte vor-
nehmen. Die Regeln und Vorschriften, nach denen der Lohnbuchhalter in der guten al-
ten Zeit gearbeitet hat, stecken jetzt in den Anweisungen des verwendeten Programms,
in der Software, und dieses Programm arbeitet mit zwei verschiedenen Datenarten: ers-
tens mit den Daten, die aus der Zeiterfassung in den Rechner übertragen wurden, und
zweitens mit den zusätzlichen Daten, die auch der Lohnbuchhalter gebraucht hat, näm-
lich mit den Steuersätzen und Ähnlichem mehr. Da ein Computer schwerlich in den
Steuertabellen nachsehen kann, sind diese Daten auf der Festplatte des Rechners abge-
speichert, sodass er jederzeit auf sie zugreifen kann. Während der eigentlichen Berech-
nung, in der wieder Bruttolohn, Abzüge und Nettolohn ausgerechnet werden, kommen
sowohl Ihre Anwesenheitsdaten als auch die für Ihren Fall nötigen auf der Festplatte
vorrätigen Daten in den aktuellen Arbeitsspeicher des Rechners, damit er, wenn er nun
schon mal Rechner heißt, auch alles Gewünschte ausrechnen kann. Die Rolle des Tisch-
rechners, den noch der Lohnbuchhalter verwendet hatte, spielt dabei das Rechenwerk
des Computers.
Sie werden zugeben, dass die Aktivitäten des letzten Absatzes sicher die Bezeichnung
Verarbeitung verdienen. Der nächste Schritt ist wenig überraschend. Die berechneten
Ergebnisse müssen wieder den verschiedenen beteiligten Stellen zur Verfügung gestellt
werden, indem man Ausdrucke macht, Bildschirmausgaben ansieht oder über eine Netz-
werkverbindung direkt die Ergebnisse an einen anderen Rechner weitergibt. Also ergibt
sich als letzter Schritt der computergestützten Datenverarbeitung wieder die Ausgabe.
Sehen Sie den prinzipiellen Unterschied zwischen der manuellen und der maschinellen
oder auch automatisierten Datenverarbeitung, wie ich sie hier beschrieben habe? Nein?
Kein Wunder, es gibt ja auch keinen. Beide funktionieren nach dem gleichen Prinzip:
Zuerst werden Daten eingegeben, dann werden sie nach bestimmten Regeln und Metho-
den verarbeitet, wobei unter Umständen noch weitere Daten herangezogen werden, und
schließlich werden die Ergebnisse ausgegeben. Das ist ein so grundlegendes Prinzip der
gesamten Datenverarbeitung, dass es einen eigenen Namen bekommen hat. Man nennt
es das EVA-Prinzip und fasst damit die Anfangsbuchstaben der drei Schlüsselwörter
Eingabe, Verarbeitung und Ausgabe in einer eingängigen Abkürzung zusammen. Da
aber in der Informatik alles möglichst auf Englisch formuliert sein muss, gibt es auch
eine englische Variante des EVA-Prinzips. Sie müssen nur bedenken, dass Eingabe Input,
Verarbeitung Process und Ausgabe Output heißt, und schon haben Sie alles zusammen,
um auch die Abkürzung IPO-Prinzip zu verstehen.
GDI01 5
© HfB, 02.12.20, Berk, Eric (904709)
Die Datenverarbeitung verläuft in aller Regel nach dem EVA-Prinzip, das heißt nach
dem Prinzip Eingabe → Verarbeitung → Ausgabe, oder in englischer Sprache Input
→ Process → Output, weshalb man auch vom IPO-Prinzip spricht.
Kann es Ihnen also egal sein, ob Sie Ihre Datenverarbeitung automatisiert oder manuell
erledigen lassen, wenn doch das Prinzip immer das gleiche ist? Sicher nicht, denn auch
bei gleichen Prinzipien gibt es doch in der Ausführung recht deutliche Unterschiede.
Was jedem vielleicht zuerst einfällt, ist die unterschiedliche Geschwindigkeit. Das mer-
ken Sie schon, wenn Sie eine einfache Multiplikation schriftlich ausführen oder mithilfe
eines Taschenrechners und dabei die Zeit messen. Noch viel deutlicher wird das natür-
lich, wenn es um die Verarbeitung großer Datenmengen geht, also zum Beispiel um die
Lohnbuchhaltung in einem großen Unternehmen. Die Routinearbeit des Datensuchens
und Berechnens der Ergebnisse lässt sich mit einem Rechner sehr schnell durchführen,
von Hand wäre es fast eine Lebensaufgabe. Aber Vorsicht: Der Geschwindigkeitsvorteil
bezieht sich vor allem auf Routineaufgaben; sobald es um sehr spezielle Aufgaben geht,
die ein gewisses Maß an Kreativität erfordern, kann es ein Mensch immer noch oft mit
einem Computer aufnehmen.
Etwas anders sieht es aus bei der Zuverlässigkeit. Computer sind in aller Regel deutlich
zuverlässiger als Menschen, auch wenn sie gelegentlich abstürzen – aber das soll auch
schon bei Menschen vorgekommen sein, vor allem am Wochenende. Sie können den
Computer die immer gleichen Aufgaben so oft durchführen lassen, wie Sie wollen, er
wird keine Ermüdungserscheinungen zeigen, keine Flüchtigkeitsfehler machen oder Sie
durch bewusste Schlamperei ärgern; er macht einfach immer das, was Sie ihm gesagt ha-
ben. Genau darin liegt allerdings auch ein Problem: Wenn Sie der Maschine die falschen
Regeln mitgeteilt, sie also falsch programmiert haben, dann wird sie natürlich auch treu
und zuverlässig die falschen Ergebnisse liefern. Zwar immer die gleichen, aber eben fal-
sche. Man kann dafür aber nicht den Computer verantwortlich machen, der hat wie üb-
lich nur getan, was Sie ihm gesagt haben. Auch das Programm kann nichts dafür: Die
Verantwortung liegt hier eindeutig bei dem Programmierer oder dem Anwender, der ein
falsches Programm geschrieben oder falsche Eingabedaten verwendet hat.
Sie dürfen nicht vergessen, dass es beim Computereinsatz auch um wirtschaftliche Fra-
gen geht, und deshalb besteht ein wesentlicher Vorteil der automatisierten Datenverar-
beitung in den geringeren Kosten, die sie verursachen. Die einmalige Anschaffung von
Rechnern, verbunden mit der nötigen Software, verursacht natürlich zunächst einmal
Kosten, aber wenn sie erst mal da sind, dann sparen sie auch einiges ein. Die Maschine
ist auf mittlere Sicht billiger als der menschliche Lohnbuchhalter, denn sie bezieht kein
Gehalt und keine Lohnnebenkosten, ganz zu schweigen davon, dass man für sie auch
keine Kantine braucht. Das ist einerseits ein Vorteil, weil das Unternehmen Geld spart,
andererseits aber auch ein Nachteil, weil auf diese Weise Arbeitsplätze verloren gehen
– nicht immer kann man alles eindeutig bewerten.
6 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Sie sehen also, es ist sinnvoll, bei Aufgaben der Datenverarbeitung auf einen Computer
zurückzugreifen, und daher sollten wir einen Blick auf die Aufgabenfelder werfen, für
die so ein Rechner eingesetzt werden kann.
Wenn man ihn schon als einen Rechner bezeichnet, dann sollte zu seinen grundlegenden
Aufgaben sicher das Rechnen gehören, genauer gesagt die Ausführung von Berechnun-
gen. Das können Berechnungen verschiedenster Art sein, beispielsweise die Berechnung
des Nettolohns aus dem Bruttolohn und den Randbedingungen, über die ich vorhin ge-
sprochen habe. Solche Berechnungen könnte auch jeder Lohnbuchhalter durchführen;
man braucht hier den Computer nicht deshalb, weil die Berechnungen so kompliziert
sind, dass das kein Mensch mehr hinbekommt, sondern weil zu oft die gleichen recht
einfachen Dinge getan werden müssen. Anders sieht das aus bei bestimmten techni-
schen oder physikalischen Problemen, bei denen keine Massendaten verarbeitet werden,
sondern ausgesprochen komplizierte Rechenverfahren abgearbeitet werden müssen, für
die ein noch so begabter Mensch mehr Zeit bräuchte, als ein Leben hergibt. Diese Ver-
fahren lassen sich in einem Programm beschreiben, und der Computer kann mit etwas
Glück aufgrund seiner hohen Geschwindigkeit die nötigen Berechnungen vornehmen.
Rechnen ist nicht alles im Leben. Denken Sie wieder einmal an die maschinell durchge-
führte Lohnbuchhaltung, die am Ende, nachdem alles gerechnet ist, irgendwelche Er-
gebnisse liefert. Nun kann es ja sein, dass die Steuerprüfung Ihrer Firma auf den Zahn
fühlen und Ihre Abrechnungen überprüfen will, und zu diesem Zweck müssen die Er-
gebnisse gespeichert sein. Das kann man auf dem Papier machen, aber das kostet eine
Unmenge Platz, weshalb man die Speicherung großer Datenmengen oft und gerne ma-
schinell vornimmt, also mit Computerunterstützung. Auch hier gibt es verschiedene
Möglichkeiten. Es kann sich um langfristig anzulegende Daten handeln, die wird man
auf einem bestimmten Speichermedium ablegen, von dem man hofft, dass man es not-
falls wiederfindet. Dazu gehören zum Beispiel Reproduktionen von alten Manuskripten
oder Bildern, die man der Nachwelt erhalten will. Wenn Sie aber von Frankfurt nach Te-
neriffa fliegen wollen und eine Flugbuchung vorgenommen haben, dann können Sie mit
einem gewissen Recht erwarten, dass die Angestellten beim Einchecken nicht erst das
richtige Speichermedium suchen, solche Daten müssen natürlich sofort verfügbar sein.
Man verwendet also Computer auch, um Daten abzuspeichern, und zwar entweder
langfristig oder direkt verfügbar.
Vielleicht haben Sie schon darauf gewartet, dass ich endlich über das Internet rede, und
natürlich haben Sie recht: Wenn es um die Grundaufgaben eines Computers geht, muss
ich es schon mal erwähnen. Es gibt ja nicht nur einen Computer auf der Welt, und wenn
man die Leistungen und Informationen dieser Computer miteinander verbinden will,
dann spricht man von Computernetzwerken. Über das Internet hat man heute einen
Riesenverbund von Rechnern zur Verfügung, auf die eine Riesenmenge von Benutzern
zugreifen kann, und das regelt sich nicht von allein. Um diese Kommunikation zwi-
GDI01 7
© HfB, 02.12.20, Berk, Eric (904709)
schen den Computern zu steuern, brauchen Sie selbstverständlich wieder Computer, de-
ren Aufgabe es ist, die Übermittlung der gewünschten Informationen von Ort zu Ort zu
steuern.
Und damit habe ich schon das nächste Schlüsselwort gefunden: Steuerung und, was fast
immer dazugehört, Kontrolle. Nicht nur das Internet und die allgemeine Datenkommu-
nikation müssen gesteuert werden, meine Waschmaschine und mein Auto auch. Auch
das übernehmen oft genug Computer, ob Navigationssysteme oder Waschmaschinen-
steuerung, ob Autopiloten im Flugzeug oder Steuerungsgeräte für Produktionsroboter,
sie alle haben die Aufgabe, Geräte zu steuern und zu kontrollieren.
Jetzt sind wir so weit, eine Definition des Begriffs Computer geben zu können, den ich
schon so oft benutzt habe. Mein etwas angejahrtes Lexikon gibt beispielsweise die eher
einfache Definition, ein Computer sei eine elektronische Datenverarbeitungsanlage, die
heute in Wissenschaft, Wirtschaft, Verwaltung und Technik eine entscheidende Rolle
spiele. Das ist fein beobachtet, aber doch ein wenig schwammig. Auch den Computer
einfach nur als Rechner zu bezeichnen und damit auszusagen, dass man nur mit ihm
rechnet, ist zu dünn. Ich verwende deshalb die eben ermittelten Grundfunktionen und
definiere einen Computer durch die Aufgaben, für die er da ist.
Ein Computer ist ein Gerät, das Daten speichert und verarbeitet, das auf dieser Basis
Berechnungen durchführt, andere Geräte steuern kann und das mit anderen Geräten
und mit Menschen in Verbindung treten kann.
Es wird nichts darüber gesagt, dass es sich unbedingt um ein elektronisches Gerät han-
deln muss. Theoretisch kann man sich einen Computer ohne elektrischen Strom auf der
Basis fließenden Wassers vorstellen, wenn ich auch zugeben muss, dass man von hyd-
raulischen Computern selten etwas hört. Wenn ich also von einem Computer rede, dann
meine ich natürlich in aller Regel ein elektronisches Gerät, das der obigen Definition ge-
nügt.
Sie sollten dabei aber nicht aus dem Auge verlieren, dass Computer nur ein Mittel zum
Zweck sind, und der Zweck ist der im EVA-Prinzip beschriebene: die Eingabe, Verarbei-
tung und Ausgabe von Daten. Da das Ganze mithilfe des elektronischen Gerätes Com-
puter stattfinden soll, spricht man auch, wie Sie natürlich wissen, gerne von elektroni-
scher Datenverarbeitung, abgekürzt EDV. Und was ist jetzt Informatik? Auch da gehen
die Definitionen etwas auseinander, zumal in der englischen Sprache das Wort Informa-
tik gar nicht so recht existiert, da sagt man Computer Science, also die Wissenschaft vom
Computer. So falsch ist das auch gar nicht, denn schließlich gäbe es ohne Computer so
etwas wie Informatik vermutlich überhaupt nicht, und daher muss die Informatik wohl
ziemlich viel mit der elektronischen Datenverarbeitung zu tun haben. Ich halte mich
deshalb an die folgende Definition.
Informatik ist die Wissenschaft, die sich mit den theoretischen Grundlagen, den Mit-
teln und Methoden sowie mit der Anwendung der elektronischen Datenverarbeitung
beschäftigt, das heißt mit der Informationsverarbeitung unter Einsatz von Compu-
tern.
8 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Genau das werden wir hier machen, und wir haben ja auch schon in diesem Abschnitt
damit angefangen: Wir gehen der Frage nach, wie man mit Computern automatisierte
Informationsverarbeitung betreibt. Zu diesem Zweck werde ich im nächsten Abschnitt
ein wenig über Algorithmen reden.
Übung 1.1:
Analysieren Sie im Hinblick auf das EVA-Prinzip die Tätigkeiten, die ein Mensch
bzw. ein Computer durchführen muss, um zwei Gleichungen mit zwei Unbekannten
zu lösen, also zum Beispiel
ax by e
cx dy f
mit den Unbekannten x und y. Geben Sie dabei genau an, welche Eingaben nötig
sind, welche Verarbeitungsschritte durchgeführt werden und wie die Ausgabe ge-
staltet werden sollte.
Übung 1.2:
Analysieren Sie die möglichen Nachteile, die die automatisierte Datenverarbeitung
im Vergleich zur manuellen Datenverarbeitung hat.
Übung 1.3:
Ein Handlungsreisender startet in Frankfurt eine Rundreise, die ihn durch 19 weitere
Städte und zum Schluss wieder zurück nach Frankfurt führt. In jeder Stadt will er
genau einmal Station machen (außer natürlich in Frankfurt, wo er sowohl startet als
auch ankommt).
a) Zeigen Sie, dass es genau
19 ⋅ 18 ⋅ 17 ⋯ 3 ⋅ 2 ⋅ 1
verschiedene Reiserouten gibt. Man nennt diese Zahl 19! Fakultät von 19.
b) Der Reisende hat einen Computer zur Verfügung, der pro Sekunde 10 Milliarden
verschiedene Routen durchchecken kann. Wie lange braucht er, um alle mögli-
chen Reiserouten durchzugehen und so die kürzeste Route herauszufinden?
GDI01 9
© HfB, 02.12.20, Berk, Eric (904709)
ausgeführt. Also dient der Computer der Ausführung von Algorithmen, wobei das Pro-
gramm die Rolle des Vermittlers zwischen dem abstrakten Algorithmus und dem kon-
kreten Computer spielt: Man kann dem Rechner nicht auf Deutsch erklären, was er jetzt
zu tun hat, dazu braucht man eine klar definierte und auf die Bedürfnisse der Maschine
abgestimmte Sprache. Wenn Sie aber eine Verfahrensanleitung, also den Algorithmus,
übersetzen können sollen in die Anweisungen einer Programmiersprache, dann darf
dieser Algorithmus sich nicht in den Gefilden philosophischer Großzügigkeit bewegen,
sondern muss klar und exakt sein. Das ist nicht alles. Kein Computer der Welt kann un-
endlich lange laufen, also darf kein Algorithmus der Welt unendlich lang sein. Und das
heißt, dass sowohl die Anzahl der Verfahrensvorschriften als auch die Anzahl der kon-
kret durchzuführenden Einzelschritte nicht unendlich groß werden darf. Sie dürfen also
keine Anweisung der Form „Solange 1 1 ist, gib die Zahl 17 am Bildschirm aus“ in Ih-
ren Algorithmus schreiben, denn damit hätten Sie zwar nur eine einzige Verfahrensvor-
schrift, diese einzige Verfahrensvorschrift müsste jedoch unendlich oft durchgeführt
werden, und das ist offenbar nicht möglich.
Fast haben wir es schon, nur noch eine Kleinigkeit fehlt. Wie kommt man auf die Idee,
sich einen Algorithmus zu überlegen? Sicher nicht aus heiterem Himmel, weil heute
Mittwoch oder gar Donnerstag ist. Ein Algorithmus dient immer der Lösung eines Pro-
blems, einer Aufgabe, sonst wäre er sinnlos. Sobald Sie also ein Problem haben und die-
ses Problem mithilfe endlich vieler klarer Verfahrensvorschriften lösen wollen, die sich
in endlich vielen Einzelschritten durchführen lassen, können Sie mit Fug und Recht von
einem Algorithmus sprechen.
Ein Algorithmus ist eine endliche Menge von eindeutig festgelegten Verfahrensvor-
schriften zur Lösung eines Problems durch eine in der Regel endliche Menge von
Einzelschritten.
Man kann die Auffassung vertreten, die Menge der Einzelschritte dürfte auch unendlich
groß werden; es gibt mathematische Verfahren, die eine unendliche Anzahl von Wieder-
holungen verlangen und übereinstimmend als Algorithmen bezeichnet werden. Sobald
Sie solche Verfahren aber konkret anwenden wollen, werden Sie gezwungen sein, nach
einer gewissen Zeit aufzuhören – möglichst noch vor dem Ende des Universums, weil
sonst Ihre berechneten Ergebnisse zu nichts mehr zu gebrauchen sind und somit auch
kein Problem lösen können. Für uns hat ein Algorithmus deshalb in jeder Hinsicht end-
lich zu sein, auch wenn das in der Mathematik manchmal anders aussieht.
Übrigens klingt das Wort Algorithmus zwar eher griechisch oder vielleicht auch latei-
nisch, kommt aber aus der arabischen Sprache und lehnt sich an den Namen des arabi-
schen Mathematikers und Astronomen Mohamed ibn Musa al-Hwarizmi aus dem 9.
Jahrhundert an, der zwar sicher keine Computerprogramme geschrieben, aber immer-
hin als erster arabischer Mathematiker das Ihnen vertraute Dezimalsystem benutzt und
systematisch beschrieben hat. Es handelt sich also um ein reines Kunstwort, und das ist
auch in Ordnung, denn schließlich ist ein Algorithmus auch etwas Künstliches.
Führt man nun einen Algorithmus aus, so spricht man von einem Prozess, und der Aus-
führende ist der Prozessor. Wer dabei die Rolle des Prozessors spielt, hängt ganz vom
Einzelfall ab. Wollen Sie zum Beispiel zwei natürliche Zahlen auf dem Papier addieren,
dann sind Sie der Prozessor. Soll dagegen eine Addition auf dem Computer vorgenom-
men werden, wird man den Computer oder einen bestimmten Teil des Computers, über
10 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
den wir später noch sprechen werden, als Prozessor bezeichnen. Beim Ausführen eines
etwas komplizierteren Algorithmus zum Sortieren einer großen Menge von Datensätzen
kann Ihnen kein menschlicher Prozessor mehr die Ausführung des Algorithmus abneh-
men, dieser Prozess muss auf einer Maschine laufen. Es dürfte klar sein, dass wir uns im
Folgenden nur mit Algorithmen befassen werden, die als Prozessor einen Computer ver-
wenden.
Nun entsteht aber, wenn man maschinell ausführbare Algorithmen haben möchte, ein
kleines Problem. Der Prozessor muss nämlich den Algorithmus in irgendeiner Weise in-
terpretieren können, und dazu gehören immer zwei Dinge: Erstens muss er überhaupt
einmal jeden Schritt des Algorithmus verstehen, und zweitens muss er auch in der Lage
sein, jede verlangte Operation auszuführen. Wie schon erwähnt, können Sie dem Com-
puter nicht einfach erzählen, was er machen soll, Sie müssen Ihren Algorithmus in eine
Form bringen, die der Computer versteht. Und damit sind wir bei der Programmierung
und den Programmiersprachen. Der Algorithmus selbst ist zwar völlig unabhängig von
einer verwendeten Programmiersprache, um ihn aber dem Computer verständlich zu
machen, braucht man eine Formulierung in einer Programmiersprache, möglichst auch
noch in einer, die der verwendete Computer versteht. Es wird Ihnen also nichts anderes
übrig bleiben, als Ihren Algorithmus in einer Programmiersprache als Programm zu for-
mulieren und dann dem Computer zur Kenntnis zu geben. Welche Programmiersprache
Sie dabei verwenden, hängt von Ihrem Geschmack und von der Sachlage ab.
Algorithmen, die auf einem Computer ausgeführt werden sollen, müssen mithilfe ei-
ner Programmiersprache als Programme geschrieben werden.
Denken Sie immer daran: Der Algorithmus, Ihre Verfahrensbeschreibung, ist die Grund-
lage der Verarbeitung, das Programmieren setzt die Existenz eines Algorithmus voraus,
der in eine Programmiersprache umgesetzt werden soll. Wenn Sie also ein Problem mit-
hilfe eines Programms lösen wollen, dann ist es immer zu empfehlen, in drei Schritten
vorzugehen. Zuerst sollten Sie sich darüber klar werden, um welches Problem es eigent-
lich geht. Das klingt wesentlich trivialer, als es ist, denn oft genug wird wild in die Ge-
gend hinein programmiert, ohne dass sich jemand Rechenschaft darüber ablegt, welche
Ziele genau erreicht, welche konkreten Probleme gelöst werden sollen; deshalb ist die
Problemdefinition, in der das anzugehende Problem so genau wie möglich beschrieben
wird, von großer Bedeutung. Ist das Problem einmal definiert, so können Sie daran ge-
hen, einen Algorithmus zur Lösung des Problems zu entwerfen – man spricht dann vom
Programmentwurf. Darin wird also noch nicht das konkrete Programm geschrieben,
sondern es werden die Verfahrensschritte festgelegt, die anschließend in der Program-
mierung in eine Programmiersprache umgesetzt werden.
Zugegeben: Bei kleinen Problemen kann man die ersten beiden Teile dieses Ablaufs, also
die Problemdefinition und den Programmentwurf, mehr oder weniger im Kopf erledi-
gen, aber darüber nachdenken sollte man auf jeden Fall. Bei großen Systemen ist aller-
dings ein strukturiertes Vorgehen unverzichtbar, wenn man nicht ganz schnell in gewal-
tige Schwierigkeiten kommen will; das Fach Software Engineering ist die Antwort der
Informatiker auf das wilde Programmieren der informatischen Frühzeit.
Eine kleine Einschränkung muss ich noch machen. Im Prinzip ist tatsächlich der Algo-
rithmus unabhängig von der Programmiersprache, in die er anschließend umgesetzt
wird, oder er sollte es wenigstens sein. Wie die meisten hohen Prinzipien ist allerdings
GDI01 11
© HfB, 02.12.20, Berk, Eric (904709)
Bei der Erstellung eines Programms zur Lösung eines Problems sollte man schritt-
weise vorgehen. Eine mögliche Einteilung sieht drei Schritte vor:
• Problemdefinition
• Programmentwurf
• Programmierung
Der im Programmentwurf entwickelte Algorithmus sollte sich an den Gegebenhei-
ten der für die Programmierung verwendeten Programmiersprache orientieren, da-
mit er in dieser Sprache realisiert werden kann.
Ein Computer ist eine konkrete Maschine, die aus einer Menge verschiedener Einzelteile
besteht, aus physisch vorhandenen Komponenten. Die Gesamtheit dieser technischen
Geräte und Einrichtungen bezeichnet man gerne als Hardware, ein englisches Wort für
Eisenwaren. Das kann aber nicht alles sein. Wir hatten uns gerade darauf geeinigt, dass
ein Computer neben seiner Funktion als Staubfänger vor allem der Ausführung von Al-
gorithmen dient, indem er als Prozessor für Programme zur Verfügung steht. Ein Algo-
rithmus ist kein physikalischer Gegenstand. Man kann zwar das zugehörige Programm
auf eine Festplatte speichern, aber die zur Speicherung verwendeten Festplattensektoren
wird kaum jemand mit dem Programm gleichsetzen wollen. Algorithmen und Program-
me sind und bleiben etwas Abstraktes, eine geistige Konstruktion, und ganz sicher keine
Eisenwaren. Aus diesem Grund hat sich für die in einem Computer einsetzbaren Pro-
gramme die Bezeichnung Software durchgesetzt, um den Gegensatz zur Hardware zu
dokumentieren und deutlich zu machen, dass zum Betrieb eines Computers eben etwas
mehr nötig ist als die anfassbaren Komponenten.
Nun gibt es aber verschiedene Arten von Software, und man neigt dazu, sie in zwei Klas-
sen zu unterscheiden: Auf der einen Seite haben Sie die Systemsoftware, auf der ande-
ren die Anwendungssoftware. Ohne ein gewisses Maß an Systemsoftware kann kein
Rechner existieren. Irgendjemand muss sich darum kümmern, dass sich Benutzer an-
melden und dass Programme geladen und ausgeführt werden können. Man braucht eine
Software, die die Verwaltung der Dateien organisiert, die dafür sorgt, dass die Steuerung
der Prozesse reibungslos abläuft und sich mehrere Prozesse nicht gegenseitig stören – all
das erledigt das Betriebssystem, es verwaltet die Ressourcen Ihres Rechners und erspart
12 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
es Ihnen, sich selbst direkt mit der Hardware herumschlagen zu müssen. Ob das nun ein
Windows-Dialekt oder Linux oder sonst etwas ist: Die grundsätzlichen Aufgaben des
Betriebssystems sind immer gleich, und kein Computerbenutzer kann darauf verzichten.
Zur Systemsoftware zählt man neben dem Betriebssystem auch noch eine Reihe anderer
sogenannter Dienstprogramme und Werkzeuge, auf die ich jetzt nicht näher eingehen
werde. Wichtig ist: Die Systemsoftware ist die Voraussetzung, ohne die nichts geht. Und
was soll mit ihrer Hilfe eigentlich gehen? Natürlich die Programmierung, auf die will ich
ja hier hinaus. Und die Programme, die Sie erstellen, die Programme, die Sie schreiben,
damit sie jemand anwendet, fallen unter den Begriff Anwendungssoftware. Ob das
nun ein Abrechnungsprogramm ist oder ein Programm zur Berechnung von Wettervor-
hersagen oder sonst irgendeine Anwendung: Es handelt sich in jedem Fall um Anwen-
dungssoftware.
Unter Software versteht man die Menge der Programme, die auf einem Computer
eingesetzt werden können. Man unterteilt sie in Systemsoftware und Anwendungs-
software, wobei die Systemsoftware noch einmal unterteilt wird in das Betriebssys-
tem sowie systemnahe Dienstprogramme und Werkzeuge.
Seit es Programmiersprachen gibt, mit deren Hilfe man Software verschiedenster Art
entwickeln kann, neigten die Entwickler zumindest in der Anfangszeit dazu, munter vor
sich hin zu programmieren. Das führte aber zu Problemen, weil jeder seinen eigenen Stil
entwickelte und sich unter Software-Entwicklern ein leichter Hang zu einer „Nach mir
die Sintflut“ -Mentalität ausbildete: Solange der Entwickler sein Programm verstand,
war alles in Ordnung, die Nachwelt hat keinen interessiert. Insbesondere wurden mit
Begeisterung wilde Sprünge in Programmen durchgeführt, die zur Folge hatten, dass ein
Programm eben noch einen bestimmten Befehl ausführte und sich dann infolge eines
Sprungbefehls plötzlich an einer völlig anderen Stelle befand. Es erinnerte, falls die
Sprungbereitschaft stark ausgeprägt war, streckenweise an manche Gesprächspartner,
die einen Satz anfangen, sich mitten im Satz unterbrechen, dann den unterbrechenden
Satz wieder durch einen anderen Einschub zerhacken und dieses Spiel so lange betrei-
ben, bis nicht mehr die geringste Hoffnung besteht, zu dem eigentlich angefangenen
Satz zurückzufinden.
Sie können sich vorstellen, dass Programme dieser Art unter Umständen leichte Quali-
tätsprobleme aufwiesen, weshalb man sich ab den späten 1960er-Jahren bemüht hat, zu
einem besseren Stil zu finden. Nach einem grundlegenden Artikel des holländischen In-
formatikers Edsger Dijkstra, in dem er zu Recht die Sprunganweisung verdammte, ging
man langsam über zum Prinzip der strukturierten Programmierung. Ihr Grundgedan-
ke ist einfach genug: Niemals einen Sprungbefehl verwenden und alle Programmanwei-
sungen schön der Reihe nach erledigen. Eine Anweisung in einem Programm kann man
nur erreichen, wenn man die vorherige Anweisung erledigt hat, und sobald man mit ei-
ner Anweisung fertig ist, geht man zur nächsten über, ohne wie ein Känguru durch den
Programmtext zu hüpfen. Da es aber manchmal – und sogar ziemlich häufig – nötig ist,
einem Programm eine gewisse Flexibilität zu erlauben, wurden in der strukturierten
Programmierung bestimmte Kontrollstrukturen eingeführt, mit deren Hilfe man die
Abarbeitungsreihenfolge der Programmanweisungen beeinflussen kann. Es handelt sich
dabei um die berühmten Kontrollstrukturen Sequenz, Auswahl und Wiederholung,
die ich im nächsten Kapitel besprechen werde.
GDI01 13
© HfB, 02.12.20, Berk, Eric (904709)
Übung 1.4:
Untersuchen Sie die Wirkungsweise des folgenden Algorithmus, der mit einer gan-
zen Zahl a arbeitet. Welchen Wert hat die Zahl a nach der Abarbeitung des Algo-
rithmus? Könnte man den Algorithmus auch einfacher formulieren?
Falls a0 ist
dann gehe folgendermaßen vor:
Falls a 0 ist, setze a 1
ansonsten setze a 2}
Beachten Sie, dass die eingerückten Zeilen eine Gesamtheit bilden, sodass also im
Fall a0 beide Anweisungen nach dem Doppelpunkt ausgeführt werden müssen.
Übung 1.5:
Gegeben seien die folgenden beiden Algorithmen zur Beschreibung des Verhaltens
eines Autofahrers an einer Straßenkreuzung. Wie interpretieren Sie die verschiede-
nen Einrückungen? In welcher Situation unterscheiden sich Algorithmus 1 und Al-
gorithmus 2? Welcher Algorithmus erscheint Ihnen zweckmäßiger?
Algorithmus 1:
Falls die Ampelanlage funktioniert
dann gehe folgendermaßen vor:
falls die Ampel rot oder gelb anzeigt
dann bleibe stehen
ansonsten fahre
Algorithmus 2:
Falls die Ampelanlage funktioniert
dann gehe folgendermaßen vor:
falls die Ampel rot oder gelb anzeigt
dann bleibe stehen
ansonsten fahre
14 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Übung 1.6:
Das folgende Programm in einer nicht existierenden Programmiersprache enthält
mehrere Sprungbefehle, die sich auf die jeweils vorangestellten Zeilennummern be-
ziehen. Stellen Sie fest, zu welchem Problem dieses Programm führt.
01 x1
02 y2
03 gehe zu Zeile 07
04 berechne 2x
05 berechne xy
06 gehe zu Zeile 03
07 gehe zu Zeile 04
GDI01 15
© HfB, 02.12.20, Berk, Eric (904709)
Im ersten Kapitel haben Sie gesehen, dass man zur Lösung bestimmter Probleme Algo-
rithmen entwickelt und zur Umsetzung dieser Algorithmen so etwas wie Programmier-
sprachen braucht, weil der Computer sonst nicht weiß, was er mit den Algorithmen an-
fangen soll. Aber nach welchen Prinzipien soll man bei der Entwicklung eines
Algorithmus vorgehen? Gibt es verschiedene Arten der Problemlösung? Und wenn man
verschiedene Lösungen des gleichen Problems hat: Wie kann man entscheiden, welche
Lösung die bessere ist? Solchen Fragen werde ich in diesem Kapitel nachgehen.
2.1 Kontrollstrukturen
Irgendwann müssen wir auch etwas konkreter werden, und dieser Zeitpunkt ist jetzt ge-
kommen. Ich werde Sie hier noch nicht mit syntaktischen Details bestimmter Program-
miersprachen behelligen – zumindest nicht allzu sehr, weil das die Aufgabe späterer Stu-
dienhefte sein wird. Was ich Ihnen hier zeigen will, das sind die bereits angekündigten
Kontrollstrukturen, ihre grafische Darstellung mithilfe von Struktogrammen und we-
nigstens ansatzweise ihre Umsetzung in der Programmiersprache C.
Am einfachsten ist die schlichte Sequenz zu verstehen. Dass jedes Programm aus ver-
schiedenen Anweisungen besteht, haben Sie natürlich schon längst mitbekommen, und
eine Abfolge aus einzelnen Schritten, aus einzelnen Anweisungen, die schön brav nach-
einander auszuführen sind, nennt man eine Sequenz. Zu jedem Zeitpunkt der Verarbei-
tung wird genau ein Schritt ausgeführt, wobei jeder Schritt auch genau einmal ausge-
führt wird, nichts wird ausgelassen, nichts wiederholt. Sie können es sich vorstellen wie
das vollständige Auslöffeln einer Suppe: Zu jedem Zeitpunkt wird genau ein Löffel ge-
gessen, keiner wird ausgelassen, denn Sie essen Ihre Suppe brav auf, und es wird auch
keiner doppelt gegessen, zumindest hoffe ich das. Was die Programmsequenz von der
Suppe unterscheidet, ist der Umstand, dass es im Programm eine klar definierte Reihen-
folge der Abarbeitung gibt, die schon beim Aufschreiben einer Sequenz deutlich wird;
bei der Suppe ist das offenbar anders.
Wie so eine Sequenz konkret aussieht, kann man sich am Beispiel der Währungsumrech-
nung vorstellen. Nach dem Kurs des heutigen Tages (wir befinden uns im Herbst 2015)
entspricht ein Euro einem US-Dollar und 7,4 US-Cent. Schon in einer rein verbalen Be-
schreibung ist der Umrechnungsalgorithmus wohl ziemlich selbsterklärend. Der ge-
wünschte Euro-Betrag wird mit dem Umrechnungsfaktor 1,074 multipliziert und an-
schließend werden der Euro-Betrag und der Dollar-Betrag mit zwei Stellen nach dem
Dezimalpunkt ausgegeben. Eine klassische Sequenz, denn eines wird nach dem anderen
gemacht, alles passiert genau einmal, nichts wird ausgelassen, nichts wiederholt. Nun
könnte man auch ohne größere Probleme ein Programm in der Programmiersprache C
verfassen, das genau diesen schlichten Algorithmus abbildet, und das werde ich in Kürze
auch tun. Da aber nicht alle Programmierer die gleiche Programmiersprache verwen-
den, kann es nicht schaden, so etwas auch anders darstellen zu können, unabhängig von
einer konkreten Programmiersprache, und ein beliebtes Hilfsmittel zu diesem Zweck
sind Struktogramme.
16 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Struktogramme wurden 1973 von Nassi und Shneiderman als Methode zur Strukturie-
rung vorgeschlagen, weshalb man sie auch ab und zu als Nassi-Shneiderman-Dia-
gramme bezeichnet, und erfreuen sich auch heute noch großer Beliebtheit. Jeder Verar-
beitungsschritt wird in einem schlichten rechteckigen Kasten dargestellt, mehrere
Verarbeitungsschritte zusammen ergeben einen Block. Den grundsätzlichen Aufbau se-
hen Sie in Abb. 2.1.
Sequenz
Anweisung 1
Anweisung 2
Anweisung 3
Hier werden drei Anweisungen in einem Block zusammengefasst, mehr gibt ein derart
elementares Struktogramm nicht her. Auch mein kleiner Umrechnungsalgorithmus
sieht als Struktogramm nicht aufregender aus als vorher; in Abb. 2.2 können Sie es be-
wundern. Wie Sie sehen, bildet das Struktogramm genau die einzelnen Schritte meines
verbal beschriebenen Algorithmus ab, nur noch etwas genauer, da hier schon Namen für
die beteiligten Variablen vergeben werden: eine Variable enachd für den Umrech-
nungsfaktor, eine Variable euro für den betrachteten Euro-Betrag und natürlich auch
eine Variable dollar für den berechneten Dollar-Betrag. Der Benutzer soll also aufge-
fordert werden, einen Euro-Betrag einzugeben, danach wird umgerechnet und zum
Schluss sollen sowohl der Euro- als auch der berechnete Dollar-Betrag ausgegeben wer-
den. Das ist kein Hexenwerk.
Nun ist aber das Struktogramm unabhängig von der verwendeten Programmiersprache
oder sollte es jedenfalls weitgehend sein. Anders sieht es aus, wenn ich das bereits er-
stellte Struktogramm als Vorlage für ein Programm – beispielsweise in der Program-
miersprache C – verwende, denn sobald ich eine konkrete Programmiersprache einsetze,
muss ich mich auf die Gepflogenheiten dieser Sprache einlassen. Sie werden im Verlauf
der weiteren Hefte noch ausreichend Gelegenheit haben, sich mit den Details von C zu
befassen, weshalb ich Ihnen hier zwar das eine oder andere C-Programm als Realisie-
rung eines Struktogramms vorstellen, aber nicht auf alle syntaktischen Einzelheiten ein-
gehen werde.
Umrechnung
setze enachd = 1,074
einlesen euro
GDI01 17
© HfB, 02.12.20, Berk, Eric (904709)
Das eigentliche Programm will ich Ihnen jetzt nicht länger vorenthalten.
/* geld.c –- waehrungen umrechnen */
#include stdio.h
main()
{
float enachd1.074;
float euro, dollar;
printf("Geben Sie einen Euro-Betrag ein:\n);
scanf("%f“ ,&euro);
dollar euro * enachd;
printf("%6.2 f. Euro sind %6.2 f. Dollarn“ , euro,dollar;
}
Falls Sie so etwas noch nie gesehen haben, wirkt es vielleicht ein wenig seltsam, aber in
Wahrheit ist es nicht weiter dramatisch. Die erste Zeile verrät Ihnen, dass das Programm
den Namen geld.c trägt und der Umrechnung von Währungen dient. Dass diese Zeile
in die Zeichen /* … */ eingeschlossen ist, bedeutet, dass es sich nur um einen Kom-
mentar handelt, der zur Information des Lesers, Benutzers oder Programmierers dient,
für den Ablauf des Programms aber nicht die geringste Bedeutung hat. Danach sehen
Sie den Befehl #include stdio.h, der schlimmer wirkt, als er ist. C in seiner
Grundform kennt überhaupt keine Möglichkeiten zur Ausgabe von Daten auf dem Bild-
schirm oder gar zur Dateneingabe über die Tastatur. Um also überhaupt zu ermöglichen,
dass der Euro-Betrag eingegeben und der Dollar-Betrag ausgegeben wird, müssen Sie
vorher dafür sorgen, dass Ihr C-Programm die entsprechenden Anweisungen auch ver-
steht, und genau das passiert mit #includestdio.h. Die Datei stdio.h ist eine
sogenannte Header-Datei, in der bestimmte Ein- und Ausgabefunktionen zu finden sind,
die Ihnen ohne diese Datei nicht zur Verfügung stünden. Durch den Befehl
#includestdio.h ist es angenehmerweise möglich, alle in der Header-Datei ange-
gebenen Funktionen auch in dem Programm zu benutzen, das den Befehl zum Einbinden
der Header-Datei enthält.
Danach geht es eher undramatisch weiter. Mit main() wird angezeigt, dass nun end-
lich das eigentliche Programm beginnt, das sogenannte Hauptprogramm, in dem die
wirkliche Arbeit erledigt wird. Und die läuft wie folgt ab: Erst wird eine Variable
enachd angelegt und mit dem Wert 1,074 versehen. Dann werden die beiden Variablen
euro und dollar zur Verfügung gestellt. Anschließend ergeht eine Aufforderung an
den Benutzer des Programms, den Euro-Betrag einzugeben, woraufhin ebendieser Be-
trag über die Tastatur eingegeben werden kann. Ohne auf Einzelheiten der Befehle ein-
zugehen, will ich hier nur sagen, dass das printf()-Kommando der Ausgabe am Bild-
schirm dient, während Sie mithilfe des scanf()-Kommandos eine Eingabe über die
Tastatur tätigen können. Ist das erst einmal erledigt, wird natürlich umgerechnet, und
die Werte für Euro und Dollar werden am Bildschirm angezeigt.
Viele Worte für ein so kleines Programm, doch wer hier zum ersten Mal einem C-Pro-
gramm begegnet, braucht vielleicht etwas Zuspruch. Wie schon gesagt: Das eigentliche
Programmieren mit C werden Sie zu einem späteren Zeitpunkt erlernen; hier will ich Ih-
nen nur einen Eindruck vermitteln.
18 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Auswahl
Bedingung
wahr falsch
Anweisungen Anweisungen
Nichts gegen Sequenzen, aber das Leben hat nun mal nicht immer so einen ordentlichen
Ablauf, wie das eine schlichte Sequenz gerne hätte. Was ich Ihnen bisher gezeigt habe,
war zu unflexibel, zu starr, da der Ablauf ohne jede Variationsmöglichkeit vorgegeben
war. Sie brauchen sich nur einmal mein altes Beispiel der Gehaltsabrechnung und Lohn-
buchhaltung ins Gedächtnis zu rufen: Je nach Steuerklasse wird die Berechnung des
Nettogehalts verschieden ablaufen, also kann man sie nicht mithilfe einer starren Se-
quenz in ein Programm fassen. Dieses Problem löst das Prinzip der Auswahl, die die
Ausführung eines Schrittes von einer bestimmten Bedingung abhängig macht. Das ken-
nen Sie aus dem richtigen Leben – wenn Sie beispielsweise für die nächste Klausur ler-
nen, dann haben Sie gute Chancen, sie zu bestehen, wenn nicht, dann eben nicht. Es ist
Ihre Entscheidung, und ähnliche Entscheidungen beeinflussen auch den Ablauf eines
Programms. Wie so etwas grundsätzlich in einem Struktogramm aussieht, zeigt Ihnen
Abb. 2.3.
Es wird eine Bedingung gestellt und überprüft, ob sie gültig ist. Falls sie erfüllt ist, geht
das Struktogramm in den „wahr“-Zweig und führt die Anweisungen aus, die sich dort
versammeln, falls nicht, begibt es sich in den „falsch“-Zweig und tut das, was man dort
von ihm verlangt. Nur einer der beiden Zweige wird ausgeführt, ja nach dem Status der
Bedingung.
Das klingt nun ein wenig abstrakt, und deshalb zeige ich Ihnen ein konkreteres Beispiel:
einen sehr schlichten Algorithmus zur Steuerberechnung. Ausgehend vom Bruttoein-
kommen, das natürlich erst einmal eingegeben werden muss, gibt es zwei Fälle. Falls das
Bruttoeinkommen unter 100000 Euro liegt, soll der Steuersatz 30 % betragen, ansonsten
35 %. Das ist sicher kein Abbild der steuerrechtlichen Wirklichkeit, aber für unsere Zwe-
cke momentan ausreichend. Nach der Berechnung der Steuer soll dann das resultierende
Nettoeinkommen ausgegeben werden. Wie so etwas im Struktogramm aussieht, zeigt
Ihnen Abb. 2.4.
Nettogehalt
einlesen brutto
GDI01 19
© HfB, 02.12.20, Berk, Eric (904709)
Sie werden vermutlich zugeben, dass dieses Struktogramm selbsterklärend ist, und ge-
nau das macht den Reiz der Struktogramm-Technik aus: Man kann sie leicht lesen und
leicht verstehen, sobald man die elementaren grafisch-syntaktischen Möglichkeiten ver-
standen hat. Anmerken sollte ich vielleicht noch, dass offenbar nach einer beendeten
Auswahl alles wieder gut ist und die nachfolgenden Anweisungen in jedem Fall durch-
geführt werden, egal in welchem Zweig der Auswahl man sich vorher befunden hat.
Und auch den Begriff der Anweisung sollten Sie nicht allzu eng sehen, denn es kann
durchaus passieren, dass Sie nur dann etwas erledigt wissen wollen, wenn die gegebene
Bedingung wahr ist. In diesem Fall lassen Sie einfach den Anweisungsteil des „falsch“-
Zweigs leer.
Sie sollten aber auch sehen, wie sich eine Auswahl in einem C-Programm niederschlägt.
/* netto.c – nettogehalt ausrechnen */
#include stdio.h
main()
{
float brutto, netto, grenze;
grenze 100000,00;
printf("Geben Sie den Bruttobetrag ein\n");
scanf("%f“ .&brutto
if (brutto grenze)
{
steuer 0,3 * brutto;
}
else
{
steuer 0,35 * brutto;
}
printf("Ihr Bruttogehalt lautet %8.2f“,brutto
printf("Ihr Nettogehalt lautet %8.2f“,netto
}
Über die seltsame Form der Ausgabeanweisung brauchen Sie sich nicht zu grämen, das
werden Sie später noch genauer lernen; das ominöse „%8.2f“ ist nur eine Formatanwei-
sung, die angibt, in welchem Format die jeweilige Zahl ausgegeben werden soll, nämlich
mit acht Stellen, davon zwei nach dem Dezimalpunkt. Wichtiger ist hier die Formulie-
rung der Auswahl: Die Bedingung schreibt man in Klammern nach einem if, danach
schreibt man die Sequenz der gewünschten Anweisungen in ein Paar von Mengenklam-
mern, und alle Anweisungen, die bei nicht vorliegender Bedingung durchgeführt wer-
den sollen, finden Sie – wieder von Mengenklammern umschlossen – im sogenannten
else-Zweig. Vergessen Sie dabei nicht, dass die Sequenz innerhalb des if-oder des
else-Zweiges wiederum alles Mögliche enthalten kann, also zum Beispiel noch eine
Abfrage.
20 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Eine Auswahl zwischen mehreren Möglichkeiten kann man mithilfe der if-Anwei-
sung realisieren. Sie hat die grundsätzliche Form
if (Bedingung)
Anweisungen 1
else
Anweisungen 2
Ich will gar nicht erst so tun, als hätte ich Ihnen schon alles über die Kontrollstruktur
„Auswahl“ berichtet, aber das würde jetzt auch zu weit führen. Hier will ich Ihnen nur
einen Eindruck von den Prinzipien der strukturierten Programmierung vermitteln, und
deshalb gehen wir jetzt zur nächsten Kontrollstruktur über: der Wiederholung.
Die letzte der drei klassischen Kontrollstrukturen finden Sie jeden Tag auch ohne Com-
puter im täglichen Leben. Manche Dinge wiederholen sich eben immer und immer wie-
der, man steht morgens auf, geht ins Bad, frühstückt oder auch nicht und verlässt die
Wohnung. Diesen Ablauf müssen Sie offenbar nicht jeden Tag neu lernen, mit der Zeit
funktioniert das alles ohne weiteres Nachdenken automatisch, und auf dem gleichen
Prinzip der Wiederholung oder auch Iteration beruht auch die Kontrollstruktur, über
die ich jetzt reden will. Sie tritt in drei verschiedenen Varianten auf, die eigentlich alle
das Gleiche machen, weshalb ich Ihnen auch nur eine der drei Versionen vorstellen wer-
de: die Wiederholung als nicht abweisende Schleife.
Der Name hat einen Grund. Es geht hier um eine Reihe von Anweisungen, die vermut-
lich nicht nur einmal, sondern mehrfach hintereinander ausgeführt werden sollen. Na-
türlich will man sie nur einmal aufschreiben und dann in Rahmen eines Struktogramms
oder eines C-Programms deutlich machen, dass diese Sequenz von Anweisungen wie-
derholt durchgeführt werden soll. So etwas nennt man eine Schleife. Wollen Sie nun si-
cherstellen, dass die Sequenz in jedem Fall mindestens einmal durchgeführt wird, so
spricht man von einer nicht abweisenden Schleife. Wie kann man das realisieren? Ganz
einfach: mithilfe einer Bedingung. Die Sequenz soll nach dem ersten Durchlauf nur noch
dann wiederholt werden, wenn eine bestimmte Bedingung, die sogenannte Durchfüh-
rungsbedingung, erfüllt ist; ist das nicht der Fall, so hat sie ihre Daseinsberechtigung
verloren und Sie können zu anderen Dingen übergehen. Die prinzipielle Darstellung ei-
ner nicht abweisenden Schleife in einem Struktogramm sehen Sie in Abb. 2.5.
do-Schleife
Anweisung 1
Anweisung 2
Anweisung 3
Durchführungsbedingung
GDI01 21
© HfB, 02.12.20, Berk, Eric (904709)
Der sogenannte Schleifenrumpf besteht hier aus drei Anweisungen, die auf jeden Fall
einmal durchgeführt werden und deren weitere Durchführung dann davon abhängt, ob
die am Ende geprüfte Durchführungsbedingung erfüllt ist. Falls ja, fängt alles von vorn
an, falls nein, ist die Schleife beendet und es geht nach dem Schleifenblock weiter. Man
kann an dem Struktogramm übrigens deutlich sehen, warum man diese Schleifenart
auch als fußgesteuerte Schleife bezeichnet, denn die alles entscheidende Durchfüh-
rungsbedingung steht nun mal unten, am Fuß der Schleife.
Auch hier will ich es nicht bei der abstrakten Beschreibung belassen, sondern Sie bitten,
sich einmal das Struktogramm in Abb. 2.6 anzusehen. Es ist nicht schwer zu verstehen,
was hier geschieht. Zunächst wird der Benutzer aufgefordert, eine natürliche Zahl ein-
zugeben. Nun bieten aber die meisten gebräuchlichen Programmiersprachen gar keine
Möglichkeit, sich auf natürliche Zahlen einzuschränken, sondern verfügen nur über so
etwas wie ganze Zahlen oder auch reelle Zahlen mit Nachkommastellen. Sie müssen
also damit rechnen, dass die Variable n, die im nächsten Schritt eingegeben wird, eine
ganze Zahl ist, also auch negativ sein kann. Deswegen finden Sie als nächsten Schritt
eine Auswahl: Ist die Zahl positiv oder nicht? Falls nein, ist die Sache an der Unfähigkeit
des Benutzers gescheitert, eine negative Zahl von einer positiven zu unterscheiden, und
er bekommt eine entsprechende Meldung. Falls aber doch, soll das Programm die Sum-
me der natürlichen Zahlen von 1 bis n berechnen – und das funktioniert mithilfe einer
nicht abweisenden Schleife.
Vor dem Start der Schleife wird einer Variablen namens summe der Wert 0 zugewiesen
und einer anderen Variable namens i der Wert 1. Dieses i dient als Zählvariable. Im
ersten Schleifendurchlauf wird zunächst die Anweisung summe summe i durch-
geführt. Das ist nicht mathematisch zu verstehen, sondern eine schlichte Wertzuwei-
sung: Der Variablen summe wird die Summe aus dem zugewiesen, was derzeit auf
summe und auf i steht. Damit beiden Variablen bisher noch nichts passiert war, hat
summe vor dieser Wertzuweisung den Inhalt 0 und i den Inhalt 1. Also wird summe
die Summe 01 zugewiesen, weshalb nach der ersten Wertzuweisung auf summe der
Wert 1 steht. In der nächsten Anweisung soll dann der Variablen i der um 1 erhöhte
bisherige Wert von i zugewiesen werden. Die mathematisch verstandene Gleichung
i i 1 wäre natürlich übelster Blödsinn: Eine Zahl kann nicht die gleiche bleiben,
wenn man sie um 1 erhöht. Das ist aber auch mit der Anweisung ii1 nicht gemeint,
hier soll nur der Variablen i der um 1 erhöhte bisherige Wert der Variablen i zuge-
wiesen werden. Mit anderen Worten: Durch diese Anweisung wird \verb!i! einfach nur
um 1 erhöht und hat also nach dem ersten Schleifendurchlauf den Wert 2.
Nehmen wir jetzt einmal an, ich hätte am Anfang für n den Wert 3 eingegeben. Dann
wird offenbar die Bedingung in locker erfüllt, und das Programm geht in den nächs-
ten Schleifendurchlauf. Nun steht aber auf summe der Wert 1 und auf i der Wert 2,
also wird die erste Wertzuweisung im Schleifenrumpf dazu führen, dass auf summe jetzt
der Wert 1 2 3 steht. Daraufhin wird wieder i um 1 erhöht, erhält also den Wert 3
und kann noch immer den Vergleichstest mit der Variablen n bestehen, denn auch die
steht auf 3. Somit kommt es zu einem letzten Durchlauf meiner Schleife, noch einmal
werden die Anweisungen ihres Schleifenrumpfes durchlaufen. Was dabei passiert, ist
wohl klar: Der aktuelle Wert von i wird auf den aktuellen Wert von summe addiert,
das ergibt 3 3 6, und anschließend wird i traditionsgemäß um 1 erhöht. Damit ver-
lässt i allerdings den grünen Bereich der Schleifenbedingung, denn kaum einer wird
behaupten wollen, dass 4 3 gilt. Die do-Schleife ist also beendet und die Summe ist
berechnet.
22 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Summe
Ausgabe: Bitte eine natürliche Zahl eingeben
einlesen n
n>0?
wahr falsch
summe = 0
i=1
Ausgabe: Hier liegt
summe = summe + i
keine natürliche Zahl vor
i = i+1
solange i <= n
Ich will es mit der C-Programmierung nicht übertreiben und mich hier auf die Bespre-
chung der Struktogramme beschränken. Nur eines noch: Warum stand in dem allgemei-
nen Struktogramm aus Abb. 2.5 der Begriff do-Schleife? Das liegt daran, dass in einem
C-Programm jede nicht abweisende Schleife mithilfe des do-Kommandos realisiert
wird. Im nachfolgenden Merksatz sage ich dazu noch eine Kleinigkeit.
Die do Schleife, auch nicht abweisende Schleife oder fußgesteuerte Schleife ge-
nannt, erlaubt es, einen Anweisungsblock wiederholt durchführen zu lassen, sofern
bestimmte Durchführungsbedingungen erfüllt sind. In C lautet das Schema der do
Schleife:
do
{
Anweisungen
}
while(Bedingungen);
Ich verschweige Ihnen nicht, dass es mit der abweisenden Schleife und der Zählschleife
noch zwei weitere Schleifenarten gibt, aber um einen Eindruck vom Prinzip der Schleife
zu bekommen, dürfte die nicht abweisende Schleife völlig ausreichen. Tatsächlich ist da-
mit die Einführung in die Prinzipien der strukturierten Programmierung auch schon be-
endet, und nach der einen oder anderen Übung kommen wir zu etwas anderem: der Ob-
jektorientierung.
Übung 2.1:
Formulieren Sie einen Algorithmus in Form eines Struktogramms, der je nach Aus-
wahl des Benutzers einen Eurobetrag in einen Dollarbetrag oder einen Dollarbetrag
in einen Eurobetrag umrechnet.
GDI01 23
© HfB, 02.12.20, Berk, Eric (904709)
Übung 2.2:
Schreiben Sie ein Struktogramm, das den folgenden Algorithmus abbildet.
Es ist nach einem stark vereinfachten Verfahren die Steuer auf das monatliche Ein-
kommen auszurechnen. Das Einkommen ist über die Tastatur einzugeben. Die ersten
600 Euro sind steuerfrei. Die nächsten 600 Euro werden mit 30 % besteuert und der
Rest mit 40 %. Beträgt allerdings das gesamte monatliche Einkommen mehr als
15000 Euro, so sind statt 40 % 50 % zu veranschlagen.
Übung 2.3:
Schreiben Sie ein Struktogramm, das den folgenden Algorithmus abbildet.
In einem Labor sind Messreihen zu verarbeiten. Aus Erfahrung weiß man, dass keine
Werte anfallen, die über 1000 bzw. unter –1000 liegen und dass es mindestens einen
Messwert gibt. Dem Benutzer liegt nun eine Liste von Messwerten vor, die er in den
Computer eingeben soll, wobei die Eingabe beendet ist, sobald ein Wert über 1000
oder unter –1000 eingegeben wird. Es ist dabei zu beachten, dass dieser letzte Wert
nicht mehr zur eigentlichen Messreihe gehört, sondern nur dem Abbruch der Einga-
be dient, und dass der erste Wert der Messreihe in jedem Fall ein zulässiger Wert ist.
Das Programm soll dann folgende Größen berechnen und ausgeben:
• die Summe aller positiven Werte und den Durchschnitt dieser Werte
• die Summe aller negativen Werte und den Durchschnitt dieser Werte
• die Summe aller Werte und den Durchschnitt aller Werte
Übung 2.4:
Eine lineare Gleichung hat die Form ax b 0 mit den bekannten Koeffizienten a
b
und b und der Unbekannten x. Die Lösung der Gleichung lautet x , wobei man
a
auf die Sonderfälle, die entstehen können, wenn der eine oder andere Koeffizient den
Wert 0 annimmt, achten muss. Schreiben Sie einen Algorithmus in Form eines
Struktogramms, mit dessen Hilfe man eine beliebige Anzahl von linearen Gleichun-
gen lösen kann. Vor jeder neuen Gleichung soll der Benutzer gefragt werden, ob er
eine weitere Gleichung lösen will. Bei einer positiven Antwort wird eine Gleichung
eingegeben und gelöst, bei einer negativen wird das Programm beendet.
2.2 Objektorientierung
Man kann mit einem gewissen Recht sagen, dass ohne die Prinzipien der strukturierten
Programmierung, ohne die gerade besprochenen Kontrollstrukturen die Welt der Infor-
matik deutlich ärmer wäre. Trotzdem ist strukturierte Programmierung nicht alles, die
Welt hat sich auch nach der Erfindung der Struktogramme weitergedreht und neue Ide-
en sind aufgekommen. Eine davon, die heute eine große Rolle spielt, ist die Idee der Ob-
jektorientierung. Worum geht es dabei? Während wir uns bisher auf das sogenannte
prozedurale Programmieren konzentriert haben, bei dem die Aktivität, der Ablauf, die
Prozedur im Mittelpunkt des Interesses steht, kommt jetzt noch etwas hinzu. Jede Akti-
vität, jeder Vorgang hat einen direkten Bezug zu einem Objekt, das nun in den Fokus
rückt und meine Aufmerksamkeit beansprucht. Selbstverständlich müssen die Aktivitä-
24 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
ten selbst noch immer programmiert werden, und das geschieht noch immer nach den
Methoden der strukturierten Programmierung. Aber alles, was geschieht, wird bei der
objektorientierten Programmierung einen Bezug zu einem Objekt haben, wird seine
Eigenschaften ändern oder schlicht dafür sorgen, dass mit dem Objekt etwas passiert.
Was soll das heißen? Denken Sie beispielsweise an ein Auto und an die Aktivität des
Fahrens. Konzentriert man sich vor allem auf die Aktivität, so kann man darüber spre-
chen, dass Autos fahren oder Züge oder auch Heißluftballons – es geht vor allem um die
Aktivität, genau wie es bei der strukturierten Programmierung vor allem darum ging,
bestimmte Aktivitäten durch ein Struktogramm oder ein C-Programm abzubilden. Nun
ist aber der Fahrvorgang bei einem Auto ganz anders als bei einem Heißluftballon, und
diesen Unterschieden kann man Rechnung tragen, indem man die Aufmerksamkeit dem
Auto zuwendet und nur noch vom Fahren eines Autos spricht. Jede Aktivität wird – um
wieder allgemeiner zu sprechen – bei der objektorientierten Betrachtung einem be-
stimmten Objekt zugeordnet und kann nicht mehr freischwebend mal für diesen, mal
für jenen Träger verwendet werden.
Das bedeutet selbstverständlich, dass man dem zu schreibenden Programm genau sagen
muss, welche Objekte zulässig sind und welche Aktivitäten diese Objekte durchführen
dürfen, denn Aktivitäten ohne Objekte sind nicht mehr erlaubt. Und da man zum Bei-
spiel nicht jedem Auto einzeln sagen will, dass es fahren darf, wird man die Gesamtheit
aller Autos zur Menge aller Autos zusammenfassen, wobei jedes Mitglied dieser Menge
die Eigenschaft hat, fahren zu dürfen. Man spricht im Rahmen der objektorientierten
Programmierung allerdings nicht von Mengen, sondern von Klassen. Alles spielt sich
innerhalb von Klassen ab, und kein Datenelement, keine Aktivität kann sich diesem
Prinzip entziehen. Eine Klasse ist im Grunde genommen nichts weiter als eine abstrakte
Beschreibung eines Objekts mit seinen Daten und seinen möglichen Aktivitäten, und
das heißt, in einer Klassendeklaration wird in aller Regel beschrieben, wie die zukünfti-
gen Objekte aussehen sollen , die ich verarbeiten will, und was man mit ihnen anstellen
kann. Dass man Eigenschaften von Elementen zu einer Einheit zusammenfasst, ist
nichts Neues, das macht man auch im Alltag immer wieder. Aber dass man auch noch
die möglichen Aktionen gleich mit definiert, ist ein völlig neues Konzept mit weitrei-
chenden Konsequenzen, und genau das ist ein wesentlicher Punkt der Objektorientie-
rung. In einer einzigen Struktur, eben der Klasse, werden sowohl die Daten als auch die
Operationen vereinigt, die man auf diese Daten anwenden kann – ein Konzept, das man
oft als Kapselung bezeichnet. Dadurch wird sichergestellt, dass nur die Operationen, die
in einer Klasse definiert wurden, die Daten eines Objekts dieser Klasse ändern können,
sodass also der Zugriff auf Objekte einer Klasse ausschließlich mithilfe der vordefinier-
ten Aktionen, der sogenannten Methoden dieser Klasse, erfolgen soll.
Mir ist schon klar, wie abstrakt sich das anhört, aber gerade im Zusammenhang mit ob-
jektorientiertem Design muss man sich an ein wenig Abstraktion gewöhnen. Wie schon
bei der strukturierten Programmierung will ich Ihnen auch hier nur ein paar Grundzüge
zeigen, und natürlich werden wir nicht bei der abstrakten Beschreibung der Prinzipien
stehen bleiben. Da es sich bei der objektorientierten Programmierung um eine Program-
miermethode handelt, gibt es auch Programmiersprachen, die objektorientiert arbeiten,
und eine der bekanntesten dieser Sprachen ist Java. Die wenigen Programmierbeispiele,
die ich Ihnen im Folgenden zeige, sind in Java geschrieben.
GDI01 25
© HfB, 02.12.20, Berk, Eric (904709)
Kehren wir also zurück zur Klasse der Autos, die ich aus naheliegenden Gründen mit
dem Namen Auto bezeichne. Ein Auto ist ohne Frage ein Objekt, und dieses Objekt hat
bestimmte Eigenschaften, die man aufschreiben kann. Es hat eine Marke, eine Farbe und
ein Gewicht, wie jeder andere Körper. Und dazu weist es noch eine bestimmte Höchst-
geschwindigkeit auf, die von Auto zu Auto verschieden ist. Jedes Auto hat diese Eigen-
schaften, also kann ich eine Klasse Auto bilden, die mir genau diesen Sachverhalt in
eine Java-Klasse übersetzt. Die Klasse lautet dann:
class Auto
{
String marke;
String farbe;
int gewicht;
int geschwindigkeit;
}
Der Name der Klasse fängt mit einem Großbuchstaben an, und das ist kein Zufall: Klas-
sennamen sollten Sie immer mit einem Großbuchstaben beginnen lassen, das ist zwar
nicht zwingend, aber eine weitverbreitete Konvention. Die Mitglieder dieser Klasse sind
dann die schon lange angekündigten Objekte. Objekte der gleichen Art, die man durch
die gleichen Eigenschaften beschreiben kann, werden eben in einer Klasse zusammen-
gefasst; die Klasse ist sozusagen der große Topf, in dem alle Objekte der gleichen Art
schwimmen. Man nennt deshalb die Objekte auch Instanzen der Klasse.
Die Eigenschaften der Objekte der Klasse Auto habe ich schon aufgelistet: Ein Auto wird
beschrieben durch Marke, Farbe, Gewicht und Geschwindigkeit. Im Programmtext habe
ich das ausgedrückt durch vier sogenannte Membervariablen oder auch Attribute, de-
ren einzige Rolle es ist, die nötigen Eigenschaften der Objekte aufzunehmen. Ich musste
daher die vier Attribute marke vom Typ String, farbe vom Typ String,
gewicht vom Typ int und schließlich geschwindigkeit vom Typ int anlegen:
Ohne Variablen keine Eigenschaften, wo sollte ich die Eigenschaften sonst hinschreiben?
Vermutlich wird es Sie nicht sehr wundern, dass eine Variable vom Typ int der Auf-
nahme einer ganzen Zahl, also einer Integer-Zahl dient, während eine Variable vom Typ
String Zeichenketten, also eben Strings, aufnehmen kann.
Nun habe ich also die Eigenschaften der Klasse Auto festgelegt, nach denen sich in Zu-
kunft jedes Auto richten muss. Die Klasse ist aber nur ein Sammelbehälter, eine Art Ga-
rage, in die jedes Auto hineinpasst; durch das Anlegen der Klasse wird noch kein einzi-
ges Objekt kreiert. Nur weil Sie eine Garage kaufen, wird sich noch kein einziges Auto
in die Garage verirren, es sei denn, Sie kaufen eines und fahren es hinein. Bei Objekten
ist das nicht anders, man muss sie erst anlegen, bevor es sie gibt. Ein Objekt der Klasse
Auto können Sie nun in der Programmiersprache Java durch
Auto meinAuto;
deklarieren, aber damit existiert es genau genommen immer noch nicht, sondern Sie tei-
len nur mit, dass meinAuto ein Objekt der Klasse Auto werden soll. Ist das Objekt
meinAuto auf diese Weise erst einmal deklariert, also angekündigt, genügt das Kom-
mando
meinAuto new Auto();
26 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
schreiben und auf diese Weise Deklaration und Definition auf einmal hinter sich brin-
gen. Dass der Objektname hier mit einem kleinen Buchstaben beginnt, ist übrigens kein
Zufall: Das sollten Sie immer so machen, auch wenn es sich nur um eine Konvention
handelt.
Das macht einen etwas umständlichen Eindruck, aber man kann sich daran gewöhnen.
Wichtig ist, dass Sie erst eine Klasse definieren müssen, und dann durch Deklaration
und Definition einzelne Objekte dieser Klasse anlegen können.
Nehmen wir also an, Sie haben erstens die Klasse Auto deklariert und zweitens das
konkrete Autoobjekt meinAuto angelegt. Dann ist das bisher nichts weiter als ein Auto
ohne Eigenschaften, und wir sollten ihm schnellstens ein paar Eigenschaften verleihen.
Das geht ganz einfach mit der sogenannten Punktnotation. Das Autoobjekt heißt
meinAuto das erste Attribut heißt marke. Wenn es sich nun um ein Auto der Marke
„Opel“ handeln soll, dann können Sie dem Objekt diese Eigenschaft mit dem Kommando
meinAuto.marke"Opel";
zuweisen. Natürlich funktioniert das mit den anderen Attributen nicht anders, sodass
Sie durch die Angaben
meinAuto.marke"Opel";
meinAuto.farbe"blau";
meinAuto.gewicht1300;
meinAuto.geschwindigkeit180;
einen blauen Opel definiert haben, der 1300 Kilogramm wiegt und mit maximal 180 Ki-
lometern pro Stunde auf der Autobahn fährt.
Schön und gut, aber was nützen mir diese Angaben, wenn ich sie zwar in die Klasse hi-
neinbringe, aber nie mehr wieder heraus? Das kann nicht passieren, auch davor bewahrt
mich die Punktnotation. Will ich beispielsweise die Farbe meines Autos meinAuto wis-
sen, brauche ich nur das Java-Kommando
System.out.println(meinAuto.farbe);
abzusetzen, und schon wird der entsprechende String farbe von meinAuto am Bild-
schirm ausgegeben. Das eher unschöne System.out.println entspricht dabei dem
printf, das Sie schon bei unseren C-Programmen gesehen haben.
GDI01 27
© HfB, 02.12.20, Berk, Eric (904709)
Eine Klasse wird in der Programmiersprache Java deklariert durch die Angabe eines
Klassennamens und von Membervariablen oder auch Attributen in der Form
class Klassenname
{
Attribut 1;
Attribut 2;
.......
}
Dabei sollte der Klassenname mit einem Großbuchstaben anfangen, was aber nicht
zwingend ist.
Ein Objekt der Klasse wird deklariert durch
Klassenname objektname;
vereinigen. Man bezeichnet ein Objekt einer Klasse auch als eine Instanz der Klasse.
Auf die Attribute eines angelegten Objektes kann man mit der Punktnotation
objektname.Attribut zugreifen.
Das war noch nicht alles, was ich Ihnen über Klassen und Objekte zu sagen habe. Um
weiterzukommen, muss ich aber erst einmal einen neuen Begriff einführen: die Metho-
de. Methoden dienen dazu, die Aktivitäten zu beschreiben, die mit meinen Objekten
möglich sind, denn bisher habe ich ja nur Daten beschrieben und nicht gesagt, was man
damit machen kann. Was kann ich zum Beispiel mit meinen Autoobjekten anstellen? Es
handelt sich nicht um die echten Autos, sondern nur um eine Autoverwaltung im Com-
puter, das dürfen Sie nicht vergessen. Trotzdem wäre es sinnvoll, zu jedem Auto unbü-
rokratisch eine Beschreibung ausgeben oder seine Farbe ändern zu können. Kein Prob-
lem, wird sofort erledigt, indem ich zwei Methoden definiere, die mir diese Arbeit
abnehmen. Damit Sie sehen, wie eine Methode in eine Klasse integriert wird, zeige ich
Ihnen hier die komplette um die beiden Methoden erweiterte Klasse.
class Auto
{
String marke;
String farbe;
int gewicht;
int geschwindigkeit;
String beschreiben()
{
return "Der Auto ist ein " marke ", seine Farbe ist "
farbe ", sein Gewicht betraegt " gewicht
" Kilogramm, und seine Geschwindigkeit betraegt "
geschwindigkeit ".";
28 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
}
}
Eigentlich passiert auch hier nicht viel Neues. Methoden sind Funktionen, die man auf
ein Objekt anwendet. Die Methode beschreiben() verlangt keinen Parameter, kei-
nen Input, da sie sich nur auf das aktuelle Auto und sonst auf gar nichts bezieht; also ist
ihre Parameterliste leer. Dagegen soll sie eine das Auto beschreibende Zeichenkette aus-
geben, braucht also den Rückgabetyp String. Dieses String bedeutet daher nur, dass
die Methode beschreiben() eine Zeichenkette liefern soll. Im sogenannten Metho-
denrumpf wird dann angegeben, was die Methode zu tun hat: Sie soll eine Beschreibung
des Autos liefern, um das es gerade geht, indem sie seine Eigenschaften in einem Satz
zusammenfasst und an die aufrufende Stelle zurückgibt. Beachten Sie dabei, dass ich
hier nicht die Punktnotation wie zum Beispiel meinAuto.marke verwendet, sondern
einfach nur marke geschrieben habe. Das muss auch so sein, denn ich habe ja noch gar
kein Objekt angelegt. Zurzeit beschreibe ich noch die Klasse mit ihren Membervariablen
und ihren Methoden, Objekte der Klasse existieren noch nicht. So etwas wie return
marke bedeutet also nur: Wenn für irgendein Objekt später mal diese Methode aufge-
rufen werden sollte, dann gib bitte die Marke des Autos zurück, um das es dann gehen
wird.
Was kann man denn nun anfangen mit einer solchen Klasse? Eine Klasse der vorgestell-
ten Art bildet nur einen Rahmen, ein Muster, nach dem sich alle Objekte zu richten ha-
ben, aber sie ist kein Programm, das man ausführen kann. In der Klasse Auto lege ich
die Struktur meiner Autodaten – das sind die Attribute – und die mit den Autodaten
möglichen Operationen – das sind die Methoden – ab. Ich führe aber noch keine kon-
krete Verarbeitung durch, ich sage nur, wie Verarbeitungen auszusehen haben, wenn sie
irgendwann einmal durchgeführt werden.
Nach diesem Prinzip läuft es eigentlich immer. Sie definieren innerhalb einer grundle-
genden Klasse, wie Ihre Datenstrukturen aussehen und welche Operationen damit er-
laubt sein sollen. Wer auch immer dann mit Ihrer Autoklasse und ihren Objekten han-
tieren will, muss sich nach dem richten, was Sie vorher in der Klasse Auto festgelegt
haben. Jede weitere Verarbeitung von Autoobjekten muss mit den Methoden auskom-
men, die in der Autoklasse zur Verfügung gestellt werden. Das ist einerseits eine Ein-
schränkung, weil man sich an dem vorhandenen Methodenbestand orientieren muss.
Andererseits ist es aber auch ein ungeheurer Vorteil, denn wenn diese Methoden anstän-
dig dokumentiert sind, muss man sich für weitere Entwicklungen nicht mehr darum
kümmern, wie sie programmiert sind: Der Entwickler muss dann nur noch wissen, wel-
che Parameter sie brauchen und was sie liefern; ihr Innenleben interessiert nicht mehr.
Gerade das macht die objektorientierte Programmierung für größere Projekte so inter-
essant. Ein Entwickler programmiert eine bestimmte Klasse, legt die Datenstrukturen
und Methoden fest und dokumentiert Input- und Outputverhalten der Methoden. Jeder
andere Entwickler kann dann auf diese Klasse, ihre Objekte und ihre Methoden zurück-
greifen, ohne sich Gedanken darüber machen zu müssen, wie die Methoden nun im Ein-
zelnen programmiert sind. Um also beispielsweise die Eigenschaften eines Autos auszu-
geben, muss das Hauptprogramm auf die oben definierte Methode beschreiben()
zurückgreifen, die genau diese Eigenschaften liefert. Er darf streng genommen die
Punktnotation für die Attribute im Rahmen eines Hauptprogramms gar nicht anwen-
den, weil er damit das Prinzip, sich nur über die vordefinierten Methoden den Objekten
zu nähern, außer Kraft setzen würde. Und weil in genau diesem Prinzip die Hauptidee
der Kapselung besteht, sollte man sich tunlichst auch daran halten.
GDI01 29
© HfB, 02.12.20, Berk, Eric (904709)
Eine Methode ist eine Funktion, die immer für ein bestimmtes Objekt ausgeführt
wird. Sie wird innerhalb der Klassendeklaration festgelegt und kann dann mithilfe
der Punktnotation für jedes Objekt dieser Klasse ausgeführt werden.
Innerhalb einer Klasse legt man also die Datenstruktur und die erlaubten Methoden
fest. Von anderen Klassen aus kann dann mithilfe der Methoden mit den konkreten
Objekten der ursprünglichen Klasse gearbeitet werden.
Wenn Sie also ein konkretes Objekt namens meinAuto angelegt haben, dann können
Sie in der Programmiersprache Java die Beschreibung dieses Autos durch das Komman-
do
System.out.println(meinAuto.beschreiben())
Übung 2.5:
Schreiben Sie eine Java-Klasse Linear, mit der linerare Gleichungen der Form
ax b 0 und ihre Lösungen modelliert werden sollen. Die Klasse soll zwei Attri-
bute a und b für die beiden Koeffizienten der Gleichung enthalten; der in der Spra-
che C als float bezeichnete Datentyp heißt hier double. Weiterhin soll es eine
Methode loesen()geben, mit der die Lösung einer Gleichung berechnet wird. Sie
können sich dabei auf den Fall beschränken, dass a und b von null verschieden sind.
Übung 2.6:
Entwerfen Sie eine Klasse Rechteck, die zur Modellierung beliebiger Rechtecke
dient. Welche Attribute muss diese Klasse haben? Welche Methoden brauchen Sie,
wenn Fläche und Umfang des jeweiligen Rechtecks berechenbar sein sollen? Wie
kann man dann von einer anderen Klasse aus diese beiden Größen berechnen? Mit
Ausnahme der letzten Frage kann die Aufgabe verbal beantwortet werden.
30 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Übung 2.7:
Erweitern Sie die Klasse Linear aus Übung 2.5 um eine Methode
kommentieren(), die eine Zeichenkette erzeugt. Es soll sich dabei um einen Kom-
mentar handeln, der die Fälle a 0 und b 0 bzw. a 0 und b 0 beschreibt. Die
nötige Auswahl hat in Java die gleiche Struktur wie in der Programmiersprache C,
die Abfrage, ob a 0 gilt, lautet if (a 0).
2.3 Komplexität
Bisher habe ich mich vor allem mit der Frage beschäftigt, wie man ein gegebenes Prob-
lem mithilfe eines passenden Algorithmus lösen kann. Das ist aber nicht alles. Es kann
nämlich vorkommen, dass es verschiedene Algorithmen zur Lösung des gleichen Prob-
lems gibt, so wie es beispielsweise mehrere Möglichkeiten gibt, mit dem Auto von Ihrem
derzeitigen Standort an einen anderen Ort Ihrer Wahl zu gelangen. Bei einer Autofahrt
wird man den Weg wählen, der nach irgendeinem Kriterium der günstigste ist: den kür-
zesten Weg, den bequemsten oder auch den mit dem geringsten zu erwartenden Sprit-
verbrauch. Bei Algorithmen ist es ähnlich, denn Sie werden natürlich unter verschiede-
nen zur Verfügung stehenden Algorithmen den mit dem geringsten
Ressourcenverbrauch einsetzen.
Aber was bedeutet das? Üblicherweise unterscheidet man zwischen zwei Arten von Res-
sourcen: der verbrauchten Zeit und dem benötigten Speicherplatz. Entsprechend werden
auch zwei Arten von Komplexität unterschieden. Untersucht man, wie viel Zeit ein Al-
gorithmus-Durchlauf verbraucht, so spricht man von Zeitkomplexität; will man hinge-
gen wissen, wie viel Speicherplatz bei einem Durchlauf des Algorithmus verbraucht
wird, so redet man über die Speicherkomplexität. Natürlich hängt beides von der Men-
ge bzw. der Größe der verarbeiteten Daten ab: Wenn Sie nur eine Gehaltsabrechnung
vornehmen, dann geht das schneller als bei 10000 Abrechnungen, und wenn Sie die
Quadratwurzel aus 16 berechnen wollen, so muss man davon ausgehen, dass das weni-
ger Zeit verbraucht als die Bestimmung der Wurzel aus 1522756. Es wird also einen Pa-
rameter n geben, der die jeweilige Komplexität beeinflusst. Am Beispiel der Zeitkomple-
xität werde ich Ihnen das jetzt einmal zeigen.
GDI01 31
© HfB, 02.12.20, Berk, Eric (904709)
Primzahl
Ausgabe: Eingabeaufforderung für
eine natürliche Zahl n>2
einlesen n
setze i=2
setze i = i+1
Lag Teilbarkeit
vor?
wahr falsch
Ausgabe: Ausgabe:
n ist keine Primzahl n ist Primzahl
Bei der Verschlüsselung von Daten ist man immer wieder auf Primzahlen angewiesen,
also auf von 1 verschiedene natürliche Zahlen, die nur durch 1 und durch sich selbst teil-
bar sind. Es gibt leider keine Formel, mit der man Primzahlen ausrechnen könnte, und
deshalb haben Sie, sofern Sie testen wollen, ob eine gegebene Zahl n eine Primzahl ist,
keine Wahl: Sie müssen durch alle Zahlen teilen, die als Teiler überhaupt infrage kom-
men, also durch alle natürlichen Zahlen größer als 1 und kleiner als n. Falls keine dieser
Divisionen aufgeht, lag eine Primzahl vor; falls Sie dagegen einmal erfolgreich dividiert
haben, ist Ihr n nur eine ganz gewöhnliche Zahl und keine Primzahl. Wie man diese ver-
bale Beschreibung in ein Struktogramm umsetzt, sehen Sie in Abb. 2.7.
Was genau passiert hier? Sie geben eine Zahl n 2 ein und testen mithilfe einer Schleife,
ob dieses n durch irgendetwas teilbar ist. Zu diesem Zweck beginnen Sie mit dem po-
tenziellen Faktor i 2 und erhöhen, solange keine Teilbarkeit vorliegt, dieses i so lange
um 1, bis entweder die Division von n durch i aufgeht oder aber i nicht mehr kleiner als
n ist und somit den Wert n erreicht hat. Die nachfolgende Ausgabe gibt dann je nach
erzieltem Ergebnis bekannt, ob es sich bei n um eine Primzahl handelt oder nicht. Die
Frage ist nun: Wie viele Schritte braucht dieser Algorithmus im schlimmsten Fall? Of-
fenbar tritt der schlimmste Fall genau dann ein, wenn die Schleife nicht deshalb vorzei-
tig abgebrochen werden kann, weil eben Teilbarkeit vorlag, also wenn n eine Primzahl
ist. Da ich aber im Vorhinein nicht wissen kann, ob oder wann eine der Testdivisionen
aufgehen wird – sonst bräuchte ich den Algorithmus gar nicht – muss ich davon ausge-
hen, dass die Schleife vollständig durchlaufen wird, und das heißt, dass ich alle Zahlen
zwischen i 2 und i n – 1 prüfe. Das ergibt n – 2 Durchläufe. Vor der Schleife habe
ich noch eine Eingabeaufforderung und eine Eingabe, nach der Schleife finden Sie eine
Auswahl, in der eine Ausgabe vorgenommen wird. Das sind also noch einmal 4 Opera-
tionen, sodass ich insgesamt auf n 2 Aktionen komme. Innerhalb jedes Schleifen-
durchlaufs muss ich allerdings testen, ob n durch i teilbar ist, und dann i um 1 erhöhen,
was mindestens zwei Operationen darstellt. Der Einfachheit halber gehe ich hier davon
aus, dass der Test auf Teilbarkeit sich wirklich in einer einzigen Operation durchführen
lässt, die nicht länger dauert als alle anderen Operationen wie Eingabe oder Hochsetzen
einer Variablen. Das bedeutet, dass ich bei meinen Schleifendurchläufen 2 · (n – 2) 2n
– 4 Operationen durchführen muss und somit der Testalgorithmus mit insgesamt 2n –
4 4 2n Operationen zu Buche schlägt.
32 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Primzahl
Ausgabe: Eingabeaufforderung für
eine natürliche Zahl n>2
einlesen n
setze i=2
setze i = i+1
Lag Teilbarkeit
vor?
wahr falsch
Ausgabe: Ausgabe:
n ist keine Primzahl n ist Primzahl
Sie sehen hier deutlich die Abhängigkeit der Anzahl der nötigen Operationen von der
Eingabegröße n, denn je größer die Zahl n ist, desto mehr Operationen wird der Algo-
rithmus benötigen und desto mehr Zeit wird er brauchen. Geht man davon aus, dass alle
Operationen wie Eingabe, Ausgabe, Addition oder auch Teilbarkeitsprüfung im Wesent-
lichen den gleichen Zeitbedarf haben, so kann man folgern, dass der Algorithmus zum
Primzahltest 2n Zeiteinheiten verbrauchen wird. Man macht es sich aber üblicherweise
noch einfacher und sagt schlicht, der Algorithmus habe eine Zeitkomplexität der Grö-
ßenordnung n, was man mit der Abkürzung O(n) beschreibt. Wenn Sie also einem Al-
gorithmus mit der Zeitkomplexität O(n) begegnen, dann ist damit nur gemeint, dass er
– abhängig vom Parameter n – etwa c · n Zeiteinheiten für sich beanspruchen kann, wo-
bei c eine feste Zahl ist, die nicht von n abhängt. Es ist also ganz egal, ob die konkrete
Zahl der Operationen bei 2n, bei 3n 4 oder gar bei 7n – 6 liegt, man hat es in jedem
Fall mit der Zeitkomplexität O(n) zu tun. Konstante Summanden bei der Anzahl der
Operationen wie in 3n 4 oder in 7n – 6 vernachlässigt man gerne, weil sie für große
Werte von n ohnehin keine Rolle mehr spielen: Ob Sie nun 300004 oder 300000 Opera-
tionen durchzuführen haben, kann Ihnen ziemlich egal sein.
Ist T(n) die von n abhängige Laufzeit eines Algorithmus und gibt es eine Zahl c 0,
sodass für alle n die Beziehung T (n ) c n gilt, so schreibt man kurz T(n) O(n) und
sagt, die Zeitkomplexität ist von der Größenordnung O(n).
Das Symbol O(n) wird als ein Landau-Symbol bezeichnet.
GDI01 33
© HfB, 02.12.20, Berk, Eric (904709)
ergebnis auf jeden Fall unter 5 liegen, denn wenn Sie durch eine Zahl größer als 29
teilen, dann kann nur ein Ergebnis unter 29 herauskommen, sonst würde das Produkt
der beiden Faktoren über 29 liegen. Der Faktor unterhalb von 6 müsste in diesem Fall
dem Algorithmus also schon lange aufgefallen sein, und da er das nicht ist, hat es ihn
nie gegeben. Es ist daher völlig ausreichend, die Schleife nur laufen zu lassen, solange
i n ist oder – anders gesagt – solange i 2 n gilt. Genau diese Durchlaufbedingung
macht den einzigen Unterschied zwischen dem ersten und dem zweiten Algorithmus
aus, wie Sie an den Struktogrammen unschwer ablesen können. Ich muss deshalb die
Schleife deutlich seltener laufen lassen, nur noch bis maximal n und nicht mehr bis
n, und das kann bei großen Werten von n schon einen Unterschied ausmachen. Natür-
lich wird man nun sagen, der verbesserte Algorithmus hat eine Zeitkomplexität von
O ( n ) und ist somit dem ursprünglichen Algorithmus vorzuziehen, weil er schneller
läuft.
Wie Sie gesehen haben, kann es also sehr verschiedene Komplexitäten geben, die man
dann mit der O-Notation ausdrückt. Ob O(n), O ( n ) , ob O (n 2 ) , O (n 3 ) oder gar
O (log2 n ) , sie alle beschreiben, wie viel Zeit beim Durchlauf eines Algorithmus ver-
braucht wird, also seine Zeitkomplexität. Sobald man also weiß, dass die Zeitkomplexi-
tät T(n) eines bestimmten Algorithmus kleiner oder gleich c g (n ) ist, wobei c eine feste
Zahl und g(n) eine Funktion wie g (n ) n oder g (n ) n 2 darstellt, kann man stets die
Aussage T(n) O(g(n)) treffen.
Sind f(n) und g(n) auf den natürlichen Zahlen definierte Funktionen mit positiven
Werten, so gilt f(n) O(g(n)), falls es eine konstante Zahl c gibt, sodass
f (n ) c g (n ) für alle hinreichend großen natürlichen Zahlen n gilt.
Bedenken Sie also: Auch für T(n) 3n2 16 ist T(n) O(n2), weil ab n 4 in jedem Fall
3n 2 16 4n 2 gilt.
Übung 2.8:
Gegeben sei der Algorithmus, der in dem Struktogramm aus Abb. 2.6 beschrieben
wird.
a) Bestimmen Sie die größtmögliche Anzahl der Operationen, die der Algorithmus
ausführen muss.
b) Welche Zeitkomplexität T(n) weist der Algorithmus in Abhängigkeit von der
Eingabe n auf, wenn man davon ausgeht, dass jede Operation gleich lang dauert?
Beschreiben Sie die Zeitkomplexität mit einem Landau-Symbol.
34 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Übung 2.9:
Es seien A und B zwei Algorithmen, die das gleiche Problem lösen. Für Eingaben mit
der Eingabegröße n benötigt Algorithmus A 500n2 – 16 Operationen, während Al-
1 11
gorithmus B n 3 n 7 Operationen braucht. Jede Operation dauert gleich lang.
2 2
a) Beschreiben Sie die Zeitkomplexität von A und von B mit einem Landau-Sym-
bol.
b) Welchen Algorithmus sollte man wählen, um das gegebene Problem bei einer
Eingabegröße von n 256 zu lösen?
c) Welchen Algorithmus sollte man wählen, wenn das Problem immer eine Einga-
begröße von mindestens n 1024 aufweist?
GDI01 35
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
Hier lernen Sie einige Vorläufer der heutigen Computer kennen, nämlich die
Analytische Maschine, die Turing-Maschine und die Von-Neumann-Architektur,
und Sie erfahren etwas über heutige Programmiersprachen und Betriebssysteme.
Bisher habe ich Ihnen gezeigt, worin die grundlegenden Probleme der Informatik beste-
hen, und Sie haben einen ersten Einblick in die Welt der Algorithmen gewonnen. Wie
Sie feststellen konnten, denkt man sich Algorithmen vor allem deshalb aus, weil man
Probleme mit ihnen lösen möchte, und dieser schlichte Umstand hat eine ebenso schlich-
te Folgerung: Irgendwo und irgendwie muss man diese Algorithmen auch durchführen
können, ohne alles selbst von Hand machen zu müssen, und zu diesem Zweck braucht
man einen Computer, einen Rechner. Rechenmaschinen gab es schon sehr früh, sofern
man den antiken Abakus dazurechnen will. Raffinierter waren dann die mechanischen
Rechenmaschinen aus dem 17. Jahrhundert, die beispielsweise Wilhelm Schickard, Blai-
se Pascal und Gottfried Wilhelm Leibniz gebaut haben, wobei Leibniz übrigens in die-
sem Zusammenhang das System der Dualzahlen zur Basis 2 erfand, das Sie im vierten
Kapitel noch beschäftigen wird.
Aber all diese Geräte waren eben nur Rechenmaschinen, die dem Benutzer zwar die
Durchführung der Grundrechenarten abnehmen konnten, mehr aber auch nicht. Wie
kam man denn eigentlich zum Prinzip der programmierbaren Maschine, die Algorith-
men ausführen kann? Ich will Sie hier nicht mit der gesamten Geschichte der Informatik
behelligen; das ist ein weites und interessantes Feld, aber man muss es mit der Historie
auch nicht zu weit treiben. Vorstellen werde ich Ihnen aber drei wichtige Konzepte, ei-
nes aus dem 19. und zwei aus dem 20. Jahrhundert, auf denen auch heute noch die Ar-
chitektur moderner Computer beruht: die Analytische Maschine von Charles Babbage,
die Turing-Maschine von Alan Turing und die Von-Neumann-Architektur. Die beiden
Maschinen wurden aus verschiedenen Gründen nie gebaut, aber ihre Konzepte sind
noch immer wichtig und einflussreich und haben beispielsweise die Von-Neumann-Ar-
chitektur beeinflusst, auf der auch die heutigen Computer basieren.
36 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
Die Analytische Maschine von Charles Babbage war, obwohl sie aufgrund mechani-
scher Probleme nie gebaut wurde, ein Vorläufer der modernen programmgesteuerten
Computer. Ihr Konstruktionsplan sah neben einer Ein- und einer Ausgabevorrich-
tung ein Rechenwerk, einen Speicher und ein Steuerwerk vor, das die Operationen
des Programms koordinieren sollte.
Übung 3.1:
Lochkarten wurden in der frühen Zeit der Datenverarbeitung oft zur Speicherung
von Daten verwendet. Diskutieren Sie die Vor- und Nachteile von Lochkarten als
Datenspeicher.
Übung 3.2:
Diskutieren Sie die Auffassung des Logikers Kurt Gödel, Programmiersprachen sei-
en unnötig, Logik genüge völlig.
GDI01 37
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
38 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
..... 1 1 1 .....
..... a 1 1 .....
..... a 1 1 b .....
Das alles klingt noch sehr abstrakt, und deshalb werde ich diese allgemeinen Bemerkun-
gen jetzt konkretisieren. Ich will eine Turing-Maschine bauen, die in der Lage ist, eine
beliebige, aber endliche Zahl von Einsen, die nebeneinander auf dem Band stehen, zu
verdoppeln. Hat man es also anfangs mit drei Einsen zu tun, sollen es am Ende sechs
sein, waren es anfangs nur zwei, dann sind es zum Schluss natürlich vier und so weiter.
Ich gehe ab jetzt davon aus, dass es sich zu Beginn um drei Einsen handelt und das Band
somit wie im ersten Teil von Abb. 3.1 aussieht. Ich werde nun das Turing-Programm
aufschreiben, das dieses Problem löst. Beim ersten Lesen werden Sie nicht viel verste-
hen, aber selbstverständlich werde ich das gesamte Programm anschließend erklären.
Mein Alphabet ist nicht groß; es besteht nur aus den vier Zeichen 1, a, b und □, wobei
□ für das Leerzeichen Blank steht. Und das Programm ist das folgende.
z 0 ,1 z1 , a , R
z1 ,1 z1 ,1, R
z1 , b z1 , b , R
z1 , z2 , b , L
z2 , b z2 , b , L
z2 ,1 z2 ,1, L
z2 , a z 0 ,1, R
z 0 , b z 0 ,1, R
z 0 , z E ,, H
Auf den ersten Blick eher unübersichtlich, aber ich werde das alles jetzt mit Ihnen
durchgehen. Ich gehe davon aus, dass der Lese- und Schreibkopf direkt über der ersten
1 steht; seine Position wird in Abb. 3.1 jeweils durch einen Pfeil angezeigt. z0 ist einer
der Zustände der Maschine, der als Anfangszustand betrachtet wird und somit sofort
zum Tragen kommt: Wenn die Maschine im Zustand z0 ist und dabei eine 1 liest – und
das ist sicher am Anfang der Fall –, dann soll sie in einen anderen Zustand z1 übergehen,
auf die aktuelle Stelle ein a schreiben und dann um eine Stelle nach rechts rücken, was
durch das Symbol R zum Ausdruck gebracht wird. Das ist der Inhalt der ersten Pro-
grammzeile. Dabei ist z1 nur ein Symbol für einen weiteren Zustand, den ich benutzen
werde, um die Verarbeitung weiter beschreiben zu können. In jedem Fall steht der Kopf
jetzt über der zweiten 1, und das Band sieht aus wie im zweiten Teil von Abb. 3.1. Dass
GDI01 39
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
die erste 1 nun durch ein a ersetzt wurde, dient nur der Markierung: Diese 1 wurde jetzt
als solche erkannt und soll nach den vorhandenen Einsen an die Kette angehängt wer-
den.
Die Maschine befindet sich nun im Zustand z1 und steht über dem zweiten Zeichen, also
einer 1, und das heißt, dass nun die zweite Programmzeile zieht. Folglich bleibt es beim
Zustand z1 und bei der dort stehenden 1, und der Kopf bewegt sich wieder um eine Stelle
nach rechts, wo er wieder eine 1 vorfindet. Daher zieht noch einmal die zweite Pro-
grammzeile, es bleibt bei der 1 und wir wandern nach rechts – bedenken Sie dabei, dass
immer nur die Programmzeile ausgewählt wird, die zu der aktuellen Kombination aus
Zustand und gelesenem Zeichen passt; die Reihenfolge der Programmzeilen spielt dage-
gen keine Rolle. Aber jetzt hat sich etwas geändert, denn der Kopf steht nun über dem
Leerzeichen □, weshalb die vierte Programmzeile ihr Existenzrecht beweist. Die Maschi-
ne geht daher in den Zustand z2 über, schreibt an die bisherige Leerstelle ein b und wan-
dert um eine Stelle nach links. Dieses b wird später zu einer 1 werden, im Moment muss
ich aber noch ein anderes Zeichen einsetzen, damit ich die neuen Einsen von den alten
unterscheiden kann.
Nun haben Sie die Situation aus dem dritten Teil von Abb. 3.1, die Turing-Maschine be-
findet sich im Zustand z2 und der Kopf steht über der dritten ursprünglichen 1. Somit
zieht hier die sechste Programmzeile, die dafür sorgt, dass Zustand und Zelleninhalt er-
halten bleiben und der Kopf wieder um einen Schritt nach links rückt. Das ändert nicht
viel, denn jetzt steht der Kopf wieder über einer 1 und muss deshalb infolge der sechsten
Programmzeile einen weiteren Schritt nach links rücken. Das a, das er dort vorfindet,
sorgt in Verbindung mit dem Zustand z2 für den Einsatz der siebten Programmzeile, die
dafür sorgt, dass die Maschine in den Zustand z0 übergeht, eine 1 dort hinschreibt, wo
eben noch ein a war, und schließlich um eine Position nach rechts rückt. Das Resultat
sehen Sie im ersten Teil von Abb. 3.2.
..... 1 1 1 b .....
..... 1 1 1 b b b .....
..... 1 1 1 1 1 1 .....
Sehen Sie, was inzwischen geschehen ist? Die erste 1 hatte ich in ein a umgeschrieben,
um zu markieren, dass sie am Ende der Einserkette angehängt werden soll. Das ist nun
erledigt, weshalb ich eben dieses a wieder zu einer 1 umwandeln kann. Und damit fängt
alles wieder von vorn an, denn der Lese- und Schreibkopf steht wieder über einer 1, die
ans Ende kopiert werden soll, und der aktuelle Zustand ist wie schon am Anfang z0. Die
aktuelle 1 wird also in ein a umgesetzt und der Kopf wandert nach rechts. Der einzige
Unterschied zu vorhin besteht darin, dass er unterwegs auf eine mit b belegte Speicher-
zelle trifft und damit auch die dritte Zeile des Programms zu ihrem Recht kommt, denn
40 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
nun wird dieses b an seiner Stelle gelassen, während der Kopf um eine Stelle nach rechts
rückt. Und sobald im nächsten Schritt der Kopf auf eine Leerstelle trifft, während der
Zustand immer noch auf z1 steht, beginnt der Weg zurück, wobei der Zustand auf z2 ge-
setzt wird. Nach einer Weile trifft der Kopf dann auf das vorher eingesetzte a, verwan-
delt es zurück in eine 1, und der Zustand wird wieder auf z0 gesetzt. Das kennen wir
schon, denn jetzt geht es weiter wie gehabt, nämlich mit der ersten Programmzeile.
Nachdem nun also dreimal hintereinander so ziemlich das Gleiche geschehen ist, ergibt
sich die Situation aus dem zweiten Teil von Abb. 3.2. Aber jetzt passiert etwas Neues.
Die Maschine ist wegen der siebten Programmzeile in den Zustand z0 übergegangen und
hat das dritte a durch eine 1 ersetzt. Nun trifft der Kopf allerdings auf ein b, was endlich
die achte Programmzeile wirksam werden lässt – und das gleich mehrfach. Alle drei b-
Zeichen werden nun unter Beibehaltung des Zustandes durch Einsen ersetzt, bis schließ-
lich nach dem letzten Rücken nach rechts die allerletzte Programmzeile aktiv wird: Die
Maschine geht in den Endzustand zE über, lässt das Leerzeichen rechts von der letzten
1, wie es ist, und macht nichts mehr, was durch das Symbol H für „Halten“ ausgedrückt
wird. Und damit haben wir die endgültige Situation aus dem dritten Teil von Abb. 3.2
erreicht. Sie können leicht nachprüfen, dass diese Turing-Maschine nicht nur für drei ge-
gebene Einsen funktioniert, sondern auch für jede beliebige Anzahl, sogar wenn gar kei-
ne 1 auf dem Band steht. In einer der Übungen haben Sie dazu Gelegenheit.
Ohne Frage ist das alles sehr ungewohnt, aber es ist ein einfaches Modell eines Compu-
ters, das erstens ausgesprochen viele Möglichkeiten in sich trägt und zweitens wegen der
geringen Zahl seiner Komponenten gut analysiert werden kann. Noch einmal: Mit Ma-
schinen dieser Art kann man alles berechnen, was überhaupt berechenbar ist. Natürlich
handelt es sich bei der hier beschriebenen Turing-Maschine um ein sehr spezielles Kon-
strukt, bei der das Programm fest in die Maschine eingebaut ist. Es ist aber auch möglich,
eine sogenannte universelle Turing-Maschine zu konstruieren, mit deren Hilfe man jede
beliebige spezielle Turing-Maschine programmieren kann. Und somit ist die Turing-Ma-
schine das einfachste existierende Modell eines allgemein einsetzbaren Computers.
Falls Sie einmal genauer sehen möchten, wie man sich an den Aufbau eines solchen Tu-
ring-Programms herantastet mit genau dem Ergebnis, das ich Ihnen hier gezeigt habe,
empfehle ich das unter Spannagel (2013) im Literaturverzeichnis angegebene Video.
Übung 3.3:
Weisen Sie nach, dass die oben beschriebene Turing-Maschine auch dann ihren
Zweck erfüllt, wenn auf dem Band zu Anfang keine 1 oder nur eine 1 steht.
GDI01 41
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
Übung 3.4:
Konstruieren Sie eine Turing-Maschine, die die Addition 3 4 7 in der folgenden
Form durchführt: Auf dem Band der Maschine ist die Zeichenkette „1111111“ ge-
geben, als Resultat soll auf dem Band die Zeichenkette „1111111“ stehen.
42 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
gut so. Schließlich wird von Problem zu Problem das Verhältnis zwischen reinen Daten
und Befehlen ein anderes sein, und wenn Sie zwei sauber getrennte Speicherbereiche
hätten, dann wäre je nach Art der Aufgabe die Gefahr der Speicherplatzverschwendung
recht groß. Da es aber nur einen Arbeitsspeicher gibt, in dem man ganz nach Bedarf mal
dieses und mal jenes parkt, kann das Problem der Verschwendung erst gar nicht auftre-
ten. Erwähnen sollte ich noch, dass man auch den Begriff des Speicherwerks zur Be-
zeichnung des Arbeitsspeichers verwendet.
Offenbar reicht es nicht, nur Daten und Befehle zu ihrer Verarbeitung bereitzustellen,
irgendwo muss auch die eigentliche Verarbeitung stattfinden, und der passende Ort da-
für ist der Prozessor, den man auch gern als Central Processing Unit, abgekürzt CPU,
bezeichnet. Er besteht aus einem Steuerwerk, das die organisatorische Arbeit erledigt,
und einem Rechenwerk, dem die eigentliche Aufgabe des Rechnens zugeordnet ist.
Auch für diese beiden Teile des Computerinnenlebens gibt es weitverbreitete englische
Bezeichnungen, nämlich Arithmetical Logical Unit, abgekürzt ALU, für das Rechen-
werk und Control Unit für das Steuerwerk. Tatsächlich ist die englische Bezeichnung
für das Rechenwerk präziser als die deutsche, denn im Rechenwerk wird zwar gerech-
net, aber erstens kann man dort auch logische Operationen ausführen und zweitens be-
ruht auch jeder Rechenvorgang in einem Computer auf logischen Schaltungen, die Sie
im fünften Kapitel kennenlernen werden. Beachten Sie übrigens, dass die im Arbeits-
speicher abgelegten Befehle ganz ordentlich Schritt für Schritt, also sequenziell, durch-
geführt werden und schon deshalb ein Rechner auf Basis der Von-Neumann-Architektur
als eine Art Realisierung einer Turing-Maschine betrachtet werden kann, bei der man
auch immer nur einen Schritt nach dem anderen machen darf.
Sie sollten aber nicht vergessen, wie wichtig auch das Steuerwerk für die korrekte Ver-
arbeitung der Daten ist, seine Rolle ist in etwa die eines Organisators oder Projektleiters
in einem Betrieb. Es sorgt dafür, dass die einzelnen Befehle des Programms aus ihren
Parkplätzen im Arbeitsspeicher geholt werden, es besorgt dem Rechenwerk die nötigen
Daten aus dem Arbeitsspeicher, damit das Rechenwerk auch etwas zu rechnen hat, es
veranlasst das Rechenwerk, mit den übermittelten Befehlen die übermittelten Daten zu
bearbeiten, und am Ende schaufelt es die berechneten Ergebnisse wieder in den Arbeits-
speicher, damit der Benutzer seine Freude daran hat. Kurz gesagt: Das Rechenwerk er-
ledigt die Rechenaufgaben, das Steuerwerk dagegen die organisatorischen Aufgaben.
Und wie kommt der Arbeitsspeicher an seine Daten? Das funktioniert mithilfe des Ein-
gabe-/Ausgabewerks, das die Ein- und Ausgabe von Daten vom und zum Anwender –
beispielsweise über Tastatur und Bildschirm – oder von und zu anderen Systemen über
bestimmte Schnittstellen steuert. Damit sind dann auch schon fast alle Komponenten
der Von-Neumann-Architektur beschrieben mit einer Ausnahme: Die eingegebenen Da-
ten müssen irgendwie ihren Weg zur CPU finden, also zum Rechen- und zum Steuer-
werk, und was berechnet wurde, sollte vielleicht auch wieder für die Ausgabe zur Ver-
fügung stehen, sonst hätte man sich das ganze Rechnen sparen können. Für diesen Hin-
und Hertransport der Daten und natürlich auch der Befehle steht das Bussystem zur Ver-
fügung, das nichts anderes zu tun hat, als Daten verschiedenster Art von hier nach da
zu transportieren.
GDI01 43
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
Eingabegeräte
Prozessor
Arbeitsspeicher
Steuerwerk
Daten
Rechenwerk Programme
Bussystem
Die klassische Architektur eines Computers nach John von Neumann haben wir damit
bereits besprochen, Sie können ihre grafische Darstellung in Abb. 3.3 sehen. Es kann
aber nicht schaden, einen Schritt weiter zu gehen und sich das eine oder andere damit
verbundene Problem anzusehen. Was macht man denn beispielsweise, wenn der Ar-
beitsspeicher zu klein ist? So ein Speicher, der auf elektronischen Schaltungen beruht,
kann nicht unbegrenzt groß werden, und es kann leicht passieren, dass nicht alle Befehle
oder alle Daten, die für eine bestimmte Verarbeitung gebraucht werden, in diesen Spei-
cher hineinpassen. Und das ist nicht mal das einzige Problem, ein anderes liegt noch viel
näher: Was passiert, wenn Sie Ihre Daten brav eingetippt, Ihre Programme fleißig dem
Arbeitsspeicher übermittelt haben und dann der Strom ausfällt? Das ist einfach zu be-
antworten, denn dann ist eben alles verloren, man kann von vorn anfangen und fühlt
sich so wie Sisyphos, der dazu verurteilt war, den immer gleichen Felsbrocken immer
wieder auf den immer gleichen Hügel zu rollen und kurz vor dem Ziel an der immer glei-
chen Stelle festzustellen, dass der elende Stein ihm schon wieder entglitt und nach unten
rollte.
Dieses Problem lässt sich aber lösen. Die Daten und Programme landen eben nicht
gleich im Arbeitsspeicher, sondern man pflegt sie in einem externen Speicher abzule-
gen, der nicht vom Stromfluss abhängig ist. Oft beruht ein externer Speicher auf den üb-
lichen Festplatten, die auf Magnetisierungsbasis arbeiten, aber auch CDs oder USB-
Sticks sind denkbar, die auf anderen Prinzipien basieren. In jedem Fall sind Ihre Einga-
ben dann vor plötzlichen Stromausfällen gesichert, und es muss nur für eine Verbindung
zwischen dem externen Speicher und dem Arbeitsspeicher gesorgt werden. Da der ex-
terne Speicher nicht so flüchtig ist wie der Arbeitsspeicher, nennt man ihn auch Fest-
speicher, und umgekehrt bezeichnet man den Arbeitsspeicher auch als den internen
Speicher.
Der externe Speicher hat also mehrere Funktionen. Erstens sorgt er dafür, dass eingege-
bene Daten und Programme mehrfach verwendbar sind, unabhängig von der Stromver-
sorgung des Computers. Zweitens kann man natürlich auch umgekehrt die Ergebnisse,
die der Computer liefert, nicht nur dem unsicheren Arbeitsspeicher überlassen, sondern
sie auch auf dem Festspeicher ablegen, damit die Nachwelt auch noch etwas davon hat;
insofern gehört auch die Festplatte zu den Ausgabegeräten. Und drittens können Sie mit-
hilfe des Festspeichers auch das oben angesprochene Problem der zu groß geratenen Pro-
gramme oder Datensätze lösen. Wenn beispielsweise ein Programm so groß geworden
ist, dass es nicht mehr vollständig in den Arbeitsspeicher, aber immer noch locker auf
die Festplatte passt, dann ist es möglich, das Programm eben nicht ganz, sondern nur
44 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
häppchenweise zusammen mit den jeweils nötigen Daten in den Arbeitsspeicher zu la-
den, dann vom Prozessor die geladenen Befehle ausführen zu lassen und nach ihrer Aus-
führung einfach die nächste Portion von Befehlen aus dem Festplattenspeicher zu holen.
Das klingt recht einfach, ist aber recht kompliziert, denn irgendjemand muss das alles ja
steuern. Sie werden kaum den Drang verspüren, Ihre mühsam geschriebenen Program-
me auch noch in leicht verdauliche Portionen aufzuteilen, Sie wollen das Programm
starten und zusehen, ob es richtig ist. Deshalb wird dieses häppchenweise Einlagern von
Programmteilen auch nicht vom Benutzer durchgeführt, sondern vom bereits erwähn-
ten Betriebssystem, einem speziellen Programm, das die Abläufe auf dem Rechner or-
ganisiert.
Unabhängig von der Von-Neumann-Architektur sollten wir aber noch einen Blick auf
den Arbeitsspeicher oder auch Hauptspeicher werfen. Er besteht einfach nur aus einer
Folge von vielen gleich großen Speicherelementen, die auf der Basis elektronischer
Schaltungen organisiert sind. Da es hier um elektrischen Strom geht und man nur un-
terscheidet, ob Strom fließt oder nicht, gibt es für ein Speicherelement genau zwei ver-
schiedene Möglichkeiten der Belegung: Strom fließt oder nicht, also 1 oder 0. Ein ein-
zelnes Element, das nur 0 oder 1 sein kann, wird als Bit bezeichnet, was nichts weiter
ist als eine Abkürzung für binary digit, also binäre Einheit, und darauf anspielt, dass Sie
sich bald mit der Arithmetik binärer oder auch dualer Zahlen befassen werden. Nun
kann man aber in einem Bit nicht allzu viele Informationen unterbringen, und deshalb
fasst man acht Bits zusammen zu einem Byte. Bei Licht betrachtet sind diese Bytes die
Grundeinheiten der Speicherung und nicht die kleineren Bits, aus denen sich die Bytes
zusammensetzen. Auf ein einzelnes Bit zuzugreifen ist gar nicht so einfach und außer-
dem meistens ziemlich sinnlos, da es Ihnen keine nennenswerte Information liefern
kann. Die Bytes im Arbeitsspeicher sind dagegen Einheiten, mit denen man etwas an-
fangen kann; es ist möglich, ein ganzes Zeichen wie etwa einen Buchstaben in Form ei-
nes Bytes abzuspeichern, und vor allem hat jedes Byte seine eigene Adresse im Haupt-
speicher. Einfacher kann man es sich gar nicht mehr vorstellen, die erste Adresse ist die
0, und dann geht es ganz schlicht linear weiter, indem man schön der Reihe nach num-
meriert. Sie können es sich vorstellen wie in einem Gefängnis, in dem die Bytes in or-
dentlich durchnummerierten Zellen gefangen gehalten werden, und vielleicht liegt es an
dieser Assoziation, dass man die mit einer Adresse gekennzeichneten Speicherbereiche
meistens als Speicherzellen bezeichnet. Da eine Adresse so gut ist wie eine andere, kann
man auf jede Speicherzelle so schnell zugreifen wie auf jede andere; man spricht deshalb
auch vom wahlfreien Zugriffsspeicher, auf Englisch Random Access Memory, abge-
kürzt RAM.
GDI01 45
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
Übung 3.5:
Wie Sie gesehen haben, besitzt ein heutiger Computer sowohl einen Arbeitsspeicher
als auch einen Festspeicher.
a) Untersuchen Sie, ob man einen modernen Computer auch ohne einen vollelekt-
ronischen Arbeitsspeicher konstruieren kann. Gehen Sie dabei auf die grundsätz-
liche Möglichkeit und auf das Problem der Arbeitsgeschwindigkeit eines solchen
Rechners ein.
b) Gehen Sie den Fragen aus a) nach unter der Voraussetzung, dass zwar ein voll-
elektronischer Arbeitsspeicher, dafür aber keine Festplatte vorhanden ist.
Übung 3.6:
Diskutieren Sie, warum man in der Zentraleinheit einerseits Speicherzellen zur pu-
ren Aufnahme von Daten und andererseits ein Rechenwerk zum Rechnen mit diesen
Daten vorsieht. Warum hat man nicht gleich die Speicherzellen des Arbeitsspeichers
mit Rechenfähigkeiten versehen und sich ein separates Rechenwerk gespart?
Übung 3.7:
Gegeben sei ein Speicher mit einer Kapazität von einem Gigabyte, also von 230 Byte.
Exakt um 12 Uhr wird in diesen Speicher ein Zeichen (also ein Byte) eingelesen, um
12:01 Uhr vier weitere Zeichen, um 12:02 Uhr 16 weitere Zeichen, um 12:03 Uhr 64
weitere Zeichen usw. Zu welchem Zeitpunkt kann keine weitere Zeichenreihe ein-
gelesen werden und wie viel Speicherplatz ist dann belegt?
46 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
GDI01 47
© HfB, 02.12.20, Berk, Eric (904709)
3 Rechner
werden. Und auch bei den Anwendungen, den sogenannten Apps, geht man restriktiv
vor: Es sind nur die offiziellen Apps von Apple zugelassen, die man auch nur über Apple
selbst beziehen kann.
Auch Microsoft hat etwas zum Markt der mobilen Geräte beigetragen. Das mobile Be-
triebssystem Windows Phone lehnt sich stark an das übliche Windows an, was man
auch an den Nummerierungen sieht, denn Windows Phone 8.1 ist ist die mobile Fassung
von Windows 8, und die neuere Fassung Windows 10 Mobile bezieht sich natürlich auf
die aktuelle Windows-Fassung Windows 10. Die mobilen Windows-Systeme lassen sich
zwar recht einfach an die persönlichen Wünsche des Benutzers anpassen, aber dennoch
sind sie ähnlich geschlossen wie iOS und hängen stark von Microsoft-eigenen Apps ab.
Nun sind aber Betriebssysteme nicht alles, man sollte auch Anwendungen programmie-
ren können, und wie Sie wissen, braucht man dazu Programmiersprachen. Ein Klassi-
ker ist die Programmiersprache C, die in den 1970er Jahren an den Bell Labs entwickelt
wurde. Es ist die klassische Sprache der strukturierten Programmierung, mit der man all
das umsetzen kann, was ich Ihnen über strukturierte Programmierung berichtet habe,
und noch viel mehr. C erlaubt die Programmierung nahe an der Hardware, und es ist
möglich, direkte Zugriffe auf den Speicher mithilfe sogenannter Zeiger durchzuführen.
Genau wegen dieser Nähe zur durchführenden Hardware, also wegen der Maschinen-
nähe, ist C eine ausgesprochen effiziente Sprache und kann auch in der Systemprogram-
mierung, also der Programmierung von Betriebssystemen, eingesetzt werden.
Beim puren C ist man aber nicht stehen geblieben. Schon in den 1980er-Jahren wurde
die Erweiterung C entwickelt, ausgesprochen C plus plus, die vor allem der objekto-
rientierten Programmierung dient. Es handelt sich tatsächlich um eine Erweiterung von
C, und das heißt, was C kann, das kann auch C++, weshalb Sie auch mit C++ problemlos
die reine strukturierte Programmierung betreiben können. Aber nicht nur. Wichtig ist,
dass C++ eben auch objektorientiert ist und Sie deshalb die Verfahrensweisen der objek-
torientierten Programmierung wie Klassenbildung und Kapselung, die ich Ihnen im
zweiten Kapitel gezeigt habe, anwenden können, und natürlich auch alle anderen objek-
torientierten Konstruktionen wie z. B. Vererbung oder Polymorphie, deren Erklärung
den Rahmen dieses Heftes sprengen würde. Auch C++ ist sowohl in der System- als auch
in der Anwendungsprogrammierung einsetzbar.
Angelehnt an C ist die Programmiersprache C\#, ausgesprochen C sharp, eine Mi-
crosoft-Entwicklung, die syntaktische Ähnlichkeiten mit C und der Programmier-
sprache Java aufweist und zu den streng objektorientierten Sprachen zählt. Sie ist zwar
prinzipiell auf verschiedenen Umgebungen einsetzbar, wird aber häufig im Rahmen ei-
ner speziellen Microsoft-Umgebung namens .NET verwendet.
Und damit sind wir schon bei der Programmiersprache Java, einer Entwicklung aus den
1990er-Jahren, die man nicht mit der gleichnamigen indonesischen Insel verwechseln
sollte. Java ist eine vollständig objektorientierte Sprache, wenn man davon absieht, dass
einfache Datentypen wie ganze Zahlen nicht automatisch als Objekte verarbeitet wer-
den. Ansonsten erfolgen der Zugriff auf und die Verarbeitung von Daten grundsätzlich
mithilfe von Attributen und Methoden, wie ich es Ihnen schon im zweiten Kapitel ge-
zeigt habe, und auch die bereits bei C erwähnten weitergehenden objektorientierten
Verfahrensweisen sind vorhanden. Wichtig ist bei Java noch die sogenannte Plattfor-
munabhängigkeit. Damit ist gemeint, dass sich Java-Programme mithilfe einer speziel-
len Technologie namens virtuelle Maschine ohne zusätzliche Anpassungen auf allen
Systemen ausführen lassen, für die eine solche virtuelle Maschine zur Verfügung steht.
48 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Rechner 3
Das hat Vorteile, aber auch einen Nachteil, denn gerade wegen der Plattformunabhän-
gigkeit kann Java kaum für Aufgaben der Systemprogrammierung eingesetzt werden,
weshalb ihr Schwerpunkt in der Anwendungsprogrammierung liegt.
Damit ist mein kurzer Überblick über ein paar Betriebssysteme und Programmierspra-
chen beendet. Vergessen Sie nicht: Das war nur eine kleine Auswahl aus einer großen
Menge. Sie können sich ja einmal den Spaß machen, im Internet einen Blick auf Listen
von Betriebssystemen und Programmiersprachen zu werfen; dann werden Sie merken,
was ich mit einer großen Menge meine. Man muss nicht alle diese Sprachen und Syste-
me kennen, und schon gar nicht muss man sie alle beherrschen. Wichtig ist, dass man
grundsätzlich weiß, worum es bei solchen Dingen geht; die jeweiligen Details werden
Sie dann lernen, wenn Sie mit einem Betriebssystem oder einer Programmiersprache
konkret arbeiten und Probleme lösen sollen.
Da es hier nur um einen enzyklopädischen Überblick ging, verzichte ich auf abfragende
Übungen und gehe zum nächsten Kapitel über, in dem Sie lernen werden, wie die duale
Arithmetik funktioniert.
GDI01 49
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
Sie lernen hier, dass die Darstellung von Daten im Rechner auf dem dualen Zah-
lensystem beruht. Dabei sehen Sie, wie man mit dualen Zahlen rechnet, wie man
sie in hexadezimaler und oktaler Form schreiben kann und wie die Datendarstel-
lung mithilfe des ASCII-Codes funktioniert.
Über die Aufgabe des Computers, Daten zu verarbeiten, haben wir uns schon mehrfach
geeinigt, und Sie haben auch schon etwas darüber gelernt, wie er das macht. Was soll
man aber eigentlich unter Daten verstehen? Da gehen die Meinungen etwas auseinan-
der. Man kann natürlich sagen, dass jede in irgendeiner Form aufgeschriebene Zeichen-
folge schon so etwas wie Daten darstellt, aber das ist wenig befriedigend und erinnert
an die Auffassung mancher Fernsehsender, alles, was auf dem Bildschirm herumzappelt,
sei unterhaltend. Irgendein Sinn sollte mit Daten verbunden sein, sonst wäre der Ver-
such, sie zu verarbeiten, völlig sinnlos und die Datenverarbeitung wäre ein Spiel mit in-
haltsleeren Zeichenketten. Ich will daher davon ausgehen, dass Daten einen Bezug zu
irgendeinem Problem haben, an dessen Lösung man interessiert ist – sie sind ein Hilfs-
mittel, um ein Problem zu lösen, und nicht sinnlos hingekritzelte Zeichenketten.
Unter Daten versteht man Angaben zu Personen, Sachen oder Sachverhalten, die zur
Lösung eines Problems zweckdienlich sein können.
Niemand kann die Zweckdienlichkeit eines bestimmten Datensatzes für ein bestimmtes
Problem garantieren, aber man sollte mit Daten wenigstens die Hoffnung verbinden,
dass ihre Verwendung zur Lösung des einen oder anderen Problems sinnvoll sein kann.
Und natürlich müssen diese Daten dem Computer in einer Form zur Verfügung gestellt
werden, die er versteht, mit der er etwas anfangen kann. Da gibt es aber nur eine Mög-
lichkeit. Ein Computer ist ein elektronisches Instrument, er basiert darauf, dass Strom
fließt oder auch nicht, und dazwischen gibt es gar nichts. Er muss also mit zwei Grund-
zuständen zurande kommen: Strom fließt oder Strom fließt nicht. Auch seine Rechen-
operationen haben keine andere Wahl, als sich auf diese beiden Grundzustände einzu-
lassen, und das bedeutet, dass wir eine sehr spezielle Art von Zahlen brauchen – Zahlen,
die nur zwei Grundzustände kennen, die man üblicherweise mit 0 und 1 bezeichnet. Das
sind die sogenannten Dualzahlen oder auch Binärzahlen.
4.1 Zahlensysteme
Man muss sich mit der Tatsache abfinden, dass ein Computer irgendwie mit Einsen und
Nullen rechnet. Das klingt für normale Menschen zunächst etwas seltsam: Wie soll man
denn mit Einsen und Nullen rechnen können, da kommt man doch nicht weit. Stimmt
aber nicht. Auch das vertraute Dezimalsystem hat nur zehn Ziffern, und trotzdem kann
man damit weiter als bis zehn zählen, warum sollte etwas Ähnliches dann nicht auch
mit zwei Ziffern funktionieren?
Versetzen Sie sich einmal in die Lage eines Computers. Von außen akzeptiert er selbst-
verständlich die üblichen dezimalen Zahlen in der üblichen Darstellung, und er gibt
auch seine Ergebnisse wieder in dieser Form aus, aber seine Verarbeitungen macht er
völlig anders. Die internen Schaltungen eines Rechners beruhen auf dem Fließen von
Strom, und da gibt es nur zwei Grundzustände: Strom fließt oder er fließt nicht. Beim
50 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
Aufbau des Zahlensystems habe ich dagegen zehn Grundzustände, nämlich die Ziffern
von null bis neun, aus denen sich alles andere zusammensetzt. So viele Möglichkeiten
besitzt der Computer nicht, er muss seine gesamte Arithmetik, all seine Rechnungen, zu-
sammensetzen aus den erwähnten zwei Grundzuständen. In Ziffern übersetzt heißt das:
Ihm stehen keine zehn Ziffern zur Verfügung, sondern nur zwei, und damit muss er aus-
kommen.
Sehen wir uns also an, wie man die natürlichen Zahlen computerfreundlich darstellen
kann. Die Darstellung, nach der ich suche, beruht auf nur zwei Ziffern, und deshalb
spricht man oft vom Zweiersystem oder auch Dualsystem. Es spielt aber keine Rolle,
wie Sie es nennen. Wichtig ist nur, dass es darum geht, sämtliche natürlichen Zahlen
mithilfe von nur zwei Ziffern darzustellen. Dabei ist es sinnvoll, sich erst einmal zu
überlegen, wie eigentlich das vertraute Dezimalsystem aufgebaut ist.
Die Bedeutung der Dezimalzahl 13271 dürfte Ihnen vertraut sein. Offenbar hat die 1
ganz vorne einen völlig anderen Wert als die 1 ganz hinten, denn sie steht in Wahrheit
für 10000, während die hintere 1 einfach nur für sich selbst steht. Die Wertigkeit einer
Ziffer hängt also davon ab, an welcher Stelle der Zahl sie steht, und je weiter man nach
links kommt, desto höher steigt man in der Zehnerpotenz. Im Falle meiner Beispielzahl
gilt:
13271 1 ⋅ 1043 ⋅ 103 2 ⋅ 102 7 ⋅ 101 1 ⋅ 100.
Nach diesem Prinzip kann ich jetzt genau aufschreiben, was man unter einer Dezimal-
zahl versteht: Es ist eine Folge von endlich vielen Ziffern zwischen 0 und 9, und der tat-
sächliche Zahlenwert jeder Ziffer hängt davon ab, an welcher Stelle der Zahl die Ziffer
steht. Je weiter links, desto höher ist die Zehnerpotenz, mit der man die Ziffer multipli-
zieren muss.
Dualzahlen sind genauso aufgebaut wie die Dezimalzahlen, nur eben mit zwei Ziffern
anstatt zehn. Eine typische Dualzahl lautet beispielsweise 101112, wobei die kleine Zwei
bedeutet, dass es sich um eine Zahl zur Basis 2 handelt, und genau deshalb muss ich die
Basiszahl 2 verwenden, um die eigentliche Bedeutung der Dualzahl 101112 vor mir zu
sehen. Ihr dezimaler Wert kann nur 1 · 24 0 · 23 1 · 22 1 · 21 1 · 20 sein, denn
überall dort, wo ich bei einer Dezimalzahl die Basis 10 eingesetzt hätte, gehe ich hier zur
Basis 2 über. Rechnet man das aus, so ergibt sich die Dezimalzahl 23, weshalb also die
Dualzahl 101112 der Dezimalzahl 23 entspricht.
Jetzt dürften Sie so weit sein, die Definition der Dualzahlen zu verstehen. Sie sieht fast
genauso aus wie die Definition der Dezimalzahlen, nur dass ich auf die richtigen Ziffern
achten muss: Bisher hatte ich 0 bis 9, jetzt habe ich nur noch 0 und 1. Und auch beim
dezimalen Wert wird sich etwas ändern.
GDI01 51
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
Eine (n 1)-stellige Dualzahl oder auch Binärzahl ist eine Folge von n 1 Ziffern
an an-1 … a1 a0
die alle entweder gleich 0 oder gleich 1 sind. Der dezimale Wert dieser Zahl beträgt
an ⋅ 2n an-1 ⋅ 2n-1 ⋯ a1 ⋅ 2 a0.
Da Sie vermutlich mit dieser seltsamen Art von Zahlen noch selten etwas zu tun hatten,
zeige ich Ihnen noch ein paar Beispiele. Um Verwechslungen zu vermeiden, werde ich
alle Dualzahlen mit dem Index 2 versehen, damit klar ist, dass es sich um Zahlen zur Ba-
sis 2 handelt.
Wie lautet nun der dezimale Wert von 1010102 ? Die Zahl besteht aus sechs Ziffern, da-
her ist die höchste Potenz 25. Damit ergibt sich:
10101021 ⋅ 25 0 ⋅ 24 1 ⋅ 23 0 ⋅ 22 1 ⋅ 21 0 ⋅ 20 32 8 2 42.
1010102 ist also die duale Darstellung der vertrauten Zahl 42. Allerdings musste ich hier
noch die Stellen der Dualzahl zählen, damit ich wusste, mit welcher Zweierpotenz ich
anfangen muss. Etwas einfacher hat man es, wenn man bei der Umrechnung von rechts
nach links vorgeht und nicht wie eben von links nach rechts. Das bedeutet zum Beispiel:
110111012 1 4 8 16 64 128 221,
denn ganz rechts finden Sie eine duale 1, die einer vertrauten 1 entspricht. Nach einer 0
kommt dann wieder eine 1, also gibt es keine Zweierstelle, sondern erst wieder eine Vie-
rerstelle, der sich eine Achterstelle und eine Sechzehnerstelle anschließen. Die Zweiund-
dreißigerstelle wird dann wieder ausgelassen, und ganz links versammeln sich eine Vier-
undsechzigerstelle und eine Einhundertachtundzwanzigerstelle. Auf die gleiche Weise
können Sie dann zum Beispiel 100001012 1 4 64 69 rechnen.
Übung 4.1:
Berechnen Sie die dezimalen Werte der folgenden Dualzahlen.
a) m 1101100112
b) m 111100012
Übung 4.2:
Entwickeln Sie ein Verfahren, das zwei duale Zahlen der Größe nach vergleicht und
feststellt, welche die größere ist. Beschreiben Sie das Verfahren verbal.
4.2 Umrechnung
Im letzten Abschnitt haben Sie gesehen, wie man den dezimalen Wert einer gegebenen
Dualzahl berechnen kann, nämlich einfach durch Ausnutzung der Definition. Wie ist es
aber umgekehrt? Wie lautet also beispielsweise die duale Darstellung der Dezimalzahl
13? Das ist nicht so schwer, denn eine Dualzahl besteht aus einer Summe von Zweier-
potenzen, und Sie können die 13 schreiben als 13 8 4 1 23 22 20, was der
Dualzahl 11012 entspricht. Man muss also nur überprüfen, welche Zweierpotenzen man
52 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
addieren muss, um die gewünschte Dezimalzahl zu erhalten, und schon kann man die
entsprechende Dualzahl ablesen. Am besten fängt man bei der größten passenden Zwei-
erpotenz an und arbeitet sich dann langsam nach unten vor.
Das ist aber manchmal leichter gesagt als getan. Sie werden mir vermutlich zustimmen,
dass es beispielsweise kein reines Vergnügen ist, die größte Zweierpotenz zu finden, die
1896268482366748 nicht übersteigt, und auch die Suche nach den nächstkleineren
Zweierpotenzen könnte hier problematisch werden. Ich werde Ihnen deshalb jetzt eine
deutlich einfachere Umrechnungsmethode zeigen. Das Prinzip sehen wir uns erst einmal
am Beispiel der Dezimalzahl m 247 an. Was passiert, wenn ich dieses m durch 10 teile
und dabei den Rest aufschreibe? Dann ist
247 : 10 24 Rest 7.
Das Divisionsergebnis 24 teile ich wieder mit Rest durch 10 und erhalte
24 : 10 2 Rest 4.
Wenn ich dann zum Schluss auch dieses Divisionsergebnis durch 10 teile, so ergibt sich
2 : 10 0 Rest 2.
Was fällt Ihnen auf, wenn Sie die Divisionsreste von unten nach oben betrachten? Sie
ergeben nebeneinander geschrieben wieder genau die Zahl 247. Man erhält also die De-
zimalstellen einer Zahl, indem man immer wieder mit Rest durch 10 teilt und anschlie-
ßend die Reste in umgekehrter Reihenfolge aufschreibt. Und bei der Dualdarstellung ist
das nicht anders, nur dass Sie hier natürlich nicht durch 10 teilen, sondern durch die Ba-
siszahl der Dualzahlen, also durch 2, die hier die Rolle der 10 übernimmt.
Sehen wir uns erst einmal an einem Beispiel an, ob das auch funktioniert. Dazu betrach-
te ich wieder die Dezimalzahl m 13. Im Folgenden schreibe ich auf, was passiert, wenn
man andauernd durch 2 teilt, die Reste aufschreibt und dann das jeweilige Divisionser-
gebnis wieder durch 2 teilt. Dann gilt:
13 : 2 6 Rest 1
6 : 2 3 Rest 0
3 : 2 1 Rest 1
1 : 2 0 Rest 1
Schreibt man nun die Reste in umgekehrter Reihenfolge als Dualzahl auf, so ergibt sich
11012 8 4 1 13, also ist alles in Ordnung.
Man muss also nur oft genug durch 2 teilen und die Divisionsreste richtig herum auf-
schreiben. Ich formuliere das jetzt wieder als ein allgemeingültiges Verfahren.
GDI01 53
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
Vielleicht sollte ich noch ein Wort über das Abbruchkriterium verlieren: Sobald das Di-
visionsergebnis eine Null liefert, soll ich aufhören. Was würde geschehen, wenn ich die-
se Vorschrift ignoriere? Im nächsten Schritt würde ich das alte Divisionsergebnis durch
2 teilen, und da ich vorher bereits eine Null ermittelt habe, wäre das Ergebnis schlicht 0
Rest 0. Und auch alle weiteren Schritte können mir nur immer mehr Nullen liefern. Die
Reste muss ich dann aber in der umgekehrten Reihenfolge ihres Auftretens notieren, und
das heißt, dass ich auf diese Weise nur eine Unmenge führender Nullen produziere, von
denen es in Politik und Wirtschaft schon genug gibt und die meine Dualzahl sicher nicht
verbessern. Deshalb kann ich das Verfahren beenden, sobald das Divisionsergebnis eine
Null liefert.
Nach dem Notieren eines Verfahrens sollte man immer noch ein Beispiel rechnen, damit
man sich an das Verfahren gewöhnt. Zum Glück ist die Rechenmethode ziemlich über-
sichtlich, sodass ich hier auf erklärende Kommentare verzichten kann. Ich werde jetzt
also m 157 in seine duale Darstellung umrechnen. Es gilt:
157 : 2 78 Rest 1
78 : 2 39 Rest 0
39 : 2 19 Rest 1
19 : 2 9 Rest 1
9 : 2 4 Rest 1
4 : 2 2 Rest 0
2 : 2 1 Rest 0
1 : 2 0 Rest 1
Folglich ist 157 100111012.
So viel für den Moment zu Umrechnungen. Etwas später, wenn ich Ihnen etwas über He-
xadezimalzahlen und Oktalzahlen berichte, werden wir uns noch einmal mit dem Um-
rechnen zwischen Zahlensystemen befassen.
Übung 4.3:
Berechnen Sie die Dualdarstellung von m 278 und von n 131.
54 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
1 0 0 0 1
1 01 11 1
1 1 1 0 0
Sie sehen, was passiert. In der Einserstelle ist 12 12 102, also erhält das Ergebnis in
der Einserstelle eine 0, und den Übertrag 1 schleppe ich mit in die Zweierstelle. Nun
habe ich in der Zweierstelle die Addition 12 12 02, denn den Übertrag muss man
mitaddieren, und das ergibt wieder 102. Somit schreibe ich in die Zweierstelle des Er-
gebnisses wieder eine 0 und belaste die Viererstelle mit einem neuen Übertrag 1. So
langsam bekommen wir Routine. In der Viererstelle ergibt sich die Addition 12 02 02,
was genau 12 ergibt und mir für die Achterstelle jeden Übertrag erspart. Sowohl in der
Achter- als auch in der Sechzehnerstelle ist dann nur eine duale 12 auf eine duale 02 zu
addieren mit dem jeweils gleichen Ergebnis 12. Insgesamt komme ich auf das Ergebnis
111002, und aus Sicherheitsgründen sollte ich nachsehen, ob das überhaupt stimmt. Das
ist aber kein Problem. Aus 100012 17 und 10112 11 folgt sofort 100012 10112 17
11 28 111002, denn 111002 16 8 4 28. Es passt also alles zusammen.
Ein Beispiel könnte reiner Zufall sein, und außerdem kann ein wenig Übung in einem
neuen Verfahren nicht schaden. Ich addiere jetzt also die beiden dualen Zahlen 1110112
und 111012. Nach dem eben besprochenen Schema ergibt das die folgende Rechnung.
GDI01 55
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
1 1 1 0 1 1
1 11 11 11 01 1
1 0 1 1 0 0 0
Hier geschieht auch nichts anderes als vorher. Bei der Addition der Einser-, Zweier- und
Viererstellen erhalte ich jeweils das Ergebnis 102, also eine 0 in der Ergebniszahl und
eine 1 im Übertrag. Bei der Achterstelle müssen Sie ein wenig aufpassen, denn hier tritt
die Addition 12 12 12 auf, die natürlich zu dem Resultat 112 führt, also zu einer 1 in
der Ergebniszahl und einer 1 im Übertrag. Das Gleiche passiert in der Sechzehnerstelle,
während dann in der Zweiunddreißigerstelle die Addition 12 12 Sie zu dem Ergebnis
102 bringt. Noch ein kurzer Blick auf die Kontrollrechnung: es gilt 1110112 59 und
111012 29, also 1110112 111012 59 29 88 10110002, denn 10110002 64
16 8 88. Es ist nichts schiefgegangen.
Und dabei geht auch nie etwas schief, das ist genau die Methode, mit der man duale Zah-
len addiert und die auch der Computer bei seinen Rechenoperationen anwendet. Das
dürfte schon eine allgemeine Regel wert sein.
Man addiert Dualzahlen, indem man sie untereinanderschreibt, von rechts nach
links stellenweise addiert, die Einerziffer der Addition in die Ergebniszahl schreibt
und eventuell auftretende Überträge in die jeweils nächste Stelle übernimmt.
Kürzer gesagt: Man addiert dual genauso wie dezimal, nur eben zur Basis 2.
Auch die duale Subtraktion bietet auf den ersten Blick keine Überraschungen, allerdings
wird es hier zu einem zweiten Blick kommen. Zunächst einmal kann man sich auf den
Standpunkt stellen, dass auch hier das von den Dezimalzahlen gewohnte Verfahren ana-
log zur Addition verwendet werden kann, und das funktioniert auch problemlos. Ich zei-
ge Ihnen das an einem kleinen Beispiel, indem ich 110012 10102 berechne. Wie üblich
schreibt man die beiden Zahlen untereinander und rechnet von rechts nach links.
1 1 0 0 1
1 11 01 1 0
1 1 1 1
Sie müssen dabei immer nur darauf achten, alles ganz genauso zu machen wie bei dezi-
malen Zahlen – nur mit dem Unterschied, dass die Basis hier 2 lautet und nicht 10. In
der Einserstelle wollen Sie von 0 auf 1 zählen, was keinerlei Schwierigkeiten macht und
zum Resultat 1 führt. In der Zweierstelle sieht es schon schlechter aus, Sie müssen von
1 auf 0 zählen, und das geht natürlich nicht. Die 0 wird daher als 102 interpretiert, die
1, die Ihnen zu dieser 102 oben fehlt, wird als Übertrag unten in die Viererstelle geschrie-
ben, und in der Ergebniszahl haben Sie eine 1, da 12 12 102 gilt; so hätten sie das bei
vergleichbaren Verhältnissen im Dezimalsystem auch gemacht. Diese Situation wieder-
holt sich aufgrund des Übertrages in der Viererspalte: Das Zählen von der 1 auf die 0
liefert Ihnen einen Übertrag in die Achterstelle und in der Ergebniszahl wieder eine 1.
Jetzt sehe ich mir die Achterstelle an, und da sieht die Sache anders aus. Durch den Über-
trag, den mir die Viererstelle eingebrockt hat, steht jetzt bei der unteren Zahl 12 12
102, und von dieser 102 aus muss ich hochzählen auf 12. Das geht nur dann, wenn diese
duale 12 eigentlich eine 112 ist und ich mir wieder einen Übertrag für die Sechzehner-
56 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
stelle aufhalse. Hier habe ich dann nur noch von der 1 auf die 1 zu zählen, was ich mir
genauso gut sparen kann. Der übliche Test sollte auch beim Subtrahieren nicht fehlen.
Es gilt 110012 25 und 10102 10, also 110012 – 10102 25 – 10 15, und natürlich
gilt 11112 8 4 2 1 15. Wieder ist alles gut gegangen.
Es ist nicht einzusehen, warum ich beim Subtrahieren gutgläubiger sein soll, als ich es
beim Addieren war; ein zweites Beispiel sollte ich schon noch anführen. Berechnen wir
also 1010012 – 111112.
1 0 1 0 0 1
1 11 11 11 1 1
1 0 1 0
In der Einserstelle passiert gar nichts, denn das Hochzählen von der 1 zur 1 liefert für
die Ergebniszahl eine 0. Auch die Zweierstelle verhält sich nicht ungewöhnlich, ich zäh-
le von 1 auf 0 hoch und erhalte um den Preis eines Übertrags die 1 für die Ergebniszahl.
Immerhin etwas neuer ist die Lage bei der Viererstelle. Wegen des Übertrags habe ich in
der unteren Zahl jetzt 12 12 102, was ich auf eine 0 hochzählen muss – das liefert
natürlich wieder den Übertrag 1 für die Achterstelle und die 0 für die Ergebniszahl. Der
Rest der Rechnung besteht nur noch aus Altvertrautem, weshalb ich nicht mehr näher
darauf eingehe. Und auch der Test kann mich nicht mehr wirklich aus der Ruhe bringen;
es gilt 1010012 41 und 111112 31, also ist 1010012 – 111112 41 – 31 10 10102,
denn 10102 8 2 10.
Warum hätte es auch nicht funktionieren sollen? Schon bei der Addition haben die Du-
alzahlen die Analogie zu den Dezimalzahlen gut vertragen, und bei der Subtraktion ist
es nicht anders.
Man subtrahiert eine kleinere Dualzahl von einer größeren, indem man die kleinere
unter die größere schreibt, von rechts nach links stellenweise subtrahiert und even-
tuell auftretende Überträge in die jeweils nächste Stelle übernimmt.
Auch das ist wie bei den Dezimalzahlen und von daher nichts Besonderes. Ich hatte aber
schon angekündigt, dass es einen zweiten Blick auf die Subtraktion geben würde, nach-
dem der erste Blick sich als etwas überraschungsarm herausgestellt hat. Diese Art des
Subtrahierens hat nämlich zwei Probleme. Beim manuellen Rechnen neigt man dazu,
sich mit den Überträgen zu verheddern, die den meisten Leuten erfahrungsgemäß bei
der Addition leichter fallen als bei der Subtraktion. Das wäre noch nicht weiter schlimm,
denn die ganze duale Rechnerei soll ja zeigen, wie ein Computer intern rechnet, und der
wird sich schon nicht verheddern. Stimmt. Aber denken Sie daran, dass das Rechnen im
Computer mithilfe von elektronischen Schaltelementen durchgeführt wird, die man erst
einmal bauen muss, und je weniger verschiedene Schaltelemente man sich überlegen
muss, desto besser. Für die Addition braucht man auf jeden Fall ein Schaltelement, das
bleibt nicht aus. Es wäre aber günstig, wenn man auch mit diesem einen Schaltelement
auskäme und sich nicht noch ein neues für die Subtraktion ausdenken müsste. Ich will
jetzt also daran gehen, eine Subtraktion durchzuführen, ohne wirklich subtrahieren zu
müssen, nur durch Additionen. Am Beispiel einer dezimalen Rechnung zeige ich Ihnen,
wie man die unangenehmen Subtraktionsüberträge vermeiden kann, und im dualen Fall
wird sich sogar der Vorgang des Subtrahierens selbst verflüchtigen und nur Additionen
GDI01 57
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
zurücklassen. Zunächst einmal das dezimale Beispiel, es geht um 3784 – 1717. Das zu-
gehörige Rechenschema finden Sie hier, anschließend erkläre ich, was beim Rechnen ei-
gentlich passiert ist.
9 9 9
9
1 7 1
7
8 2 8
2
3 7 8
4
1 2 0 6
6
1
1 2 0 6 7
1 0 0 0 0
2 0 6 7
Erinnern Sie sich daran, dass ich ohne Überträge abziehen wollte? Nun handelt es sich
hier um vierstellige Zahlen, und die einzige vierstellige Dezimalzahl, von der ich mit Si-
cherheit jede vierstellige Dezimalzahl ohne jeden Übertrag subtrahieren kann, ist die
9999. Also rechne ich erst einmal 9999 – 1717. Das ist natürlich die falsche Subtraktion,
also muss ich das Ergebnis irgendwie korrigieren. Addiert man im nächsten Schritt die
gewünschten 3784 und anschließend noch eine 1 dazu, so hat man insgesamt die Rech-
nung
9999 – 1717 3784 1 100003784 – 1717
durchgeführt. Diese Zahl ist also genau um 10000 größer als die gesuchte, weshalb ich
dann auch am Ende noch diese überschüssigen 10000 abziehe und damit das gesuchte
Ergebnis 2067 erhalte. Hier kommen zwar zwei Subtraktionen vor, aber jede ist harmlos.
Die erste erfolgt ohne den leisesten Hauch eines Übertrags, weil von 9999 subtrahiert
wird. Und die zweite kann man durch das Streichen der führenden Ziffer 1 erledigen,
denn Sie müssen hier nur die Zahl 10000 von einer fünfstelligen Dezimalzahl abziehen.
Auf diese Weise konnte ich subtrahieren, ohne wirklich subtrahieren zu müssen.
Sie sehen, das Prinzip ist ganz einfach: Man subtrahiert von der größtmöglichen Zahl
mit der gleichen Anzahl von Ziffern, um lästige Überträge zu vermeiden, und gleicht
den dabei verursachten Fehler später durch Addieren und Streichen einer Eins wieder
aus. Wie ich Ihnen gleich zeigen werde, funktioniert das gleiche Prinzip auch bei Dual-
zahlen mit dem zusätzlichen Vorteil, dass man die erste Subtraktion eigentlich über-
haupt nicht vornehmen muss. Zunächst wieder ein Beispiel, das die Methode illustriert;
ich berechne 110012 – 10102. Damit ich keine Problem mit der Anzahl der Stellen be-
komme, schreibe ich die 10102 als 010102, so haben beide Zahlen fünf Stellen. Nun ha-
ben Sie gerade gelernt, dass man die abzuziehende Zahl von der größtmöglichen Zahl
mit der gleichen Anzahl von Stellen subtrahieren muss. Bei den Dezimalzahlen bestand
diese Riesenzahl aus lauter Neunen, weil 9 nun mal die höchste Ziffer im Dezimalsystem
ist. Und wie lautet die höchste Ziffer im Dualsystem? Das ist die 1, und daher lautet die
größtmögliche fünfstellige Dualzahl schlicht 111112. Das Rechenschema ist dann das
folgende.
58 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
1 1 1 1 1
0 1 0 0 1
1 0 1 1 0
1 1 0 1 0
1 0 1 1 0 1
1
1 0 1 1 1 1
1 0 0 0 0 0
1 1 1 1
Was ist hier geschehen? Zuerst habe ich ganz nach Plan 010102 von 111112 abgezogen,
und das ging ganz ohne Übertrag. Auf dieses Zwischenergebnis habe ich dann die ur-
sprüngliche Zahl 110012 addiert, um mich wieder der eigentlichen Aufgabe zu nähern.
Natürlich habe ich jetzt 111112 zu viel auf der Rechnung, was ich noch ein wenig ver-
schlimmere, indem ich eine schlichte 12 dazuaddiere. Das führt zu einem Überschuss
von 111112 12 1000002, und das ist das Beste, was mir passieren konnte. Sie sehen
nämlich, dass mein bisheriges Ergebnis 1011112 beträgt, eine Zahl mit einer 1 vorne und
fünf weiteren Stellen. Und davon muss ich 1000002 abziehen, ebenfalls eine 1 vorne mit
fünf anschließenden Nullen. Nichts könnte also leichter sein, als diese überschüssige
1000002 abzuziehen, ich muss dafür nur die führende 1 aus dem bisherigen Ergebnis
1011112 streichen und finde das Endergebnis 11112.
So geht das tatsächlich immer. Die eigentliche Subtraktion erledigen Sie, indem Sie von
der größtmöglichen Zahl mit der passenden Anzahl von Stellen subtrahieren, also von
einer hinreichend langen Ansammlung von Einsen, was immer ohne Übertrag funktio-
niert. Dann addieren Sie noch die ursprüngliche größere Zahl, addieren eine weitere
Eins und streichen aus dem entstandenen Ergebnis die führende Eins heraus, um
schließlich beim Endergebnis zu landen. Schön und gut, aber noch nicht wirklich über-
zeugend. Das Ziel war ja, eine Subtraktion hinzubekommen, ohne subtrahieren zu müs-
sen, nur mit Additionen. Aber auch wenn es vielleicht nicht so aussieht: Dieses Ziel ist
schon erreicht. Bei der Rechnung 111112 – 010102 kam beispielsweise 101012 heraus,
und jetzt vergleichen Sie einmal 01010 mit 10101. Natürlich fällt Ihnen hier auf, dass
man von der ersten Zahl auf die zweite Zahl kommt, indem man die Nullen durch Ein-
sen und die Einsen durch Nullen ersetzt. Das kann auch gar nicht anders sein, denn an
jeder Stelle der Subtraktion subtrahieren Sie von einer dualen 1, da ich als Ausgangs-
punkt der Subtraktion die Zahl 111112 gewählt habe. Nun ist aber 12 – 02 12, sodass
aus einer Eins eine Null wird, und 12 – 12 02, sodass aus einer Null eine Eins wird. Die
erste vorkommende Subtraktion ist also nur scheinbar eine echte Subtraktion, in Wahr-
heit reicht es, wenn Sie Einsen zu Nullen werden lassen und umgekehrt. Diese Operation
ist so wichtig, dass sie einen eigenen Namen bekommen hat: das Einserkomplement.
Und weil im Verlauf der Rechnung auch noch eine schlichte 12 addiert werden muss, hat
man sich auch dafür einen Namen ausgedacht: Die Summe aus Einserkomplement und
12 heißt Zweierkomplement.
Das Einserkomplement einer Dualzahl erhält man, indem man die Nullen der Zahl
durch Einsen und ihre Einsen durch Nullen ersetzt. Ihr Zweierkomplement erhält
man, indem man zu ihrem Einserkomplement noch 12 addiert.
GDI01 59
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
Damit haben wir schon alles zusammen, um eine „subtraktionsfreie“ Subtraktion einer
kleineren dualen Zahl von einer größeren durchführen zu können. Im Beispiel haben Sie
gesehen, wie es geht: Man bildet erst das Einserkomplement der abzuziehenden Zahl,
addiert dann die ursprüngliche größere Zahl und addiert auf diese Summe noch 12. An-
ders gesagt: Auf die ursprünglich größere Zahl addiert man das Zweierkomplement der
kleineren. Das ergibt ein bisschen zu viel, aber das macht gar nichts, denn den Über-
schuss kann man beseitigen, indem man die führende Eins streicht.
Die führenden Nullen sind dabei für die Subtraktion sehr bedeutend. Wenn Sie nämlich
vergessen, eine zu kurz geratene Zahl b mit führenden Nullen auf die passende Stellen-
zahl zu bringen, wird das Ihr ganzes Ergebnis ruinieren. Nehmen Sie als Beispiel die
Aufgabe 10012 – 12. Das Einserkomplement der unaufgefüllten 12 lautet natürlich gera-
de 02, und da man darauf noch eine 12 addieren muss, ergibt sich das Zweierkomple-
ment 12. Das Verfahren würde also von mir verlangen, 10012 12 10102 auszurechnen
und davon die führende Eins zu streichen, das ergibt 0102 102. Aber dummerweise ist
10012 – 12 10002 ≠ 102, und dieser Fehler konnte nur auftreten, weil ich die kleinere
Zahl 12 nicht durch führende Nullen auf die richtige Form 00012 gebracht hatte. In die-
sem Fall funktioniert nämlich wieder alles; das Zweierkomplement lautet 11102 1
11112, also hat man die Addition 10012 11112 110002, und das Streichen der führen-
den Eins ergibt das korrekte Resultat 10002.
Sie sehen: Sobald man eine Grundschaltung für die Addition hat, kann man die Subtrak-
tion ebenfalls mithilfe dieser Additionsschaltung durchführen. Damit ist mein Ziel er-
reicht.
Übung 4.4:
Führen Sie die folgenden dualen Rechenvorgänge durch. Subtraktionen sind dabei
mithilfe des Zweierkomplements anzugehen.
a) 11010102 101011102
b) 10102 01101102
c) 111011012 – 1010012
d) 11012 – 111012
60 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
keit auftreten, sich einen Speicherauszug ausdrucken zu lassen, und der Speicher besteht
nun mal aus einer Unmenge von Einsen und Nullen, keine sehr augenfreundliche Lek-
türe. Um so etwas absolut Unlesbares wenigstens ein wenig lesbarer zu machen, hat man
die Hexadezimalzahlen entwickelt, auf Deutsch die Sechzehnerzahlen.
Die Idee ist einfach genug. Teilt man eine Dualzahl auf in Viererpäckchen, dann kann
jedes Viererpäckchen jeden Wert zwischen 00002 und 11112 annehmen, dezimal gerech-
net zwischen 0 und 15. Wie Sie unschwer feststellen können, sind das für jedes Vierer-
päckchen 16 mögliche Werte, weshalb man ja auch von Hexadezimalzahlen spricht. Im
Sinne einer einfachen Darstellung braucht man also für jeden Wert zwischen 0 und 15
ein eigenes Zeichen, damit man sie leicht auseinanderhalten kann. Für die Ziffern von 0
bis 9 muss ich da nicht lange suchen und nehme selbstverständlich die Ziffern von 0 bis
9. Nur die Zahlen von 10 bis 15 sollten noch mit Zeichen versehen werden, und dafür
verwendet man die ersten fünf Buchstaben des Alphabets. Man schreibt also A 10,
B 11, C 12, D 13, E 14 und F 15. Damit wird beispielsweise 11111111112
11111111112 3FF16: Zuerst teilt man die Zahl von rechts nach links in Viererblöcke auf
und dann setzt man diese Viererblöcke in die hexadezimalen Ziffern um. Der vorderste
Block hat nur zwei Stellen, aber das schadet gar nichts, denn 112 00112 3 316. Wei-
terhin ist 11112 15 F16, und so entsteht die Hexadezimalzahl 3FF16. Sie werden zuge-
ben, dass man drei gültige Stellen leichter lesen kann als zehn.
Man erhält den hexadezimalen Wert einer Dualzahl, indem man sie von rechts nach
links in Vierergruppen zusammenfasst und die hexadezimalen Werte dieser Vierer-
gruppen aufschreibt. Umgekehrt erhält man die duale Darstellung einer Hexadezi-
malzahl, indem man die einzelnen hexadezimalen Ziffern in duale Vierergruppen
auflöst.
Zwei Beispiele sollen das Ganze noch abrunden. Zunächst bestimme ich die Hexadezi-
maldarstellung von 110011111012. Die Vierergruppen von rechts nach links lauten
1101, 0111 und 0110, wobei ich ganz vorn eigentlich nur eine Dreiergruppe 110 habe,
aber das Hinzufügen von führenden Nullen hat noch nie einer Zahl geschadet. Nun gilt
aber 11012 13 D16, 0111 7 716 und 0110 6 616. Daraus folgt, wenn man wieder
die richtige Reihenfolge herstellt, 110011111012 67D16 Es ist ziemlich klar, dass die he-
xadezimale Darstellung etwas übersichtlicher ist und daher beim Lesen von Speicher-
auszügen in aller Regel bevorzugt wird. Nun gehe ich umgekehrt vor und suche die Du-
aldarstellung der Hexadezimalzahl 4FA16. Ich muss jede hexadezimale Ziffer in eine
duale Vierergruppe auflösen.
Es gilt: A16 10 10102, F16 15 11112 und 416 4 0100. Damit folgt: 4FA16
0100111110102 100111110102
Sie sollten aber nicht glauben, dass Hexadezimalzahlen nur als eine abkürzende Schreib-
weise zu verstehen sind; selbstverständlich handelt es sich auch um schlichte Zahlen ge-
nau wie die Dezimalzahlen oder die Dualzahlen – nur eben zur Basis 16. Deshalb ist bei-
spielsweise
67D16 6 · 162 7 · 161 13 · 160 1661,
denn das hexadezimale D steht für die dezimale Zahl 13, und ansonsten müssen Sie die
16 als Basis einsetzen, die man entsprechend potenzieren muss. Noch ein letztes Beispiel,
dann haben wir das auch schon erledigt.
GDI01 61
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
Es gilt:
4FA16 4 · 162 15 · 161 10 · 160 1274,
da das hexadezimale F für die Dezimalzahl 15 steht und Sie das hexadezimale A als eine
dezimale 10 ansprechen dürfen.
Nicht ganz so wichtig wie die Hexadezimalzahlen sind die Oktalzahlen, die diesen Na-
men durchaus zu Recht tragen, denn es handelt sich nur um Zahlen zur Basis 8 – die
vierte und letzte Basis für ein Zahlensystem, mit der ich Sie behellige, nach den Basen
10, 2 und 16. Für sie muss man keine neuen Ziffernsymbole erfinden, da man mit den
bekannten Ziffern 0 bis 7 problemlos auskommt. Da das Prinzip nicht anders ist als bei
den Hexadezimalzahlen, darf ich mich hier etwas kürzer fassen.
Da es sich bei den Oktalzahlen um Zahlen zur Basis 8 handelt, können Sie leicht
vom oktalen in das dezimale System umrechnen. So ist zum Beispiel
4238 4 · 82 2 · 81 3 · 80 275 und 6728 6 · 82 7 · 81 2 · 80 442.
Das ist nichts wirklich Neues. Und auch die Umsetzung in Dualzahlen ist nicht schwer:
Die benötigten Ziffern von 0 bis 7 entsprechen genau den dreistelligen Dualzahlen von
0002 bis 1112, und das bedeutet, dass man zur Umrechnung einer Oktalzahl in das duale
System eben mit Dreierpäckchen arbeiten muss, genau wie bei den Hexadezimalzahlen
Viererpäckchen zum Einsatz kamen.
Man erhält den oktalen Wert einer Dualzahl, indem man sie von rechts nach links in
Dreiergruppen zusammenfasst und die oktalen Werte dieser Dreiergruppen auf-
schreibt. Umgekehrt erhält man die duale Darstellung einer Oktalzahl, indem man
die einzelnen oktalen Ziffern in duale Dreiergruppen auflöst.
Auch das sollte man an einem Beispiel sehen. Ich greife dafür auf die Dualzahl
110011111012 zurück, die ich bereits in die Hexadezimaldarstellung umgewandelt hatte.
Was damals die Vierergruppen geleistet haben, erledigen jetzt die Dreiergruppen, wie-
der in der Reihenfolge von rechts nach links aufgelistet. Sie lauten 1012, 1112, 0012 und
0112, wobei ich die letzte Gruppe gewinnen konnte, indem ich den verbliebenen vorde-
ren zwei Stellen eine 0 vorgesetzt habe. Umgerechnet in oktale Schreibweise ergibt das
die Ziffern 5, 7, 1 und 3, also insgesamt die Oktalzahl 31758. So einfach ist das. Und um-
gekehrt ist es auch nicht schlimmer, wie Sie am Beispiel der Oktalzahl 5728 sehen kön-
nen, denn es gilt:
58 1012, 78 1112 und 28 0102,
also erhalten Sie: 5728 10111110102.
Falls Sie der Auffassung sind, dass noch eine Kleinigkeit fehlt, dann haben Sie völlig
recht. Ich habe nämlich noch kein Wort darüber verloren, wie man eine gegebene Dezi-
malzahl in eine Hexadezimal- oder eine Oktalzahl umrechnet. Aber bei Licht betrachtet
können Sie das schon, denn erstens sind Sie in der Lage, Dezimalzahlen in Dualzahlen
umzurechnen, und zweitens haben Sie gerade gelernt, wie man aus einer Dualzahl die
entsprechende Hexadezimal- oder Oktalzahl herstellt. Natürlich ist das ein zweistufiger
62 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Dualzahlen 4
Prozess, den man vielleicht gerne in einem Schritt zusammenfassen würde. Wie ein Ver-
fahren zur direkten Umrechnung aussieht, werden Sie sich in Übung 4.7 selbst überle-
gen.
Übung 4.5:
Führen Sie eine Umrechnung von einer Dualzahl in eine Hexadezimalzahl bzw. um-
gekehrt durch.
a) Dualzahl 1101100112
b) Hexadezimalzahl ABC16
Übung 4.6:
Führen Sie eine Umrechnung von einer Dualzahl in eine Oktalzahl bzw. umgekehrt
durch.
a) Dualzahl 1101100112
b) Oktalzahl 45678
Übung 4.7:
Entwickeln Sie in Analogie zum Verfahren zur Umrechnung von Dezimalzahlen in
Dualzahlen Methoden zur Umrechnung von Dezimalzahlen in Hexadezimal- bzw.
Oktalzahlen. Testen Sie die Methode für Hexadezimalzahlen an der Dezimalzahl
2367.
GDI01 63
© HfB, 02.12.20, Berk, Eric (904709)
4 Dualzahlen
werden, weil zum Beispiel der ursprüngliche ASCII-Code wegen seiner amerikanischen
Wurzeln keine deutschen Umlaute kennt. Man war dabei aber klug genug, die sprach-
spezifischen Erweiterungen so zu regeln, dass sie mit dem ursprünglichen ASCII-Code
weitgehend kompatibel sind, sodass alle im ASCII-Code definierten Zeichen auch in den
verschiedenen Erweiterungen durch die gleichen Bitmuster codiert werden.
Es wäre nicht sehr sinnvoll, wenn Sie den gesamten ASCII-Code auswendig lernen
müssten. Ich gebe Ihnen hier nur das eine oder andere Beispiel. So steht die achtstellige
Dualzahl 000011012 für das Sonderzeichen „cr“, was nichts anderes als „carriage return“
bedeutet und somit die Entertaste repräsentiert. Natürlich kann man sie auch hexadezi-
mal schreiben und erhält dann die Darstellung 0D16 oder dezimal den Wert 13. Die üb-
lichen Großbuchstaben A bis Z entsprechen den fortlaufend hochgezählten Hexadezi-
malwerten 4116 bis 5A16, während die kleinen Buchstaben a bis z durch 6116 bis 7A16
dargestellt werden. Um nur noch zwei Beispiele zu nennen: Das Ausrufungszeichen
wird durch 2116 codiert, das Fragezeichen durch 3F16. Wie Sie schon hieran sehen kön-
nen, ist die Verwendung von Hexadezimalzahlen eine hilfreiche Sache, wenn es um die
übersichtliche Schreibweise von ASCII-Codierungen geht.
Noch hilfreicher werden hexadezimale Darstellungen beim Einsatz des Unicodes. Be-
rücksichtigt man nämlich, dass es neben den im ASCII-Code abgelegten Zeichen auch
noch ganz andere Zeichen gibt, die sich vollkommen von denen aus unserem Standard-
alphabet unterscheiden, wie zum Beispiel das arabische Alphabet oder gar die mathe-
matischen Sonderzeichen, so kann man leicht einsehen, dass die 256 Zeichen des ASCII-
Codes nicht ausreichen. Deshalb ist man im Rahmen des Unicodes übereingekommen,
mehr als 8 Bit zur Codierung zu verwenden, sich also nicht auf achtstellige Dualzahlen
zu beschränken. Üblicherweise verwendet man dann mehrere Bytes. Mit zwei Bytes, die
sechzehnstelligen Dualzahlen bzw. vierstelligen Hexadezimalzahlen entsprechen, lassen
sich 216 65536, mit vier Bytes sogar 4294967296 verschiedene Zeichen codieren, was
vermutlich für eine Weile reichen dürfte. Es führt aber nicht weiter, sich auf Einzelheiten
des Unicodes einzulassen, denn im Grunde hat sich nichts geändert: Es handelt sich nur
um eine Konvention zur dualen oder auch hexadezimalen Darstellung von Zeichen im
Rechner.
Übung 4.8:
a) Stellen Sie die Zeichenkette ?Informatik! im ASCII-Code in hexadezimaler
Schreibweise dar.
b) Welches Wort wird durch den auf zweistelligen Hexadezimalzahlen beruhenden
ASCII-Code 436F64696572756E67 dargestellt?
64 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Sie wissen jetzt zwar, wie man grundsätzlich so rechnet, als wäre man ein Computer,
aber wie wird das nun eigentlich auf der Basis von elektrischem Strom im Rechenwerk
realisiert? Auch Einsen und Nullen kann kein Computer der Welt direkt verstehen, er
versteht nur den Strom, der fließt oder es bleiben lässt. Wir müssen uns also ein paar
Gedanken darüber machen, wie man die Rechenoperationen aus dem letzten Abschnitt
konkret mithilfe von elektronischen Schaltungen umsetzen kann. Glücklicherweise ha-
ben Sie bei der binären Arithmetik gelernt, dass Subtraktionen auf Additionen zurück-
zuführen sind, und man kann sich auch schnell überlegen, dass das Multiplizieren und
Dividieren mithilfe von Additionen und Subtraktionen durchgeführt werden können.
Deshalb werde ich mich hier darauf beschränken, eine Art von Addierwerk zu bauen,
das in der Lage sein soll, duale Zahlen zu addieren.
Das wesentliche Hilfsmittel bei der Realisierung der Addition sind die logischen Schal-
tungenoder auch logischen Gatter. Sie beruhen auf nur drei sehr einfachen logischen
Konstruktionen, weshalb man auch drei verschiedene Grundschaltungen unterscheidet.
Fangen wir vorsichtig mit der ersten an.
A
& A∧B
B
GDI01 65
© HfB, 02.12.20, Berk, Eric (904709)
Das Formelsymbol hat man der Aussagenlogik entnommen, für zwei Inputs schreibt
man A ∧ B, für drei Inputs A,B und C entsprechend A ∧ B ∧ C. Ich will mich hier nicht
über die Rechenregeln für das UND-Symbol ∧ verbreiten, das gehört in die Algebra, Dis-
krete Mathematik oder auch Digitaltechnik. Wir sehen uns lieber an, wie sich die UND-
Schaltung in einer konkreten Tabelle ausdrückt. Schwer ist das nicht, man muss nur alle
möglichen Input-Kombinationen und ihren jeweiligen Output ordentlich in einer Tabel-
le zusammenfassen. Bei drei Inputs sieht das dann wie in Tab. 5.1 aus.
A B C A∧B∧C
0 0 0 0
0 0 1 0
0 1 0 0
0 1 1 0
1 0 0 0
1 0 1 0
1 1 0 0
1 1 1 1
Sie sehen auch hier wieder: Der Output liefert Ihnen genau dann eine 1, wenn alle Inputs
auf 0 standen. Beachten Sie übrigens, wie ich die möglichen Inputwerte in der Tabelle
eingetragen habe. Da es sich um drei Inputs handelt, habe ich es mit dreistelligen dualen
Zahlen zu tun, und deshalb habe ich einfach die dreistelligen dualen Zahlen in die Zeilen
der Tabelle hineingeschrieben. Von 0002 bis 1112, also von 0 bis 7, gibt es aber genau
acht Zahlen, daher hat die Tabelle auch acht Zeilen.
Zur Verdeutlichung hat man sich ein grafisches Symbol überlegt, das die Einzeichnung
von UND-Schaltungen in Schaltbilder zur leichten Übung werden lässt; Sie sehen dieses
für sich sprechende Symbol in Abb. 5.1.
Die UND-Schaltung bestimmt aus mehreren dualen Inputs einen dualen Output. Der
Output ist genau dann 1, wenn alle Inputs auf 1 stehen; hat auch nur ein Input den
Wert 0, so liefert die UND-Schaltung den Output 0. Die UND-Verknüpfung von zwei
Inputs A und B wird mit dem Symbol A ∧ B symbolisiert.
Gehen wir einen Schritt weiter und sehen uns die nächste logische Schaltung an. Das
Wort „oder“ hat in der deutschen Sprache zwei Bedeutungen, auch wenn man sich das
nicht immer klarmacht. Der nette Straßenräuber, der Ihnen auf der B-Ebene der Frank-
furter Konstablerwache den freundlichen Satz „Geld her oder ich schieße“ zuraunt,
meint genau genommen „entweder – oder“, denn sobald Sie Ihre Brieftasche herausrü-
cken, sollte man zumindest hoffen, dass er Sie ungeschoren lässt. Im Zusammenhang
mit der Informatik im Allgemeinen und der Logik sowie den Schaltungen im Besonde-
ren ist etwas anderes gemeint, unser Oder ist ein Oder-auch, ein einschließendes Oder,
sodass mit „A oder B“ immer gemeint ist: A oder B oder beides.
66 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Nach dieser Vorrede ist vielleicht schon klar, was man unter einer ODER-Schaltung
bzw. OR-Schaltung zu verstehen hat. Sie liefert genau dann den Output 1, wenn min-
destens einer der beteiligten Inputs den Wert 1 hat; nur wenn alle Inputs auf 0 stehen,
wird sie den Output 0 liefern. Physikalisch betrachtet kann sie durch eine Parallelschal-
tung realisiert werden. Wie schon bei der UND-Schaltung interessiert mich hier aber die
logische, nicht die physikalische Seite dieser Schaltung. Ihr Formelsymbol hat man wie-
der der Aussagenlogik entnommen, für zwei Inputs schreibt man A ∨ B, für drei Inputs
A, B und C entsprechend A ∨ B ∨ C und so weiter.
Wie sieht das nun in einer Tabelle aus? Ganz einfach. Nehmen wir wie im Falle der
UND-Schaltung wieder drei Inputs A, B und C an, so ist A ∨ B ∨ C genau dann 0, wenn
sowohl A 0 als auch B 0 als auch C 0 gilt, in allen anderen Fällen kommt 1 heraus.
An der Menge der möglichen Inputkombinationen ändert sich gar nichts, es sind immer
noch die gleichen acht Möglichkeiten wie bei der Tabelle für die UND-Schaltung. Damit
ergibt sich Tab. 5.2.
A B C A∨B∨C
0 0 0 0
0 0 1 1
0 1 0 1
0 1 1 1
1 0 0 1
1 0 1 1
1 1 0 1
1 1 1 1
Und auch für die ODER-Schaltung hat sich ein eigenes Symbol durchgesetzt, das Sie in
Abb. 5.2 bewundern können.
A
A∨B
B
Die ODER-Schaltung bestimmt aus mehreren dualen Inputs einen dualen Output.
Der Output ist genau dann 0, wenn alle Inputs auf 0 stehen; hat auch nur ein Input
den Wert 1, so liefert die ODER-Schaltung den Output 1. Die ODER-Verknüpfung
von zwei Inputs A und B wird mit dem Symbol A ∨ B symbolisiert.
Eine logische Schaltung fehlt noch in der Liste der grundlegenden Schaltungen, nämlich
die NICHT-Schaltung oder auch NOT-Schaltung, und sie ist von allen drei logischen
Grundschaltungen die einfachste – aus einer 1 macht sie eine 0, aus einer 0 dagegen eine
GDI01 67
© HfB, 02.12.20, Berk, Eric (904709)
1. Man spricht daher auch von der Negation eines Inputs und meint damit, dass sich der
Wert des Inputs genau in sein Gegenteil verkehrt. Für diese Negation sind sogar zwei
verschiedene Symbole im Gebrauch; ist A ein Input für die NICHT-Schaltung, so wird
der Output von manchen als A und von anderen als ¬A bezeichnet. Beides ist gebräuch-
lich, und Sie können sich aussuchen, was Ihnen besser gefällt.
Die tabellarische Darstellung der Negation ist ausgesprochen übersichtlich, da ich nur
einen Input und einen Output habe. Das führt zu Tab. 5.3.
A A
0 1
1 0
Es wird niemanden überraschen, dass sich auch für die NICHT-Schaltung ein Symbol
durchgesetzt hat, das ihre Darstellung in größeren Schaltbildern vereinfacht. In Abb. 5.3
ist es zu sehen.
A 1
Die NICHT-Schaltung bestimmt aus einem dualen Input einen dualen Output. Der
Output ist genau dann 1, wenn der Input auf 0 steht, und genau dann 0, wenn der
Input auf 1 steht. Wird ein Input A der NICHT-Schaltung unterworfen, so schreibt
man für den Output A oder auch ¬A.
Übung 5.1:
Übung 5.2:
Untersuchen Sie, ob es möglich ist, mit nur zwei logischen Grundschaltungen aus-
zukommen anstatt, wie dargestellt, mit drei Grundschaltungen.
So viel zu den Grundschaltungen. Jetzt sehen wir uns an, was man damit anfangen kann.
68 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
A B AB
0 0 0
0 1 1
1 0 1
1 1 0
Offenbar liefert A B genau dann eine 1, wenn entweder A oder B auf 1 steht. Nun
will ich aber nur die drei Grundschaltungen verwenden und nicht alle fünf Minuten ge-
zwungen sein, eine weitere Grundschaltung einzuführen. Folglich muss ich jetzt zuse-
hen, wie ich diese neue Schaltung aus meinen alten Schaltungen zusammensetze.
Sehen wir uns das XOR etwas genauer an und formulieren die Bedingungen um, unter
denen es den Output 1 liefert. Es ist doch A B 1 genau dann, wenn A 0 und B 1
gilt oder wenn A 1 und B 0 gilt. Da aber A 0 genau dann gilt, wenn man A 1
GDI01 69
© HfB, 02.12.20, Berk, Eric (904709)
hat, bedeutet das: A B 1 gilt genau dann, wenn A 1 und B 1 gilt oder wenn
A 1 und B 1 gilt. Das ist aber praktisch, denn in dieser Beschreibung kommen nur
noch die Schlüsselwörter „und“, „oder“ und die Negation vor, und das waren genau mei-
ne drei logischen Grundschaltungen. Wenn Sie jetzt nämlich die letzte Beschreibung in
eine formale Schreibweise übersetzen, dann stellen Sie fest, dass genau dann A B 1
gilt, wenn A B 1 oder B A 1 gilt. Und da ich auch ein Symbol für das ODER ha-
be, folgt insgesamt:
A B ( A B ) ( A B ).
Sobald man das einmal gefunden hat, kann man es auch leicht nachprüfen, indem man
einfach für die rechte Seite eine Tabelle aufstellt und die Ergebnisspalte mit der von
A B vergleicht. Natürlich sind dann beide Spalten identisch.
Damit ist das XOR übersetzt in eine Kombination der drei bekannten und beliebten
Grundschaltungen. Üblicherweise setzt man so etwas um in ein Schaltbild, das den Vor-
teil einer etwas größeren Anschaulichkeit hat. Das Schaltbild für das ausschließende
ODER sehen Sie in Abb. 5.4.
A 1
&
B
⊕
1
&
Die beiden Inputs heißen A und B. Von A aus gerät man sowohl in ein NICHT-Gatter
als auch – über eine Abzweigung, die durch einen dicken schwarzen Punkt markiert
wird – in ein UND-Gatter, und genau das Gleiche passiert mit B. Der Wert A wird dann
über eine UND-Schaltung mit B kombiniert, was zu dem Zwischenergebnis A B
führt. Analog wird der Wert B über die zweite UND-Schaltung kombiniert mit A selbst,
und das ergibt natürlich A B . Das war es auch schon fast, denn diese beiden Zwi-
schenergebnisse müssen nur noch in einem letzten ODER-Gatter zusammengefasst wer-
den, um am Ende das Ergebnis A B ( A B ) ( B A ) zu erhalten.
Die XOR-Schaltung bestimmt aus zwei dualen Inputs einen dualen Output. Der Out-
put ist genau dann 1, wenn genau ein Input auf 1 steht, wenn also entweder der eine
oder andere Input den Wert 1 hat. Haben beide Inputs den gleichen Wert, so liefert
die XOR-Schaltung den Output 0. Die XOR-Verknüpfung von zwei Inputs A und B
wird mit dem Symbol A B symbolisiert. Sie kann mithilfe der drei Grundschal-
tungen UND, ODER und NICHT erzeugt werden.
Und weil das XOR so eine schöne Schaltung ist, hat es auch ein eigenes Symbol, das Sie
sich in Abb. 5.5 ansehen können.
70 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
A
=1 ⊕ B
B
Nun bin ich so weit, dass ich den ersten Addierer bauen kann, den sogenannten Halb-
addierer. Er leistet nichts weiter als die Addition zweier Dualziffern und liefert dabei
eine Ergebnisziffer und einen Übertrag, beides kann 0 oder 1 werden. Da es nun um kon-
krete Ziffern geht, werde ich die Bezeichnungsweise ändern und die beiden Inputziffern
mit x und y bezeichnen, die Ergebnisziffer r nennen und den Übertrag u. Dann ist also
r die Ziffer, die man beim schriftlichen Addieren unter dem Strich finden würde, und u
ist der Übertrag, der für das weitere Rechnen über den Strich geschrieben wird. Das er-
gibt dann Tab. 5.5.
x y r u
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1
Werfen Sie einen Blick auf die beiden Ergebnisspalten. Kommen sie Ihnen bekannt vor?
Ich hoffe doch, denn beide haben wir bereits besprochen. Die Ergebnisziffer r entspricht
genau der XOR-Verknüpfung der beiden Inputs x und y, während der Übertrag noch ein-
facher gebaut ist, denn er wird genau dann 1, wenn beide Inputs auf 1 stehen, und stellt
daher nur die UND-Verknüpfung von x und y dar. Also ist schlicht r x y und
u x ∧ y. Um das wieder in ein Schaltbild zu zeichnen, muss ich jetzt natürlich nicht
mehr alle Einzelheiten der XOR-Schaltung aufmalen, die kennen Sie bereits aus
Abb. 5.4. Es genügt jetzt, das XOR als eine Schaltung zu betrachten, die man bereits als
Bauteil zur Verfügung hat, und diese Schaltung als eine Einheit in die gesuchte Schal-
tung des Halbaddierers einzubauen. In Abb. 5.6 sehen Sie, was dabei herauskommt.
x r
=1
y
u
&
Ich habe damit eine Schaltung zusammengebaut, die auf der Basis von elektrischem
Strom mithilfe der drei Grundschaltungen zwei Ziffern addiert und eine Ergebnisziffer
sowie einen Übertrag berechnet. Die Schaltung heißt deshalb Halbaddierer, weil sie nur
GDI01 71
© HfB, 02.12.20, Berk, Eric (904709)
die Hälfte der Arbeit erledigt, die man beim Addieren von Dualzahlen braucht. Wenn
Sie nämlich zwei duale Zahlen addieren, dann ist es zwar sicher wahr, dass Sie in jeder
Stelle eine Ergebnisziffer und einen Übertrag bekommen werden, aber Sie müssen im-
mer damit rechnen, dass aus der vorher betrachteten Stelle noch ein Übertrag da ist, den
Sie berücksichtigen müssen. Bei Additionen der Form
1 1 1 0 1 1
1 11 11 11 01 1
1 0 1 1 0 0 0
wird es ziemlich häufig passieren, dass man drei Ziffern addieren muss und nicht nur
zwei, und genau das kann der Halbaddierer nicht leisten. Es hilft also nichts, ich werde
das Spiel noch etwas weitertreiben und Ihnen zeigen, wie man einen Volladdierer baut,
der dieses Problem löst.
Der Halbaddierer bestimmt aus zwei dualen Inputs zwei duale Outputs. Er addiert
zwei Ziffern x und y und liefert dabei eine Ergebnisziffer r und einen Übertrag u.
Man kann ihn zusammensetzen aus einer XOR-Schaltung und einer UND-Schal-
tung; es gilt r x y und u x ∧ y.
Bei der Addition zweier mehrstelliger Dualzahlen wird man nicht nur jeweils zwei Zif-
fern x und y, sondern leider auch noch einen alten Übertrag u1 aus der vorherigen Stelle
addieren müssen. Diese Addition aus drei Inputs liefert dann eine Ergebnisziffer r und
einen neuen Übertrag u2, genau wie schon beim Halbaddierer. Die zugehörige Tab. 5.6
ist schnell aufgeschrieben.
x y u1 r u2
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1
Jetzt muss ich nur noch feststellen, wie ich diese Tabelle in eine vernünftige Schaltung
umsetzen kann. Ich fange mit dem Output r an. Wann steht r auf 1? Offenbar genau
dann, wenn genau ein Input auf 1 steht oder wenn alle drei Inputs auf 1 stehen. In der
zweiten Zeile ist beispielsweise nur u1 1, und das heißt, dass sowohl x 1 als auch
y 1 gilt. Anders gesagt: Genau dann gilt gleichzeitig x 0 und y 0 und u1 1, wenn
72 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
x y u1 1 gilt. In diesem Fall habe ich auf jeden Fall r 1. Aber nicht nur in diesem
Fall, es könnte ja auch sein, dass x 0,y 1 und u1 0 gilt, wie es der dritten Zeile mei-
ner Tabelle entspricht. Das bedeutet natürlich x 1, y 1 und u1 1 , also ist dieser Fall
genau dann gegeben, wenn x y u1 1 ist. Sie können sich schon denken, wie es wei-
ter geht. In der fünften Zeile hat r wieder eine 1 aufzuweisen, und weil hier x 1,y 0
und u1 0 gilt, muss x y u1 1 sein. Damit habe ich die Fälle erledigt, in denen ge-
nau einer der Inputs auf 1 steht. Es bleibt noch die letzte Zeile der Tabelle, bei der über-
haupt alles zu 1 wird, insbesondere auch x, y und u1. Diese drei haben aber genau dann
gleichzeitig den Wert 1, wenn x y u1 1 ist, denn genauso war die UND-Verknüp-
fung gemacht.
Gefunden habe ich jetzt, dass r genau dann den Wert 1 liefert, wenn x y u1 1 oder
x y u1 1 oder x y u1 1 oder x y u1 1 gilt. Und weil wir für solche
Zwecke eine ODER-Verknüpfung haben, heißt das:
r (x y u1 ) (x y u1 ) (x y u1 ) (x y u1 )
(x y u1 ) (x y u1 ) (x y u1 ) (x y u1 ).
In der zweiten Zeile habe ich dabei nur den ersten Klammerausdruck nach vorn gestellt,
damit ich nicht gleich mit so etwas Negativem wie einer Negation anfangen muss. Jetzt
habe ich meine logische Schaltung für die Ergebnisziffer schon zusammen. Ich werde sie
auch gleich in ein Schaltbild eintragen, aber das liefert nur noch eine grafische Veran-
schaulichung dieser Formel, die Informationen stecken schon vollständig in dem Aus-
druck, den ich aus der Tabelle geschlossen habe. Noch bin ich allerdings nicht fertig, der
Übertrag u2 fehlt mir noch, und er verdient einen genaueren Blick.
Gehen wir zunächst nach dem gleichen Schema vor wie eben. Der Output u2 liefert in
der vierten, sechsten, siebten und achten Zeile der Tabelle eine 1, was ich wieder leicht
in eine logische Schaltung übersetzen kann. In der vierten Zeile ist x 0,y 1 und
u1 1, was zu dem Ausdruck x y u1 führt. Nach der gleichen Methode entsprechen
die restlichen interessanten Zeilen den Ausdrücken x y u1 , x y u1 und
x y u1 . Daraus folgt, dass
u2 (x y u1 ) (x y u1 ) (x y u1 ) (x y u1 )
gilt.
Denken Sie daran: Das sollen konkrete Schaltungen in einem Computer werden, und je
weniger Bauteile Sie benötigen, desto besser für die Kostenrechnung. Den Übertrag u2
können Sie aber tatsächlich ein wenig kürzer darstellen, mit einer deutlich einfacheren
Schaltung. Noch einmal ziehe ich die Tabelle zurate und sehe mir genauer an, wann
u2 1 ist. Das gilt nämlich genau dann, wenn mindestens zwei der Inputs auf 1 stehen,
wenn also x ∧ y 1 oder x ∧ u1 1 oder y ∧ u1 1 ist. Sobald eine dieser drei Bedingun-
gen erfüllt ist, können Sie sich auf keinen Fall in der ersten, zweiten, dritten oder fünften
Zeile der Tabelle befinden, denn dort würde jede der drei UND-Verknüpfungen den
GDI01 73
© HfB, 02.12.20, Berk, Eric (904709)
Wert 0 ergeben. In der letzten Zeile sind sogar alle drei Bedingungen auf einmal erfüllt,
und in den anderen drei Zeilen jeweils eine, weshalb ich u2 auch durch den wesentlich
einfacheren Ausdruck
u2 (x y ) (x u1 ) ( y u1 )
darstellen kann. Sollten Sie daran zweifeln, dann empfehle ich Ihnen, diesen Ausdruck
einfach in einer Input-Output-Tabelle auszuwerten und das Ergebnis mit u2 zu verglei-
chen. Spätestens das wird Sie überzeugen.
Nun habe ich sowohl für die Ergebnisziffer r als auch für den Übertrag u2 eine logische
Schaltung gefunden und muss die beiden Schaltungen nur noch in einem Schaltbild dar-
stellen. Das wird natürlich etwas komplizierter ausfallen als die bisherigen recht über-
sichtlichen Schaltbilder, denn die Schaltungen sind nun mal aufwendiger; sie sehen es in
Abb. 5.7 vor sich.
1
&
1
1
1
&
&
r
&
&
&
2
&
Wenn Sie die Verbindungslinien unter Berücksichtigung der dick eingezeichneten Kno-
tenpunkte verfolgen, werden Sie die Übereinstimmung des Schaltbildes mit meinen
mühsam ermittelten logischen Ausdrücken feststellen, und nur darauf kommt es an. Im
unteren Teil des Bildes werden zum Beispiel jeweils zwei Inputs in einer UND-Schaltung
verknüpft, woraufhin die Ergebnisse der UND-Verknüpfungen in einem ODER-Gatter
zusammengeführt werden: Das ist genau die Schaltung, die mir den Übertrag u2 liefert.
Im oberen Teil habe ich zunächst alle drei Inputs in ein UND-Gatter geführt und den
Ausgang dieses UND-Gatters zum Input eines ODER-Gatters werden lassen: Damit
wird x ∧ y ∧ u1 zu einem der vier Inputs des großen ODER-Gatters, und genauso verlangt
es der logische Ausdruck für r. Danach wird jeder der drei Inputs x, y und u1 durch ein
NICHT-Gatter geschickt und so mit den anderen Inputs UND-verknüpft, dass die drei
Ausdrücke x y u1 , x y u1 und x y u1 entstehen, die sich dann zusammen
mit dem vorher erzeugten x ∧ y ∧ u1 in einem ODER-Gatter vereinigen, das schließlich
die Ergebnisziffer r liefert.
Damit haben Sie nun den Grundbaustein in der Hand. Sie hatten gesehen, dass die ge-
samte duale Arithmetik sich auf die Addition zurückführen lässt, und wie man eine Ad-
dition von dualen Ziffern umsetzt, habe ich Ihnen gerade gezeigt.
74 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Der Volladdierer bestimmt aus drei dualen Inputs zwei duale Outputs. Er addiert
zwei Ziffern x und y zu einem Übertrag u1 und liefert dabei eine Ergebnisziffer r und
einen Übertrag u2. Es gelten die Formeln:
r (x y u1 ) (x y u1 ) (x y u1 ) (x y u1 )
und
u2 (x y ) (x u1 ) ( y u1 ).
Aber wie werden denn jetzt ganze Dualzahlen addiert, wie wird man die Beschränkung
auf Ziffern los? Das ist ganz einfach. Nehmen wir an, dass unser Rechner in der Lage
sein soll, vierstellige Zahlen zu addieren, so muss ich nur die beiden entwickelten Schal-
telemente „Halbaddierer“ und „Volladdierer“ richtig zusammenschalten. Zu diesem
Zweck bezeichne ich den Halbaddierer mit dem Symbol HA und den Volladdierer mit
dem Symbol VA. Addieren will ich die beiden vierstelligen Dualzahlen x3x2x1x0 und
y3y2y1y0. Dann ergibt sich das Additionswerk aus Abb. 5.8; die Inputs stehen über dem
Additionswerk, der Output r4r3r2r1r0 setzt sich aus den Ergebnissen zusammen, die von
den einzelnen Additionsschaltungen geliefert werden.
x3 y3 x2 y2 x1 y1 x0 y0
VA VA VA HA
u3 u2 u1
r4 r3 r2 r1 r0
Zuerst werden die beiden Einserstellen x0 und y0 addiert, das sind nur zwei Stellen, also
brauche ich dazu nur einen Halbaddierer. Die Ergebnisziffer r0 wird bei den Ergebnissen
notiert, der Übertrag u1 wird an die nächste Stelle weitergereicht. Für die nächste Addi-
tion brauche ich dann auch tatsächlich einen Volladdierer, denn hier habe ich die bei-
den Inputziffern x1, y1 und den Übertrag u1 der vorherigen Addition zu addieren. Die
Ergebnisziffer r1, die der Volladdierer liefert, wird wieder bei den Ergebnissen notiert,
der Übertrag u2 zur nächsten ziffernweisen Addition weitergegeben. Und so zieht sich
das Spiel durch, bis das Ende in Gestalt der letzten Stelle erreicht ist. Hier gibt es natür-
lich keinen Übertrag mehr, denn es steht keine weitere Stelle mehr zur Verfügung, mit
der gerechnet werden könnte. Ob dann die Ergebnisziffer r4 wirklich zu den Ergebnissen
gerechnet wird, hängt ab von der tatsächlichen Architektur Ihres Rechenwerks: Wenn
beispielsweise durchgängig nur mit vier Stellen gerechnet wird, haben Sie im Falle r4
1 einen sogenannten Überlauf und r4 geht verloren. Man sollte deshalb immer eine Stel-
le mehr zur Verfügung haben, als man zu brauchen glaubt, denn für gewöhnlich wird
ein Überlauf im Dunkel der Nacht verschwinden.
GDI01 75
© HfB, 02.12.20, Berk, Eric (904709)
Ein Additionswerk oder auch Addierwerk setzt sich zusammen aus einem Halbad-
dierer und mehreren Volladdierern, wobei die Anzahl der Volladdierer von der An-
zahl der gewünschten Stellen der verwendeten Dualzahlen abhängt. Mit ihm ist es
möglich, Dualzahlen nach den Regeln der dualen Arithmetik auf der Basis der drei
logischen Grundschaltungen zu addieren.
&
&
r
&
1
1
1
Übung 5.3:
Stellen Sie für r (x y ) z eine Funktionstabelle auf. Vergleichen Sie die Ergeb-
nisse mit denen der Ergebnisziffer beim Volladdierer. Wie kann man mithilfe von
(x y ) z das Schaltbild des Volladdierers vereinfachen?
Übung 5.4:
Setzen Sie die Schaltung aus Abb. 5.9 in eine Tabelle um.
Übung 5.5:
Setzen Sie Tab. 5.7 mit den Inputs x,y und dem Output r in eine logische Schaltung
um. Wie hängt diese Schaltung mit der XOR-Schaltung zusammen?
x y r
0 0 1
0 1 0
1 0 0
1 1 1
Genug geschaltet, und damit Sie auch wirklich ein mal abschalten können, darf ich Ih-
nen mitteilen, dass Sie nun diese Einführung in die Informatik hinter sich gebracht ha-
ben.
76 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
GDI01 77
© HfB, 02.12.20, Berk, Eric (904709)
duziert sich die Anzahl der noch zur Auswahl stehenden Städte von Station
zu Station um 1, sodass jeweils eine um 1 geringere Zahl zum bereits vorhan-
denen Produkt dazu multipliziert werden muss. Insgesamt hat man daher
19 · 18… 2 · 1 19! verschiedene mögliche Reiserouten.
b) Der gegebene Rechner kann 1010 Routen pro Sekunde testen. Um festzustel-
len, wie lange er für die Ermittlung der optimalen Route braucht, muss man
einen einigermaßen genauen Wert für 19! ausrechnen; es gilt 19! ≈ 1,216451
· 1017. Diese Anzahl der möglichen Reiserouten muss man durch die Anzahl
der überprüfbaren Routen pro Sekunde teilen. Das ergibt
1,216451 1017
10
1,216451 107 . Da ein Jahr 31536000 3,1536 · 107 Sekun-
10
1,216451 107
den hat, braucht der gegebene Computer 0,3857 Jahre, also
3,1536 107
etwa 141 Tage.
1.4 Sofern a0 ist, wird a auf den Wert 2 gesetzt, ansonsten behält a seinen Wert.
Die Abfrage, ob a0 gilt, ist völlig sinnlos, da an dieser Stelle schon a0 fest-
gestellt wurde. Man kann also diese Abfrage weglassen.
1.5 Betrachtet man nur den reinen Text, so sind beide Algorithmen identisch. Der
einzige Unterschied liegt in der Einrückung, und genau dadurch wird der we-
sentliche inhaltliche Unterschied zum Ausdruck gebracht. In Algorithmus 1 wird
nur beschrieben, was zu tun ist, wenn die Ampelanlage funktioniert: Falls dann
die Ampel rot oder gelb ist, soll der Fahrer stehen bleiben, ansonsten fahren. Das
Wort sonst“ in diesem Algorithmus bezieht sich also, wie man nur an der Einrü-
ckung sehen kann, auf den „wenn“-Fall, dass die Ampel rot oder gelb ist, be-
schreibt also die Situation, dass eine funktionierende Ampel grün ist. Algorith-
mus 2 arbeitet ganz anders. Zunächst beschreibt auch er die Handlungsfolge im
Falle einer funktionierenden Ampel: Falls die Ampel rot oder gelb ist, soll der
Fahrer stehen bleiben. Weiter verrät dieser Algorithmus nichts über das Verhal-
ten bei einer funktionierenden Ampel, denn der sonst“-Zweig in der letzten Zeile
bezieht sich aufgrund der Einrückung auf die Situation, dass die Ampelanlage
nicht funktioniert, stellt also den sonst“-Zweig zu der übergeordneten Bedin-
gung dar, dass die Ampelanlage funktioniert. Algorithmus 2 weist also den Fah-
rer an, bei einer nicht funktionierenden Ampelanlage einfach loszufahren. Im
Falle einer funktionierenden Anlage erhält der Fahrer zwar bei roter oder gelber
Ampel klare Anweisungen, weiß aber nicht, was er bei einer grünen Ampel zu
tun hat. Dagegen hat Algorithmus 1 das Verhalten bei einer funktionierenden
Anlage vollständig beschrieben, den Fahrer aber mit der Situation einer nicht
funktionierenden Anlage allein gelassen. Die beiden Algorithmen unterscheiden
sich also dann, wenn die Ampelanlage funktioniert und die Ampel grün ist (Al-
gorithmus 1 empfiehlt dann fahren, Algorithmus 2 schweigt sich aus) und wenn
die Ampelanlage nicht funktioniert (Algorithmus 1 sagt gar nichts, Algorithmus
2 empfiehlt fahren). Obwohl also beide Algorithmen keineswegs vollständig
sind, ist aus Sicherheitsgründen eher Algorithmus 1 zu empfehlen.
1.6 Sobald das Programm von Zeile 03 zu Zeile 07 gesprungen ist, ergibt sich immer
wieder der folgende Ablauf: 07 → 04 → 05 → 06 → 03 → 07 → 04 → …. Das
Programm befindet sich daher in einer Endlosschleife.
78 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Umrechnen
enachd = 1,074
Ausgabe:
Euro nach Dollar (1)?
Dollar nach Euro (2)?
Einlesen antwort
antwort = 1?
wahr falsch
Ausgabe: antwort = 2?
euro eingeben wahr falsch
euro eingeben Ausgabe:
dollar eingeben
dollar = euro * enachd
dollar eingeben Ausgabe
Fehlermeldung
dollar ausgeben euro = dollar / enachd
euro ausgeben
Steuer
einkommen eingeben
einkommen <= 600 ?
wahr falsch
einkommen <= 1200 ?
wahr falsch
e = einkommen 1200
Ausgabe: steuer = einkommen > 15000 ?
keine Steuern 0,3 * (einkommen600) wahr falsch
steuer = steuer =
180 + 0,5*e 180 + 0,4*e
steuer ausgeben
GDI01 79
© HfB, 02.12.20, Berk, Eric (904709)
Messung
summeplus=0,summeminus=0,summe=0
anzahlplus=0,anzahlminus=0,anzahl=0
eingeben x
summe = summe + x
anzahl = anzahl +1
x>0?
wahr falsch
summeplus =
summeplus + x wahr falsch
eingeben x
anzahlplus > 0 ?
wahr falsch
Ausgabe:
Ausgabe: keine positiven Werte
summeplus, summeplus/anzahlplus
anzahlminus > 0 ?
wahr falsch
Ausgabe:
Ausgabe: keine negativen Werte
summeminus, summeminus/anzahlminus
anzahl > 0 ?
wahr falsch
Ausgabe: Ausgabe: keine Werte
summe, summe/anzahl
Lineare Gleichungen
Ausgabe: Lösen linearer Gleichungen
eingeben von a und b
a=0?
wahr falsch
b=0?
wahr falsch
Ausgabe: Ausgabe: Ausgabe: x
allgemeingültig unlösbar
Ausgabe: weitere Gleichung?
eingeben antwort
solange antwort = 'j' oder antwort = 'J'
80 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
double loesen()
{
return -b/a;
}
}
2.6 Die Klasse braucht mindestens die beiden Attribute breite und hoehe, die
den Typ double haben sollten. Zur Berechnung der Fläche braucht man eine
Methode flaeche, die den Wert breite * hoehe zurückgibt; zur Berech-
nung des Umfangs braucht man eine Methode umfang, die den Wert
2*(breite hoehe) zurückgibt. Hat man dann ein konkretes Objekt
meinrechteck der Klasse Rechteck gegeben, so kann man die entsprechen-
den Berechnungen mithilfe von meinrechteck.umfang() und
meinrechteck.flaeche() vornehmen.
2.8 a) Der Algorithmus startet mit einer Ausgabe, einer Eingabe und einer Abfrage.
Für n0 erfolgen zwei Wertzuweisungen und n Schleifendurchläufe, wobei
in jedem Durchlauf zwei Additionen und eine Abfrage stattfinden. Insge-
samt sind das 3 1 1 3n 3n 5 Operationen.
b) Nach Teil a) ist T(n) O(n).
2.9 a) Da man sich beim Einsatz der Landausymbole auf die höchste vorkommende
Potenz beschränken kann, gilt TA(n) O(n2) und TB(n) O(n3).
b) Für n 256 braucht Algorithmus A genau 32767984 Operationen, Algorith-
mus B dagegen 8390023 Operationen, wie man durch Einsetzen in die For-
meln feststellt. Daher ist für n 256 Algorithmus B vorzuziehen.
GDI01 81
© HfB, 02.12.20, Berk, Eric (904709)
c) Es gilt:
1 3 11 1 11
n n 7 (500n 2 16) n 3 500n 2 n 23
2 2 2 2
1
(n 3 1000n 2 11n 46).
2
Für n ≥ 1024 1000 ist aber n3 – 1000n2 n2 · (n – 1000) 0 und natürlich
1 11
auch 11n 46 0. Daher ist in diesem Fall n 3 n 7 500n 2 16 , wes-
2 2
halb Algorithmus A vorzuziehen ist.
3.1 • Vorteile:
– niedrige Kosten
– Haltbarkeit
– auch ohne Maschine lesbar
– leicht zu beschreiben
• Nachteile:
– Dateneingabe zeitraubend
– Dateneingabe fehleranfällig
– platzraubende Lagerung
– niedrige Einlesegeschwindigkeit
3.2 Gödels Aussage ist im Prinzip richtig, da jedes Computerprogramm nichts ande-
res als Logik braucht. Schließlich baut es auf den beiden Zuständen „kein Strom
fließt“ und „Strom fließt“ auf, die man durch die beiden Zustände 0 und 1 be-
schreiben kann, was den beiden Wahrheitswerten „falsch“ und „wahr“ ent-
spricht. In der Praxis ist Gödels Auffassung aber unbrauchbar, denn ein direktes
Programmieren eines Computers nur auf der Basis von Nullen und Einsen ist ex-
trem aufwendig und fehleranfällig.
3.3 Bei einem Band ohne Einsen kommt zunächst die Programmzeile
z 0 , z E ,, H zum Einsatz. Es wird zu Beginn ein Leerzeichen gelesen, da-
nach geht die Maschine in den Endzustand über, schreibt das Leerzeichen wieder
auf das Band und hält an. Wegen 2 · 0 0 wurde die Anzahl der Einsen verdop-
pelt. Bei nur einer 1 startet die Maschine mit der ersten Programmzeile, geht in
den Zustand z1 über, schreibt ein a anstelle der 1 und rückt nach rechts. Da sie
dort auf eine Leerstelle trifft, zieht nun die dritte Programmzeile, nach der die
Maschine in den Zustand z2 übergeht, die Leerstelle durch ein b ersetzt und nach
links rückt. Dort agiert die siebte Programmzeile, ersetzt das a durch eine 1, geht
in den Zustand z0 über und rückt wieder nach rechts, wo die Turing-Maschine
wegen der achten Programmzeile auch das b durch eine 1 ersetzt, im Zustand z0
verharrt und nach rechts rückt. Dort trifft sie im Zustand z0 auf eine Leerstelle
und wird daher im letzten Schritt in den Endzustand übergehen und anhalten.
82 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
z 0 ,1 z 0 ,1, R
z 0 , z 0 ,1, R
z 0 , z1 ,, L
z1 ,1 z E ,, H
3.5 a) Ein Computer wäre auch ohne einen ausgeprägten Arbeitsspeicher denkbar,
wenn man dafür sorgt, dass zumindest das jeweils zur Verarbeitung benötig-
te Element in der Zentraleinheit gespeichert werden kann. Liegt also eine
kleine Zahl hinreichend großer Speicherstellen in der Zentraleinheit vor, so
kann ein Computer im Prinzip auch ohne Arbeitsspeicher zum Einsatz kom-
men. Sinnvoll wäre das allerdings nicht, denn es führt dazu, dass andauernd
Schreib- und Lesevorgänge auf die Festplatte und von der Festplatte durch-
geführt werden müssen, was die Verarbeitungsgeschwindigkeit stark redu-
zieren würde.
b) Ersetzt man die Festplatte durch eine andere Form von Festspeicher, so ist
auch ein Computer ohne Platte denkbar. Nimmt man ihm aber jede Form des
Festspeichers, so sind die Schwierigkeiten kaum noch zu beheben. Zwar
wäre es möglich, Daten und Programme über Lochkarten ein- und sogar
wieder auszugeben, aber von einem in irgendeiner Form modernen Compu-
ter wäre man mit dieser Konstruktion weit entfernt.
Insgesamt ergibt sich, dass ein moderner Computer sowohl über einen Fest-
speicher als auch über einen Hauptspeicher verfügen sollte.
3.6 Der Arbeitsspeicher dient nur der Aufnahme von Daten und Programmstücken,
ein Rechenwerk ist wegen seiner weitergehenden Aufgaben deutlich komplizier-
ter. Schon um die Konstruktion nicht zu kompliziert werden zu lassen, ist eine
Aufteilung daher sinnvoll. Außerdem wird stets nur mit kleinen Datenmengen
auf einmal gerechnet, sodass es völlig unnötig wäre, den gesamten Arbeitsspei-
cher mit den Fähigkeiten des Rechenwerks zu versehen. Das Argument, man
könnte doch im Gegenzug den Arbeitsspeicher klein halten, ist nicht schlüssig,
wie schon in der vorherigen Aufgabe diskutiert wurde.
3.7 Der Speicher enthält 1 Gigabyte, also 230 Byte. Um 12:01 Uhr sind 1 4 Byte be-
setzt, um 12:02 Uhr 1 4 16 1 4 42 Byte und so weiter. Also sind nach
n Minuten
4n 1 1 22n 2 1
1 4 4 2 4n
3 3
Byte im Speicher besetzt, wobei die Summenformel der geometrischen Summe
entspricht. Gesucht ist also das größte n mit der Eigenschaft:
22n 2 1
230 beziehungsweise 22n 2 3 230 1.
3
GDI01 83
© HfB, 02.12.20, Berk, Eric (904709)
Für n 14 ist die Ungleichung offenbar erfüllt, wie man durch Einsetzen sofort
feststellt. Für n 15 steht auf der linken Seite 232 4 230 3 230 1 , weshalb
n 14 die gesuchte Zahl ist.
230 1
Um 12:14 Uhr muss der Prozess also gestoppt werden, und es sind Byte
3
im
4.1 a) 1101100112 1 2 16 32 128 256 435.
b) 111100012 1 16 32 64 128 241.
4.2 Gegeben seien zwei Dualzahlen m und n, die ohne führende Nullen notiert wer-
den. Hat m mehr Ziffern als n, so ist m n. Hat n mehr Ziffern als m, so ist nm.
Ist die Anzahl der Ziffern von m und n gleich, so vergleiche man, beginnend mit
den führenden Ziffern, die Ziffern von m und n, die an jeweils der gleichen Po-
sition stehen. Sind sie jeweils gleich, so gilt m n. Ansonsten gibt es eine Posi-
tion, bei der zum ersten Mal ein Unterschied auftritt. Hat an dieser Position m
die Ziffer 1 und n die Ziffer 0, so ist mn. Hat dagegen an dieser Position n die
Ziffer 1 und m die Ziffer 0, so ist n m.
4.3 Es gilt:
Also ist 278 1000101102. Auch n 131 kann man auf diese Weise behandeln.
Man sieht aber, dass 131 128 2 1 27 21 20 gilt, und daher ist
131 100000112.
4.4 a) Die Addition ergibt 11010102 101011102 1000110002.
b) Die Addition ergibt 10102 01101102 10000002.
c) Gesucht ist 111011012 – 1010012. Das Zweierkomplement der Zahl
001010012 ist 110101102 12 110101112. Die Addition ergibt 111011012
110101112 1110001002
Streichen der führenden 1 ergibt dann 111011012 – 1010012 110001002.
84 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
GDI01 85
© HfB, 02.12.20, Berk, Eric (904709)
4.8 a) Die beiden Zeichen „?“ und „!“ sind im Text mit der Codierung 3F bzw. 21
angegeben. Da die Buchstaben mit fortlaufender Nummerierung codiert
werden und die Codierungen der Buchstaben „A“ und „a“ mit 41 bzw. 61 an-
gegeben sind, ergibt sich insgesamt die hexadezimale Darstellung
3F496E666F726D6174696B21.
b) Hier ist das Wort „Codierung“ dargestellt.
5.1 Die nötigen Werte finden Sie in Tab. A.1.
5.2 Die drei logischen Grundschaltungen lauten UND, ODER und NICHT. Nach
Übung 5.1 ist aber beispielsweise A B A B und damit A B A B .
Man kann also die UND-Schaltung aus der ODER-Schaltung und der NICHT-
Schaltung kombinieren, sodass nur zwei Schaltungen nötig sind. Entsprechend
folgt aus A B A B , dass man die ODER-Schaltung aus der UND-Schaltung
und der NICHT-Schaltung kombinieren kann.
5.3 Die nötigen Werte finden Sie in Tab. A.2.
Der Vergleich mit der Ergebnisziffer r des Volladdierers ergibt, dass
r (x y ) u1 gilt.
Setzt man also die XOR-Schaltung voraus, so kann man die Ergebnisziffer im
Volladdierer als Kombination zweier XOR-Schaltungen realisieren. Das verein-
fachte Schaltbild sehen Sie in Abb. A.5.
1
=1
=1 r
&
&
2
&
86 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
x y z x y (x y ) z
0 0 0 0 0
0 0 1 0 1
0 1 0 1 1
0 1 1 1 0
1 0 0 1 1
1 0 1 1 0
1 1 0 0 0
1 1 1 0 1
((x y z ) (x y z )) (x y ).
x y z r1 r2 r3 r4 r
0 0 0 0 0 0 1 0
0 0 1 0 0 0 1 0
0 1 0 0 0 0 1 0
0 1 1 0 0 0 1 0
1 0 0 0 0 0 1 0
1 0 1 0 0 0 1 0
1 1 0 0 1 1 0 0
1 1 1 1 0 1 0 0
Die Ergebnisspalte besteht nur aus Nullen. Tatsächlich sieht man an der Tabelle,
dass r3 x y gilt, und es gilt x y x y . Somit ist r4 die Negation von r3,
weshalb r r3 r4 immer den Wert 0 haben muss.
GDI01 87
© HfB, 02.12.20, Berk, Eric (904709)
r (x y ) (x y ).
Offenbar ist r x y .
x 1
&
y 1
&
88 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
B. Literaturverzeichnis
Ernst, H.; Schmidt, J.; Beneken, G.: (2015). Grundkurs Informatik.
5. Aufl., Heidelberg: Springer Vieweg.
Gumm, H. P.; Sommer, M.: (2013). Einführung in die Informatik.
10. Aufl., Berlin: Oldenbourg Wissenschaftsverlag.
Herold, H.; Lurz, B. (2012). Grundlagen der Informatik.
2. Aufl., Pearson Studium.
Rechenberg, P. (2000). Was ist Informatik?
3. Aufl., München: Hanser Fachbuch.
Spannagel, Ch (2013). Turingmaschinen.
http://wikis.zum.de/zum/PH_Heidelberg/Bausteine/Turingmaschinen.
GDI01 89
© HfB, 02.12.20, Berk, Eric (904709)
C. Abbildungsverzeichnis
GDI01
90 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
D. Tabellenverzeichnis
GDI01
GDI01 91
© HfB, 02.12.20, Berk, Eric (904709)
E. Sachwortverzeichnis
GDI01
A D
Abfolge, sequenzielle ....................... 43 Daten ............................................... 50
Additionswerk ................................. 75 Datenverarbeitung ............................. 6
Algorithmus ................................ 3, 10 Definition ........................................ 27
ALU ................................................ 43 Deklaration ...................................... 27
Analytische Maschine ...................... 36 do-Schleife ....................................... 23
Android ........................................... 47 Dualsystem ...................................... 51
Anwendungssoftware ...................... 13 Dualzahl .............................. 50, 51, 52
App ................................................. 48 Addition ...................................... 55
Arbeitsspeicher .......................... 42, 45 Subtraktion ................................. 56
Arithmetical Logical Unit ................. 43 umrechnen .................................. 52
ASCII-Code ..................................... 63
Attribut ........................................... 26 E
Ausgabe ............................................. 4 EDV ................................................... 8
ausschlie{ss ...................................... 69 Eingabe .............................................. 4
Auswahl .................................... 14, 19 Einserkomplement ........................... 59
EVA-Prinzip ....................................... 5
B
Babbage, Charles ............................. 36 F
Betriebssystem ...................... 13, 45, 46 Festplatte ......................................... 44
binary digit ...................................... 45 Festspeicher ..................................... 44
Binärzahl ................................... 50, 52 Funktionstabelle ............................... 69
Bit ............................................. 45, 63
Block ............................................... 17 G
Byte ........................................... 45, 63 Gatter, logische ................................ 65
C H
C ..................................................... 16 Halbaddierer .................................... 71
C++ ................................................. 48 Hardware ......................................... 12
Central Processing Unit .................... 43 Hauptprogramm .............................. 18
Code ................................................ 63 Hauptspeicher .................................. 45
Computer .......................................... 8 Hexadezimalzahl .............................. 60
Aufgabenbereiche ......................... 7
Computernetzwerk ............................ 7 I
Control Unit .................................... 43 Informatik .......................................... 8
CPU ................................................ 43 Instanz ............................................. 26
iOS .................................................. 47
IPO-Prinzip ........................................ 5
Iteration ........................................... 21
92 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
Sachwortverzeichnis E
J R
Java ............................................ 25, 48 RAM ............................................... 45
Random Access Memory .................. 45
K Rechenmaschine
Kapselung ................................... 25, 29 programmgesteuerte ................... 36
Klasse .............................................. 25 Rechenwerk ..................................... 43
Kommunikation ................................. 7 Reihenschaltung .............................. 65
Komplexität ..................................... 31
Kontrolle ........................................... 8 S
Kontrollstruktur .......................... 14, 16 Schaltung ........................................ 65
AND-Schaltung .......................... 65
L logische ....................................... 65
Landau-Symbol ............................... 33 NICHT-Schaltung ....................... 67
NOT-Schaltung ........................... 67
M ODER-Schaltung ......................... 67
Membervariable .............................. 26 OR-Schaltung .............................. 67
Methode ..................................... 25, 28 UND-Schaltung .......................... 65
Schleife
N fußgesteuerte .............................. 22
Nassi ............................................... 17 nicht abweisende ........................ 21
Nassi-Shneiderman-Diagramm ........ 17 Schleifenrumpf ................................ 22
Sequenz ..................................... 14, 16
O Serienschaltung ............................... 65
Objekt ............................................. 25 Shneiderman ................................... 17
Objektorientierung .......................... 24 Software .................................. 3, 5, 12
ODER-Schaltung ............................. 67 Software Engineering ....................... 12
Oktalzahlen ..................................... 62 Softwarekrise ..................................... 3
OR-Schaltung .................................. 67 Speicher
Arbeitsspeicher ........................... 44
P externer ...................................... 44
Parallelschaltung ............................. 67 Festspeicher ................................ 44
Primzahlen ...................................... 32 Hauptspeicher ............................. 45
Problemdefinition ............................ 11 interner ....................................... 44
Programm ..................................... 3, 9 Speicherkomplexität ......................... 31
Programmentwurf ........................... 11 Speicherwerk ................................... 43
Programmiersprache ............. 10, 11, 48 Speicherzelle .................................... 45
Programmierung .............................. 11 Steuerung .......................................... 8
objektorientierte .......................... 25 Steuerwerk ...................................... 43
Prozess ............................................ 11 Struktogramm ........................... 16, 17
Prozessor .................................... 11, 43 Strukturierte Programmierung ......... 13
Systemsoftware ............................... 13
GDI01 93
© HfB, 02.12.20, Berk, Eric (904709)
E Sachwortverzeichnis
T
Turing, Alan .................................... 38
Turing-Maschine .............................. 38
U
Überlauf .......................................... 75
Umrechnung dezimal in dual ........... 53
UND-Schaltung ............................... 65
Unicode ..................................... 63, 64
V
Verarbeitung ...................................... 4
virtuelle Maschine ........................... 48
Volladdierer ..................................... 72
Von Neumann, John ......................... 42
Von Neumann-Architektur ......... 42, 45
W
Wiederholung ............................ 14, 21
Windows ......................................... 47
Windows 10 Mobile ......................... 48
Windows Phone ............................... 48
X
XOR ................................................ 69
Z
Zeitkomplexität ............................... 31
Zustand einer Turing-Maschine ....... 38
Zweierkomplement .......................... 59
Zweiersystem .................................. 51
94 GDI01
© HfB, 02.12.20, Berk, Eric (904709)
F. Einsendeaufgabe Typ A
Einführung in die Informatik Einsendeaufgabencode:
GDI01-XX2-K09
1. Schreiben Sie einen Algorithmus in Form eines Struktogramms, der eine beliebige
Anzahl quadratischer Gleichungen der Art x2 px q 0 löst, wobei mindestens
eine Gleichung gelöst werden soll. Der Benutzer soll bei jeder Gleichung die Koeffi-
zienten p und q eingeben, woraufhin der Algorithmus die jeweilige Anzahl der Lö-
sungen sowie die Lösungen selbst bestimmt und ausgibt. Dabei sind die verschiede-
p p2
nen Fälle zu beachten, die bei Anwendung der Lösungsformel x 1,2 q
2 4
auftreten können. Zur Bezeichnung der Quadratwurzel a aus einer Zahl kann
sqrt(a) verwendet werden. Nach jeder gelösten Gleichung soll der Benutzer gefragt
werden, ob er eine weitere Gleichung lösen lassen möchte. Je nach Antwort geht der
Algorithmus dann zur nächsten Gleichung über oder wird beendet.
6 Pkt.
2. a) Welches Problem löst der Algorithmus „Suche“? Erläutern Sie das Verfahren, mit
dem er dieses Problem löst.
Abb. F.1:
GDI01 95
© HfB, 02.12.20, Berk, Eric (904709)
F Einsendeaufgabe Typ A
b) Bestimmen Sie die Komplexität des Algorithmus und beschreiben Sie sie mithilfe
eines Landau-Symbols.
6 Pkt.
3. Konstruieren Sie eine Turing-Maschine, die eine auf dem Band gespeicherte Dual-
zahl in ihr Zweierkomplement verwandelt. Gehen Sie dabei davon aus, dass der Le-
se- und Schreibkopf zu Beginn auf der vordersten, also der am weitesten links ste-
henden Stelle der Dualzahl steht.
6 Pkt.
4. Führen Sie die folgenden Operationen durch:
a) Berechnen Sie 110110112 101010012.
b) Berechnen Sie 110101102 – 11001112 mithilfe des Zweierkomplements.
c) Rechnen Sie die Dezimalzahl 1756 in eine Hexadezimalzahl um.
6 Pkt.
5. Setzen Sie die folgende Tabelle mit den drei Inputs x,y,z und dem Output r in ein
Schaltbild um.
Tab. F.1:
x y z r
0 0 0 1
0 0 1 0
0 1 0 1
0 1 1 1
1 0 0 1
1 0 1 1
1 1 0 1
1 1 1 0
6 Pkt.
96 GDI01
Studienheft
INM11
$
INM11
Werden Personenbezeichnungen aus Gründen der besseren Lesbarkeit nur in der männlichen oder
weiblichen Form verwendet, so schließt dies das jeweils andere Geschlecht mit ein.
Falls wir in unseren Studienheften auf Seiten im Internet verweisen, haben wir diese nach sorgfältigen
Erwägungen ausgewählt. Auf die zukünftige Gestaltung und den Inhalt der Seiten haben wir jedoch
keinen Einfluss. Wir distanzieren uns daher ausdrücklich von diesen Seiten, soweit darin rechtswid-
rige, insbesondere jugendgefährdende oder verfassungsfeindliche Inhalte zutage treten sollten.
© HfB, 02.12.20, Berk, Eric (904709)
Teil 1
Inhaltsverzeichnis
0419K03
Vorwort ....................................................................................................................... 1
1 Einführung ................................................................................................................ 3
2 Algorithmen .............................................................................................................. 11
2.1 Beispiele ....................................................................................................... 11
2.2 Begriff Algorithmus .................................................................................... 16
Zusammenfassung .................................................................................................... 18
Aufgaben zur Selbstüberprüfung ............................................................................ 19
INM11
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
7 Programmiersprache C ........................................................................................... 70
7.1 Aufbau eines C-Programms ....................................................................... 70
7.2 Syntaxregeln von C ..................................................................................... 73
7.2.1 Grundsymbole ............................................................................................. 73
7.2.2 Syntaxdiagramme zu C ............................................................................... 76
7.2.3 Kontextsensitive Nebenbedingungen ........................................................ 81
7.3 Modulkonzept von C – Funktionen in C ................................................... 82
7.3.1 Definition von Funktionen ......................................................................... 83
7.3.2 Aufruf von Funktionen ............................................................................... 86
Zusammenfassung ..................................................................................................... 91
Aufgaben zur Selbstüberprüfung ............................................................................. 91
Anhang
A. Lösungen der Aufgaben zur Selbstüberprüfung ....................................... 93
B. Literaturverzeichnis ..................................................................................... 103
C. Grundstrukturen von Algorithmen ........................................................... 104
D. Syntaxdiagramme der Programmiersprache C ......................................... 106
E. Abbildungsverzeichnis ................................................................................ 112
F. Tabellenverzeichnis ..................................................................................... 114
G. Sachwortverzeichnis .................................................................................... 115
H. Einsendeaufgabe Typ A .............................................................................. 119
INM11
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
INM11Grundlagen der
Algorithmierung und der Programmierung Teil 10419K03
INM11 1
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
2 INM11
© HfB, 02.12.20, Berk, Eric (904709)
1 Einführung
Das Bestreben der Menschen, sich körperliche Arbeit durch den Einsatz von Werkzeu-
gen und Maschinen zu erleichtern bzw. einzelne Arbeitsschritte durch Automaten, in ei-
gener Regie, scheinbar selbstständig ausführen zu lassen, hat in der Entwicklungsge-
schichte der Menschen immer eine wichtige Rolle gespielt. Für wiederkehrende/
routinemäßige geistige Tätigkeiten schuf sich der Mensch Hilfsmittel. Es waren anfangs
Rechenbretter, dann mechanische Rechenmaschinen und letztlich elektronische, digitale
Rechentechnik für schnellere und fehlerfreie Berechnungen auf fast allen Gebieten. Die
technische Entwicklung in der Mitte des 20. Jahrhunderts beschleunigte diese Entwick-
lung. Aus dem Rechner wurde der Computer, der als Personal Computer unmittelbar am
Arbeitsplatz Einzug gehalten hat. Die digitale Informationsverarbeitung entstand und
ist heute fester Bestandteil gesellschaftlicher Prozesse. In Ihrem Arbeitsumfeld finden
Sie sicher viele Beispiele für die computergestützte Informationsverarbeitung.
Informationsverarbeitung durch elektronische Systeme lässt sich ganz allgemein durch
drei grundlegende Teilsysteme und den Informationsfluss zwischen ihnen darstellen.
Eingabe:
Informationen, die zur Verarbeitung benötigt werden, Instruktionen für das verarbeiten-
de System und die zu verarbeitenden Informationen werden durch das Teilsystem „Ein-
gabe“ bereitgestellt.
Es ist die Schnittstelle zwischen Mensch und technischem System. Technische Varianten
sind Maus, Scanner, Tastatur, Touchscreen, externe Speicher (CD, DVD, USB-Stick usw.)
sowie Datenfernübertragungseinheiten.
Verarbeitung:
Ausgabe:
INM11 3
© HfB, 02.12.20, Berk, Eric (904709)
1 Einführung
Daten. Die Gemeinsamkeiten der rechnerinternen Darstellung von Programm- und Ver-
arbeitungsdaten, es sind codierte Informationen, sowie die Unterschiede in der Nutzung
sind dabei aber stets zu bedenken.
Der Computer benötigt ein Programm und Daten als Eingabe und liefert nach der Ver-
arbeitung Daten als Ausgabe.
Programme sind abstrakt gesehen nach dem EVA-Prinzip aufgebaut.
Die Nutzer moderner Software auf Computern mit modernen Betriebssystemen werden
heute durch benutzerfreundliche Bedienoberflächen in der Nutzung unterstützt. Die
„Fenster“ verfügen über Menüleisten, Werkzeugleisten, Bildlaufleisten, Arbeitsflächen,
Eingabefelder usw. Einem Benutzer dieser Software wird das Grundprinzip der Daten-
verarbeitung dadurch nicht direkt offensichtlich.
Übernehmen Sie jetzt unter unserer Anleitung die Rolle des Programmierers. An einem
kleinen Beispiel soll deutlich werden, wie das EVA-Prinzip zum Leitfaden beim Erstellen
einer Software, eines C-Programms, wird. Andererseits wollen wir Ihnen Mut machen
und zeigen, dass Sie nach dem Studium der Studienhefte INM11 und INM12 die Grund-
lagen des Programmierens beherrschen und kleine Programme selbst erstellen können.
Übung 1.1:
Das Programm „Sparjahre“ dient uns als Beispiel. Dieses Programm soll folgende
Aufgabe korrekt lösen.
„Ein Sparer möchte ein bestimmtes Startkapital nach einer Anzahl von Jahren durch
die Zinsen, die pro Jahr gezahlt werden, verdoppelt haben.“
Das Programm „Sparjahre“ benötigt folgende Eingaben: das Startkapital und den aktu-
ellen Zinssatz.
Die Ausgabe, das Ergebnis des Programms, ist eine Zahl. Sie entspricht der Anzahl der
Sparjahre, die vergehen, bis das Guthaben das Zweifache des Anfangskapitals, das End-
kapital, beträgt.
Zur Verarbeitung:
Sicher fällt Ihnen sofort ein, dass es in der Mathematik dazu eine Berechnungsvorschrift,
die Zinseszinsformel, gibt
n
p
K n K 0 1 ,
100
mit Kn … Endkapital, K0 … Startkapital, p/100 ( z. B. 0,05 5 %) Zinssatz und n… An-
zahl der Jahre. Diese Formel hilft uns, die Berechnung zu verstehen, aber sie ist als algo-
rithmische Lösung der Aufgabe zunächst nicht verwendbar. Der gesuchte Algorithmus,
der die Grundlage des späteren Entwurfs von Software ist, soll nur Grundoperationen
als Aktionen enthalten, das Potenzieren bzw. das Wurzelziehen gehören nicht dazu.
Noch einmal zur Zinseszinsrechnung: Das Guthaben erhöht sich pro Jahr um die Zinsen
auf das aktuelle Guthaben. Im darauffolgenden Jahr wird das um die Zinsen erhöhte
Guthaben verzinst usw. Das geschieht jedes Jahr, und nach einer bestimmten Anzahl
4 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Einführung 1
von Jahren beträgt der Wert des Guthabens das Doppelte des Startkapitals, d. h. mindes-
tens 2 · Startkapital. Diese Rechnung ist eine geeignete Grundlage des Algorithmus, der
im C-Programm umgesetzt wird.
Nun wird in vier typischen Schritten die Entstehung des Programms ausführlich darge-
stellt.
INM11 5
© HfB, 02.12.20, Berk, Eric (904709)
1 Einführung
Die Rechnung, die hier mehrmals wiederholt wird, entspricht der Formel:
guthaben : guthaben guthaben · zins
Im Unterschied zur Mathematik wird die Rechnung im Struktogramm nicht durch eine
Gleichung, sondern durch eine Zuweisung beschrieben. Dabei wird das sogenannte Er-
gibtzeichen „ : “ benutzt. Auf der rechten Seite der Zuweisung erfolgt eine Berechnung.
Der alte Wert der Variablen guthaben wird zur Berechnung der Zinsen verwendet. Der
durch die Zinsen erhöhte Wert, Berechnung durch die Addition, wird der auf der linken
Seite stehenden Variablen guthaben zugewiesen. Im nächsten Berechnungsschritt wird
mit diesem neuen Wert gerechnet. So wird die Zinseszinsrechnung korrekt umgesetzt.
6 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Einführung 1
Programmierung
Die Korrektheit des Algorithmus „Sparjahre“ wurde durch den Trockentest überprüft.
Das Protokoll der Veränderung der Daten entspricht im Programm der Veränderung des
Inhalts der Speicherplätze, der jeweiligen Variablen. Nun kann dieser Algorithmus mit-
hilfe der Programmiersprache C in eine Folge von Anweisungen umgesetzt werden.
INM11 7
© HfB, 02.12.20, Berk, Eric (904709)
1 Einführung
In den Zeilen (4) und (5) werden die Variablen deklariert, Datentyp und Name werden
festgelegt, man spricht von Variablendeklaration. Während die Sprache C++ weitere
Möglichkeiten für eine Variablendeklaration vorsieht, hat diese in C vollständig vor der
ersten ausführbaren Anweisung zu erfolgen.
Mit scanf(…) und printf(…) stehen die am häufigsten genutzten Ein- und Ausgabe-
Funktionen zur Verfügung.
In Zeile (7) wird mit der Anweisung printf(…) der Text ‚Berechnung …‘ ausgegeben.
In den Zeilen (8) und (9) wird über den Text eine Aufforderung auf dem Bildschirm aus-
gegeben und der Cursor mit Abstand zum Text (Tabulator "\t") auf dem Bildschirm po-
sitioniert.
Die Ausgabe von Leerzeilen, in den Zeilen (6), (13) und (21), erfolgt durch die Anwei-
sung printf("\n") (die Zeichenfolge "\n" entspricht new line, neue Zeile). Die Leer-
zeilen sind nur zur Gestaltung des Textes eingefügt worden.
Die Anweisung scanf(…) realisiert die Eingabe einer Zahl.
Der Nutzer des Programms muss die Eingabe mit der Taste (Enter) abschließen.
Im Ablauf folgt die Anweisung Zeile (10), die Ausgabe mit der Aufforderung zur Ein-
gabe der nächsten Zahl und mit Zeile (11) wird diese Eingabe entgegengenommen.
In Zeile (12) erfolgt eine Berechnung und das Ergebnis wird der Variablen end zuge-
wiesen.
In den Zeilen (14) und (15) erhalten die Variablen durch Zuweisung den bereits festge-
legten Startwert.
Mit der Anweisung while(…) (wiederhole solange eine Bedingung , <bed>, gilt), Zeile
(16) bis Zeile (19), wird die wiederholte Ausführung der zwei Anweisungen, Zeilen (17)
und (18), die zu einem Block durch {}-Klammern zusammengefasst sind, gesteuert. Die-
se Anweisungen berechnen die Variablen jahre und guthaben.
Sie werden immer wieder ausgeführt, nämlich solange die Bedingung guthaben end
gilt. Wenn das Guthaben wertmäßig größer oder gleich dem gewünschten Endkapital
ist, wird die Wiederholung der Berechnung beendet.
Zeile (20) realisiert die Ausgabe des Ergebnisses, der aktuelle Wert der Variablen jahre.
In Zeile (22) ist das Ende der Funktion main() erreicht, mit dem Rückgabewert 0 wird
die fehlerfreie Ausführung angezeigt.
Bevor dieser Quelltext Code 1.1: „Sparjahre.c“ – vom Computer in einem bestimmten
Betriebssystem ausgeführt werden kann, muss er in einen maschinenlesbaren, ausführ-
baren Code übersetzt werden. Das dazu notwendige Übersetzerprogramm ist in eine
Entwicklungsumgebung integriert oder es liegt als Programm separat vor.
In der empfohlenen Programmierumgebung DEV-C++ erfolgt das Übersetzen, das Kom-
pilieren, durch einen Klick auf den Menüpunkt Kompilieren im Menü „Ausführen“
(Abb. 1.3) oder mit Taste (F9). Der Kompiler ist Bestandteil der Programmierumge-
bung.
8 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Einführung 1
INM11 9
© HfB, 02.12.20, Berk, Eric (904709)
1 Einführung
Das vorliegende Programm entspricht der Aufgabenstellung. Für die korrekte Ausfüh-
rung ist jedoch die korrekte Eingabe von zwei reellen Zahlen Voraussetzung. Bei der
Eingabe der gebrochenen Zahl muss der Dezimalpunkt verwendet werden. Kommt zu-
fällig ein anderes Zeichen in der Eingabe vor, so „fährt sich das Programm fest“, es re-
agiert nicht mehr. Das Fenster muss geschlossen und damit das Programm durch das Be-
triebssystem beendet werden. Unser Programm ist also nicht robust gegen falsche
Eingaben. Andererseits ist es auch nicht benutzerfreundlich. Nur eine Rechnung ist
möglich, das Programm bietet keine Wiederholung der Berechnung an. Trotzdem wird
hier die Arbeit an diesem Beispiel beendet. Es gibt zahlreiche Varianten zur Verbesse-
rung dieses Programms, später kommen wir darauf zurück.
10 INM11
© HfB, 02.12.20, Berk, Eric (904709)
2 Algorithmen
Das Wort Algorithmus ist abgeleitet vom Namen des arabischen Gelehrten Al
Chwarizmi, der um 820 lebte. Es wurde ihm zu Ehren ins Lateinische für Rechen-
verfahren übernommen. Die Rechenverfahren, Algorithmen der Mathematik,
waren und sind eine wichtige Grundlage der Entwicklung moderner leistungsfä-
higer Software. In diesem Kapitel wird ein Begriff Algorithmus erarbeitet, der als
Grundlage für die Programmierung, das Entwerfen und Schreiben von Program-
men, gut geeignet ist.
Niklas Wirth, ein berühmter Schweizer Informatiker, hat in einem grundlegen-
den Fachbuch (1975) folgende Formel aufgestellt:
Programm = Algorithmus + Daten
D. h., ohne Algorithmus kann man kein Programm schreiben. Andererseits ist der
enge Zusammenhang von Algorithmus und Daten immer zu beachten, wenn ein
schnell und korrekt laufendes Programm entstehen soll. Die Begriffe Algorithmus
und Daten sind deshalb von zentraler Bedeutung in der Informatik. Sie werden
in den Kapiteln 1 und 2 dieses Studienhefts erst getrennt betrachtet und danach
in ihrem Zusammenhang erläutert.
Ein Algorithmus ist eine Vorschrift zur Lösung einer Menge von gleichartigen Pro-
blemen, d. h. eine Vorschrift zur Lösung einer bestimmten Art von Aufgaben.
Er besteht aus einer endlichen Folge von Schritten, mit der aus bekannten Eingangs-
daten neue Ausgangsdaten eindeutig berechnet werden.
2.1 Beispiele
Der Zusammenhang zur Mathematik ist offensichtlich, deshalb folgen zwei Beispiele
aus diesem Bereich.
INM11 11
© HfB, 02.12.20, Berk, Eric (904709)
2 Algorithmen
Die Mathematik beschreibt das Rechenverfahren, den Algorithmus, kurz durch fol-
gende Formel:
X 1 X 2 X 1 Y 2 X 2 Y 1
Y1 Y 2 Y 1 Y 2
Mit Worten werden die Schritte der Berechnung als Schrittfolge beschrieben:
Die Lösung dieser Aufgabe „Addition von zwei Brüchen“, wobei Zähler und Nenner na-
türliche Zahlen sind, kann durch eine mathematische Formel oder mit Worten unter
Verwendung von Variablen und Operationen als Schrittfolge beschrieben werden. Be-
achten Sie: Die Reihenfolge der Berechnungsschritte ist hier nicht fest vorgeschrieben,
sie ist nicht zwingend.
Folgende Aktionen sind Bestandteil der Schrittfolge: Die Zuweisung von Werten an
Variable, die zweistelligen Operationen Addition und Multiplikation.
X
Beispiel 2.2: Kürzen eines Bruchs
Y
Der Zähler X und der Nenner Y sind natürliche Zahlen, X, Y ℕ.
Der folgende Bruch, das Ergebnis der konkreten Aufgabe aus Beispiel 2.1, soll ge-
kürzt werden.
4 2 36 12 48
6 9 54 54
Aus der Mathematik ist die folgende Vorschrift bekannt.
1. Ermittle den größten gemeinsamen Teiler von Zähler und Nenner.
2. Der Zähler des gekürzten Bruchs wird berechnet, indem der Zähler des zu kür-
zenden Bruchs durch den größten gemeinsamen Teiler dividiert wird
3. Der Nenner des gekürzten Bruchs wird berechnet, indem der Nenner des zu kür-
zenden Bruchs durch den größten gemeinsamen Teiler dividiert wird.
• Zu 1.: Wie man leicht sieht, sind die Zahlen 2, 3 und 6 Teiler von 48 und 54. Der
größte gemeinsame Teiler von 48 und 54 ist somit 6.
48
• Zu 2.: Rechne und das Ergebnis ist 8.
6
54
• Zu 3.: Rechne und das Ergebnis ist 9.
6
12 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Algorithmen 2
8
• Das Ergebnis lautet .
9
Nun die Verallgemeinerung:
Die Schreibweise der Mathematik ist hier hilfreich. Die Funktion ggT(X, Y ) erfordert
zwei Eingaben, X und Y, sie liefert als Ergebnis (als Ausgabe oder als Rückgabewert der
Funktion) den Wert des größten gemeinsamen Teilers von X und Y.
Welche mathematischen Vorüberlegungen sind die Grundlage der folgenden Schrittfol-
ge zur Berechnung von ggT(X, Y )?
• Es gilt X, Y ℕ wobei X, Y 0.
• Wenn X Y, dann ist der ggT(X, Y ) X Y, denn eine Zahl ist größter gemeinsa-
mer Teiler von sich selbst.
• Wenn X Y und T 0 irgendein gemeinsamer Teiler von X und Y ist, dann gilt auch:
X a · T und Y b · T mit a 0 und b 0.
Daraus lässt sich die folgende Gleichung durch Einsetzen aufstellen:
X – Y a · T – b · T (a – b) · T
Das bedeutet, der Teiler T 0 ist auch Teiler der Differenz X – Y mit X, Y ℕ.
• Wenn X Y und T 0 irgendein gemeinsamer Teiler von X und Y ist, dann gilt auch:
X a · T und Y b · T mit a 0 und b 0
Daraus lässt sich folgende Gleichung durch Einsetzen aufstellen:
Y – X b · T – a · T (b – a) · T.
Das bedeutet, der Teiler T 0 ist auch Teiler der Differenz Y – X mit X, Y ℕ.
Beispiel 2.3:
Der Algorithmus für die Berechnung des ggT(X, Y ) kann nun folgendermaßen be-
schrieben werden:
Schrittfolge zu ggT(X, Y)
1. Den Variablen X und Y werden Werte zugewiesen.
2. Wiederhole so lange X Y ist, die folgende Aktion:
2.1. Wenn X Y,
2.2. dann erhält X den Wert von X – Y, X : X – Y,
INM11 13
© HfB, 02.12.20, Berk, Eric (904709)
2 Algorithmen
Tab. 2.1 zeigt die schrittweise Ausführung des Algorithmus für die zwei Eingaben
X 48 und Y 54. Die Ausführung der einzelnen Schritte und die Veränderung der
Werte der Variablen X und Y ist in den aufeinanderfolgenden Zeilen protokolliert.
Schritt Aktion X Y
1. Zuweisungen 48 54
2. Test: Ist 48 54? Ja
2.1 Ist 48 54? Nein
2.3 48 54 – 48 6
2. Ist 48 6? Ja
2.1 Ist 48 6? Ja
2.2 48 – 6 42 6
2. Ist 42 6? Ja
2.1 Ist 42 6? Ja
2.2 42 – 6 36 6
2. Ist 36 6? Ja
2.1 Ist 46 6? Ja
2.2 36 – 6 30 6
Mehrmals 2., 2.1 und 2.2
2. Ist 12 6? Ja 12 6
2.1 Ist 12 6? Ja
2.2 12 – 6 6 6
2. Ist 6 6? Nein 6 6
3. Der ggT(48, 54) (siehe letzter Wert von X ).
Mit dem Wert ggT(X, Y ) wird nun in der Schrittfolge weiter gerechnet.
X
X : , der neue Zähler.
ggT( X ,Y )
Y
Y : , der neue Nenner.
ggT( X ,Y )
14 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Algorithmen 2
X
Fazit: Das Kürzen eines Bruches erfordert als ersten Schritt die Ausführung eines
Y
Teilalgorithmus, der größte gemeinsame Teiler zweier natürlicher Zahlen muss ermittelt
werden. Danach erst kann die Berechnung, wie vorgegeben, fortgesetzt werden. Diese
Schrittfolge ist zwingend.
Folgende Aktionen sind Bestandteil der Schrittfolge: Die Zuweisung von Werten an
Variable, die zweistelligen Operationen Subtraktion und Division und Tests, Größenver-
gleiche von Zahlenwerten. Diese Tests können zu alternativen Schrittfolgen führen oder
zur Wiederholung einer Schrittfolge.
Die Entwicklung der elektronischen Rechentechnik führte zu vielen neuen Anwendun-
gen in verschiedenen Bereichen des gesellschaftlichen Lebens. Es entstanden daraus
vielfältige neue Aufgabenstellungen, und es wurden und werden neue Algorithmen be-
nötigt. Insbesondere der Bedarf an Software zur Verarbeitung von massenhaft anfallen-
den Daten entwickelte sich rasant. Das Speichern, das Verwalten und das Auswerten
von Daten sind heute wichtige Aufgabenstellungen. Der Fundus der mathematischen
Algorithmen ist auch für diese Aufgaben eine wichtige Grundlage.
Beispiel 2.4:
Das Alter des jüngsten Mitglieds des Kegelvereins „Alle Neune“ soll ermittelt wer-
den. Beachten Sie folgende Vereinfachung: Möglicherweise sind mehrere Personen
im gleichen Jahr geboren, ihr Alter ist gleich. Das Ergebnis ist dann trotzdem nur
eine Altersangabe.
Für die Lösung der Aufgabe benötigt man einen Datenbestand. Das könnte eine Liste
in Tabellenform oder ein Stapel Karteikarten mit allen zur Vereinsverwaltung benö-
tigten Daten, Informationen zu allen Mitgliedern des Vereins, sein.
Die Daten zu den Mitgliedern liegen in unserem Beispiel als Liste in Tabellenform
vor. Eine Zeile der Tabelle enthält alle Angaben zu je einem Mitglied, dabei ist hier
nur das Geburtsjahr von Interesse.
Zur Beschreibung des Algorithmus werden die zwei Variablen jahr und junior benö-
tigt.
INM11 15
© HfB, 02.12.20, Berk, Eric (904709)
2 Algorithmen
4. Berechnen Sie das Alter des jüngsten Mitglieds, nutzen Sie junior (Geburtsjahr)
und die aktuelle Jahreszahl (alter : aktJahr – junior).
Folgende Aktionen sind Bestandteil der Schrittfolge: Die Zuweisung von Werten an
Variable, die zweistellige Operation Subtraktion, ein Test (ein Größenvergleich) der zu
alternativen Aktionen führt, und ein Test, der zur Wiederholung einer Folge von Aktio-
nen führt oder zum Beenden.
Es gibt verschiedene Lösungswege und damit auch verschiedene Algorithmen zur Lö-
sung dieser Aufgabe. Im Kern ist sie eine konkrete Anwendung einer Standardaufgabe
der Informationsverarbeitung/Datenverarbeitung, der Suche nach dem Maximum, der
größten Zahl, einer endlichen Menge von Zahlen. In verschiedenen Anwendungsgebie-
ten der Wissenschaft, der Wirtschaft oder der Verwaltung gibt es Aufgabenstellungen,
die sich formal gleichen. Solche immer wieder zu lösenden Aufgabenstellungen sind z. B.
das Suchen bestimmter Angaben in einem Datenbestand oder das Sortieren der Daten
eines Datenbestands. Algorithmen für formal gleiche Aufgabenstellungen sind Stan-
dardalgorithmen. Für das Programmieren ist es notwendig, diese zu kennen und beur-
teilen zu können.
Satz 2.1:
Ein Algorithmus ist eine Vorschrift, die angibt, wie durch die Ausführung bestimm-
ter elementarer Operationen aus Eingabedaten Ausgabedaten ermittelt werden.
Die Ausführbarkeit des Algorithmus durch eine Maschine, durch den Rechner bzw. das
elektronische System, steht bei dieser Sicht im Vordergrund.
Die folgende Definition bezieht sich auf wesentliche Eigenschaften einer Vorschrift,
durch die sie zum Algorithmus wird.
Definition 2.1:
Ein Algorithmus ist eine in Beschreibung und Ausführung allgemeine, eindeutige,
endliche, determinierte und effektive Vorschrift zur effizienten Lösung eines Prob-
lems.
Allgemeinheit
Ein Algorithmus darf nicht nur die Lösung einer speziellen Aufgabe sein, sondern er
muss die Lösung einer Klasse von Problemen beschreiben.
16 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Algorithmen 2
Endlichkeit
Ein Algorithmus muss aus einer endlichen Anzahl von Schritten bestehen. Durch die
Abarbeitung dieser endlich vielen Schritte muss er nach endlich langer Zeit die Abar-
beitung beenden.
Eindeutigkeit
Die einzelnen Schritte eines Algorithmus und ihre Aufeinanderfolge müssen eindeutig
beschrieben sein.
Determiniertheit
Effektivität
Effizienz
Ein Algorithmus muss möglichst wenig Speicher und eine möglichst geringe Rechenzeit
erfordern.
In der Informatik werden Algorithmen mit diesen Merkmalen prozedurale Algorith-
men genannt. Die Ausführung dieser Algorithmen beruht auf einer Folge von elemen-
taren Aktionen, die Zustandsänderungen im technischen System (z. B. Strom fließt/
fließt nicht, Ventil offen/Ventil zu) bewirken. Die Zustände sind dabei durch die Werte
von Variablen beschreibbar. Die elementaren Aktionen bewirken die Änderung dieser
Werte.
Mit dem E-V-A-Prinzip beschreibt man die Ausführung:
• Die Eingabe bewirkt die Übernahme von Daten in eine Variable.
• Die Verarbeitung bewirkt eine Wertzuweisung, die Änderung des Wertes einer Va-
riablen aufgrund einer Rechnung bzw. Zwischenrechnung.
• Die Ausgabe bewirkt die Übergabe von Daten, des Wertes von Variablen, an ein Aus-
gabegerät.
Prozedurale Algorithmen sind eine gute Grundlage für das Schreiben von Programmen/
Software.
Welche Arten von Algorithmen kann man noch betrachten?
Wird die Endlichkeit als Merkmal für Algorithmen aufgegeben, dann erhält man nicht-
terminierende, nicht-endende, Algorithmen. Beispielsweise Steuerungsalgorithmen,
die die Grundlage von Steuerungsprozessen sind, sind nicht-endende Prozesse.
Wird das Merkmal der Determiniertheit aufgegeben, dann erhält man nicht-determi-
nierte Algorithmen. Das bedeutet, dass in der Vorschrift zur Lösung einer Aufgabe auf
mindestens eine Aktion zwei verschiedene Aktionen folgen können. Soll z. B. eine Ver-
INM11 17
© HfB, 02.12.20, Berk, Eric (904709)
2 Algorithmen
bindung in einem Straßennetz von Punkt A nach Punkt B ermittelt werden, sind unter-
schiedliche Lösungen möglich. Erfolgt die Auswahl alternativer Aktionen in einem Al-
gorithmus durch Zufall, so erhält man stochastische Algorithmen.
Es können noch viele weiterführende Fragen zu Algorithmen Gegenstand des Interesses
sein, z. B.:
Kann jedes Problem durch einen Algorithmus beschrieben werden? Diese Frage bezieht
sich auf die Frage der Berechenbarkeit von Problemen.
Kann zu jedem Algorithmus ein Programm geschrieben werden? Diese Frage führt zur
Betrachtung von Programmiersprachen und den Anforderungen an sie.
Ist ein Computer grundsätzlich in der Lage, einen Algorithmus, der als Programm vor-
liegt, abzuarbeiten? Die Komplexität von Algorithmen bzw. Programmen muss dazu be-
trachtet werden. Diese und weitere Fragen sind Gegenstand der Theoretischen Informa-
tik.
Zusammenfassung
In diesem Kapitel wurde der Begriff Algorithmus über seine Merkmale definiert und be-
schrieben. Jedes Computerprogramm ist als konkrete Umsetzung eines formal und
sprachunabhängig definierten Algorithmus aufzufassen. Das weitere Vorgehen wird
durch die Betrachtung prozeduraler Algorithmen bestimmt. Diese sind dadurch gekenn-
zeichnet, dass die Problemlösung auf eine Folge elementarer Aktionen, die durch Zu-
standsänderungen charakterisiert sind, zurückgeführt wird.
Das E-V-A-Prinzip ist Grundlage der Informationsverarbeitung mit einem technischen
System, dem Computer. Die Eingabe von Werten über die Tastatur (E) sowie die Ausga-
be von Werten bzw. von Text in einem Anwendungsfenster (A) sind die beiden Dialog-
möglichkeiten mit dem Benutzer des Programms.
Die Verarbeitung von Daten (V) erfolgt durch nur zwei grundlegenden Aktionen:
die Wertzuweisung und die Wertänderung durch Berechnungen. Die Berechnungen
werden mit den mathematischen Grundoperationen Addition, Subtraktion, Multiplika-
tion und Division umgesetzt. Durch Größenvergleiche (zweistellige Operationen) wird
eine Verzweigung, eine alternative Abfolge von Aktionen oder eine wiederholte Abfolge
einer festgelegten Folge von Aktionen möglich.
Bevor Daten und ihr Zusammenhang mit den Algorithmen im nächsten Kapitel näher
betrachtet werden, sollten Sie anhand der folgenden Aufgaben prüfen, ob Sie mit eige-
nen Worten Schrittfolgen zur Lösung einer Aufgabe, also Algorithmen, analog den dar-
gestellten Beispielen beschreiben können.
18 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Algorithmen 2
2.1 Suchen Sie in der Fachliteratur oder im Internet nach einem effizienteren als den
im Beispiel 2.2 beschriebenen Algorithmus für die Berechnung des größten ge-
meinsamen Teilers. Beschreiben Sie ihn als Schrittfolge in eigenen Worten.
Hinweis: Unter „Euklidischer Algorithmus“ wird man fündig.
2.2 Formulieren Sie in eigenen Worten einen Algorithmus zur Ermittlung des Alters
des ältesten Mitglieds des „Kunst- und Kulturvereins R. Schumann“.
Gehen Sie analog dem Vorgehen in Beispiel 2.4 in Abschnitt 2.1 vor. Betrachten
Sie auch nur das Geburtsjahr.
2.3 Formulieren Sie einen Algorithmus, mit dessen Hilfe von einer Jahreszahl A ent-
schieden werden kann, ob es sich bei dem Jahr A um ein Schaltjahr handelt oder
nicht.
Sie können davon ausgehen, dass 1581 A 2100 gilt, der gregorianische Kalen-
der gilt seit dem Jahr 1582.
INM11 19
© HfB, 02.12.20, Berk, Eric (904709)
Anmerkung:
Die Information über die Körpergröße eines Menschen ist eine Zahlenangabe, ein
Vielfaches einer bestimmten Maßeinheit, z. B. 176 cm. Die Angabe 176 cm könnte
aber auch die Information über die Breite eines Möbelstückes sein.
Informationen zur Temperatur in einem Raum über eine längere Zeit werden durch
eine Folge von Messwerten, eine Messreihe aufgezeichnet. Auch hier werden Zah-
lenwerte einer Maßeinheit zur Darstellung verwendet.
Informationen über die Mitglieder eines Vereins werden durch Zeichenfolgen,
Name, Vorname, Adresse, Geburtsdatum usw., in elektronischer Form aufbereitet
und gespeichert.
Zieht man den Duden zurate, dann sind Daten bzw. Datenobjekte
• durch Beobachtung, Messung oder statistische Erhebung u. a. gewonnene Zahlen-
werte, Angaben oder Befunde;
• zur Lösung einer Aufgabe vorgegebene Werte oder Größen;
• elektronisch gespeicherte Zeichenfolgen.
Definition 3.1:
Daten sind jene Informationen bzw. deren abstrakte Repräsentationen, die von pro-
grammgesteuerten elektronischen digitalen Verarbeitungssystemen zur Verarbei-
tung benötigt bzw. von diesen verarbeitet werden.
20 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Daten können von elektronischen digitalen Systemen nur dann gespeichert und verar-
beitet werden, wenn sie entsprechend digitalisiert wurden. Da auf die rechnerinterne
Darstellung von Daten in diesem Studienmaterial nicht eingegangen wird, sei auf die
Darstellung als Folgen von Bits, die Dualziffern 0 und 1, sowie die entsprechende Co-
dierung nur verwiesen.
Ein Datenobjekt hat einen bestimmten Typ, es repräsentiert Information durch einen
konstanten oder variablen Wert aus einem Wertebereich. Innerhalb dieses Wertebe-
reichs sind bestimmte Operationen definiert, d. h., nur diese Operationen sind auf das
Datenobjekt anwendbar.
Definition 3.2:
Ein Datentyp beschreibt eine Menge von zulässigen Werten und eine Menge von
Operationen.
Die folgende Klassifikation der Datentypen zeigt die Vielfalt der in der Informations-
verarbeitung vorkommenden Datentypen:
Datentypen (DT)
Gleitkomma-
ordinale DT statische DT dynamische DT
zahlen
ganze Wahrheits-
Zeichen Aufzählung Zeiger
Zahlen werte
Die erste Ebene dieser Klassifikation betrachtet die Datentypen unter dem Aspekt der
Struktur, man unterscheidet Daten einfacher Struktur und komplexer Struktur:
Einfache Datentypen sind Einzeldaten, ein Datenobjekt von einem bestimmten Typ
mit elementarer Struktur. Diese einfachen Datentypen sind in vielen Programmierspra-
chen vordefiniert und werden häufig Standarddatentypen genannt.
Strukturierte Datentypen sind jeweils mehrere Datenobjekte, die in bestimmter Wei-
se ‚angeordnet‘ sind, sie bilden eine Struktur. In Algorithmen kann diese Struktur
zweckmäßig genutzt werden oder auch hinderlich sein.
INM11 21
© HfB, 02.12.20, Berk, Eric (904709)
Die nächste Ebene der Klassifikation der strukturierten Datentypen betrachtet den
Aspekt der Veränderbarkeit der Struktur des Datentyps. Wenn die Struktur der Daten-
typen durch Algorithmen geändert werden kann, spricht man von strukturierten dyna-
mischen Datentypen. Ist die Struktur der Datentypen festgelegt, dann werden sie
strukturierte statische Datentypen genannt.
Anmerkung:
Die Begriffe Datentyp und Datenstruktur werden in der Fachliteratur nicht ein-
heitlich gebraucht.
In diesem Studienheft soll vom Datentyp eines Datenobjektes gesprochen werden,
wenn dessen Wertebereich und die möglichen Operationen von Interesse sind. Sind
nicht die einzelnen Datenobjekte und deren Datentyp, sondern ihre Anordnung, die
Struktur, von Interesse, dann soll das Wort Datenstruktur benutzt werden.
22 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 23
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.1:
int x, y 16, z; Die Variablen x, y, z sind vom Datentyp int.
x 23; Diese Werte werden den int-Variablen zugewiesen.
y 45;
x ––y; (Wertverringerung) x erhält den Wert 44, den Wert des Vorgängers
von y.
z y; (Werterhöhung) z erhält den Wert 46, den Wert des Nachfolgers
von y
Beachten Sie bitte: In den Beispielen verwenden wir bereits die Schreibweise von
Deklarationen und Zuweisungen der Programmiersprache C.
Beispiel 3.2:
char x, y; → x und y sind Variablen vom Datentyp char, dann sind folgende Zuwei-
sungen möglich:
x 'A';
x 'G';
Der Test x y? ist ebenso möglich. Der Wertevergleich 65 < 71 liefert als Ergebnis
den logischen Wert ,ja‘.
24 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.3:
float x; → Sei x vom Typ float, dann ist die Zuweisung
x –4.25;
aber auch diese Zuweisung ist möglich: x 15;
Die ganzen Zahlen sind Dezimalbrüche mit dem Nenner 1 (15/1) – Datentyp
float.
• Für den Datentyp float sind u.a. folgende Operationen und Funktionen definiert:
Die zweistelligen Operationen:
– Addition(+), Subtraktion(–),
– Multiplikation(∗), die Division (/)
Die rechnerinterne Darstellung bestimmt den Wertebereich und die Rechengenauigkeit,
deshalb muss der Programmierer Eingaben, Zwischenergebnisse und Endergebnis durch
Überschlag prüfen. Zahlreiche Funktionen sind für diesen Datentyp vordefiniert.
Beispiel 3.4:
float x, y; → x und y sind vom Datentyp float, dann sind z. B.
y sin(x) die Berechnung des Sinus definiert,
x fabs(y) die Berechnung des absoluten Betrags.
In den verschiedenen Programmiersprachen gibt es jeweils Zusammenstellungen
der verfügbaren Funktionen.
Abschließend zu den einfachen Datentypen sind in Tab. 3.1 die in der Programmierspra-
che C vordefinierten sogenannten Standarddatentypen zusammengestellt. Die Anga-
ben zu Wertebereich und Genauigkeit sind für einen Programmierer wichtige Informa-
tionen.
INM11 25
© HfB, 02.12.20, Berk, Eric (904709)
• Die Elemente des Feldes e, e1, e2, …, en sind im Speicher linear angeordnet. Die An-
zahl der Elemente wird in der Deklaration einer Variablen oder einer Konstanten
dieses Datentyps festgelegt und ist im Gültigkeitsbereich der Deklaration fest (sta-
tisch) nicht veränderbar.
Beispiel 3.5:
float messung[233];
Mit dieser Deklaration wird der Speicherplatz für 233 Gleitkommazahlen unter
dem Variablennamen messung reserviert.
Die ‚Ähnlichkeit‘ zwischen der Datenstruktur Feld und dem eindimensionalen Vek-
tor in der Mathematik ist offensichtlich. In dieser Datenstruktur kann eine Menge
von beliebig vielen Zahlenwerten, z. B. 233, gespeichert und für die Verarbeitung be-
reitgestellt werden. Die Berechnung der Summe der Zahlenwerte, die Berechnung
eines Durchschnittswertes u. a. Berechnungen sind möglich.
• Der wahlfreie Zugriff auf die Elemente der Datenstruktur ist die grundlegende
Operation dieser Datenstruktur. Durch den Index, der die Position des Elements im
Feld angibt, kann auf ein bestimmtes Feldelement ‚zugegriffen‘ werden. Somit sind
Aktionen wie die Zuweisung oder die Ausgabe auf einzelne Elemente anwendbar.
Beispiel 3.6:
float messung[233];
messung[45] 24.89; Zuweisung an das Element mit dem Index 45.
printf("Der Wert ist %f Grad Celsius.", messung[15]); Ausgabe
des Wertes des Elementes mit Index 15.
26 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.7:
char Vorname[15];
Mit dieser Deklaration wird der Speicherplatz für 15 Zeichen unter dem Variab-
lennamen Vorname reserviert. Eine Folge von Zeichen ist ein Feld.
Beachten Sie bitte: In den Beispielen verwenden wir bereits die Schreibweise von
Deklarationen und Zuweisungen der Programmiersprache C. In C beginnt der Index
bei 0 und endet bei 15 deklarierten Elementen mit dem Index 14.
• Operationen wie das Anfügen von weiteren Elementen, das Löschen von bestimm-
ten Elementen des Feldes und das Sortieren der Elemente des Feldes, die Ermittlung
des Maximums usw. sind nicht vordefiniert.
Die zur Lösung dieser Aufgaben notwendigen Algorithmen gehören zu den Stan-
dardalgorithmen, die auf die konkrete Anwendung angepasst werden müssen.
Beispiel 3.8:
1. struct datum { int tag, monat, jahr; }; Der Datensatz datum hat
drei Komponenten vom Datentyp int.
2. struct datum heute; ist die Deklaration einer Variablen vom Datensatztyp
datum.
Beispiel 3.9:
struct person {
char Name[35], Vorname[15];
int Nummer;
struct datum geboren;
};
Der Name dieser Datenstruktur ist person. Der Datensatz hat vier Komponenten:
• Zwei Komponenten sind jeweils eine Datenstruktur, Feld, Name und Vorname, mit
Elementen vom Datentyp char, wobei die Anzahl der Feldelemente in der Deklara-
tion festgelegt ist.
• Eine Komponente, die Nummer, ist vom Datentyp int.
• Eine Komponente ist ein Datensatz, Datentyp datum.
INM11 27
© HfB, 02.12.20, Berk, Eric (904709)
Durch diese Deklaration kann in einem Datenobjekt, einer Variablen vom Datensatztyp
person, die Information über eine reale Person, deren Familienname, Vorname, eine
Nummer sowie das Geburtsdatum codiert, gespeichert und zur Verarbeitung bereitge-
stellt werden.
Hinweis:
Ein Datenbestand, der von einer festen Anzahl von Personen genau diese Daten bereit-
stellt und speichert, könnte durch die Verwendung einer solchen Datenstruktur, ein Feld
mit Elementen des Datentyps person, also Datensätzen, aufgebaut werden, z. B.
person KegelVerein[54];
Durch diese Deklaration wird Speicherplatz für die Angaben (s. o.) zu 54 Personen im
Speicher reserviert.
• Für die Datenstruktur Datensatz ist der Zugriff auf die Komponenten durch den
Punkt-Operator (.) definiert. Der Name des Datensatzes und der Name der Kompo-
nente des Datensatzes werden als Angaben benötigt.
• <name_Datensatz>.<name_Komponente>.<…>
Beispiel 3.10:
struct datum { int tag, monat, jahr; };
struct datum gebDatum;
Die Variable gebDatum vom Datensatztyp datum erhält, wie auch andere Datenobjekte,
durch Zuweisung einen Wert, z. B. das Datum 12.04.1998.
Die Werte von Tag, Monat und Jahr werden durch den komponentenweisen Zugriff mit-
hilfe des Punkt-Operators zugewiesen:
gebDatum.tag = 12;
gebDatum.monat = 4;
gebDatum.jahr = 1998;
Beispiel 3.11:
struct person {
char Name[35], Vorname[15];
int Nummer;
struct datum geboren;
};
struct person KegelVerein[54];
Die Variable KegelVerein ist eine Datenstruktur Feld, sie hat 54 Elemente vom Daten-
satztyp person.
Der wahlfreie Zugriff auf ein Element des Feldes mithilfe des Index und die Anwendung
des Punkt-Operators ermöglichen den Zugriff auf ein bestimmtes Datenobjekt.
Das Geburtsdatum des 24. Mitglieds (Index 23), der 21.03.1978 wird durch die folgen-
den Zuweisungen in den Datenbestand übernommen:
KegelVerein[23].geboren.tag = 21;
KegelVerein[23].geboren.monat = 3;
KegelVerein[23].geboren.jahr = 1978;
28 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.12:
int *zi; Die Variable zi ist ein Zeiger, der auf ein Datenobjekt vom Datentyp int
zeigt
struct person *zperson; Die Variable zperson ist ein Zeiger, der auf einen Da-
tensatz person, der vorher definiert wurde, zeigt.
Beispiel 3.13:
int *z, zahl, h; Deklaration einer Zeigervariablen und von zwei Variablen
vom int Typ
zahl = 233; Zuweisung eines Wertes an die Variable zahl
z = &zahl; Der Zeigervariablen wird die Adresse der Variablen zugewie-
sen, z zeigt auf Speicherplatz von zahl.
h = *z; Der Variablen h wird der Wert, der unter der Adresse (Wert
von z) steht, zugewiesen.
Der Datentyp Zeiger bietet in der Programmiersprache C eine wichtige Grundlage zur
Umsetzung des Modulkonzepts. Andererseits werden Zeiger zur Erzeugung von dyna-
mischen Datenstrukturen benötigt.
Zusammenfassung
Der Algorithmus, die Vorschrift zur Ausführung von elementaren Operationen, dient
der Verarbeitung von Eingangsdaten zu Ausgangsdaten. Die Begriffe Datentyp und Da-
tenstruktur unterstützen eine nähere Betrachtung der Verarbeitungsdaten. Deren Viel-
falt wird durch die vorliegende Klassifikation der Datentypen bzw. Datenstrukturen
deutlich.
INM11 29
© HfB, 02.12.20, Berk, Eric (904709)
Datentypen werden durch den Wertebereich und die in diesem Bereich definierten Ope-
rationen charakterisiert. Die einfachen Datentypen – int, char, float - und der Datentyp
Zeiger gehören zu den Standarddatentypen von Programmiersprachen.
Die ausgewählten statischen Datenstrukturen – Feld und Verbund – können zwar in ih-
rer Grundstruktur beschrieben werden. Jedoch die Operationen mit diesen Datenstruk-
turen bzw. mit deren Elementen (Standarddatentypen) sind nicht vordefiniert. Für die
konkrete Datenstruktur bzw. die jeweiligen Elemente müssen diese durch spezielle Al-
gorithmen/Operationsfolgen definiert werden. Sogenannte Standardalgorithmen ent-
standen in der Entwicklung der Datenverarbeitung.
Zu Beginn von Kapitel 2 wurde die Frage nach dem Zusammenhang von Algorithmus
und Daten gestellt. Die Antwort ergibt sich nun zusammenfassend aus den Kapiteln 2
und 3.
Zur Lösung von Problemen durch Algorithmen werden konstante und/oder variable
Werte als Daten benötigt. Die gewählten Datentypen und/oder Datenstrukturen ermög-
lichen oder verhindern Operationen, die Ausführung von Aktionen, die der Algorithmus
vorgibt.
Wenn in einem Algorithmus bestimmte Operationen notwendig sind, muss mit einem
bestimmten Datentyp sowie einer bestimmten Datenstruktur gearbeitet werden. So
werden die Operationen, die zur Lösung der Aufgabe nötig sind, möglich. Die optimale
Abstimmung zwischen Daten/Datenstruktur und Algorithmus ist eine der wichtigsten
Voraussetzungen für ein effizientes Programm.
3.1 Für die rechnerinterne Darstellung eines Datentyps short int werden
2 Byte 2 · 8 Bit verwendet (siehe Tab. 3.1).
Berechnen Sie, wie viel verschiedene Bitfolgen und damit verschiedene Werte dar-
gestellt werden können?
Ein Beispiel einer Bitfolge: 0000 1010 1000 1101
3.2 Der Datentyp float wurde als nicht ordinaler Datentyp beschrieben, sein Werte-
bereich ist eine Teilmenge der Menge der reellen Zahlen.
In diesem Datentyp sind die Operationen Dekrementieren (Nachfolger von x ge-
sucht) bzw. Inkrementieren (Vorgänger von x gesucht) nicht definiert.
Begründen Sie, warum diese Operationen für diesen Datentyp keinen Sinn erge-
ben.
3.3 Für den Datentyp int ist die Potenzfunktion nicht definiert. Beschreiben Sie einen
Algorithmus für die Berechnung von erg b pot, wobei b 0 und pot 0 vom Da-
tentyp int sind.
3.4 Nutzen Sie die ASCII- Zeichentabelle (http://www.ascii-tabelle.com) und ermit-
teln Sie das Ergebnis des Vergleichs „Der“ „Zu“.
Erläutern Sie, wie Sie das Ergebnis ermittelt haben.
30 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 31
© HfB, 02.12.20, Berk, Eric (904709)
4.1 Struktogramm
In der Fachliteratur findet man unterschiedliche Arten der Beschreibung und Darstel-
lung von Algorithmen. Neben der informellen sprachlichen Beschreibung durch eine
Kunstsprache, den sogenannten Pseudocode, sind es grafische Darstellungen in Form
von Programmablaufplänen (PAP) bzw. Flussdiagramme und Struktogramme. Die Pro-
grammablaufpläne gelten in der Softwareentwicklung zwar als veraltet, werden aber in
der Dokumentation zur Darstellung eines bestimmten Ablaufverhaltens von Algorith-
men noch häufig verwendet.
Struktogramme stellen Algorithmen im Vergleich zu Programmablaufplänen übersicht-
licher dar. Diese klar strukturierte Abfolge von Aktionen ist gut lesbar und lässt sich re-
lativ leicht in ein Programm umsetzen, zudem ist diese Umsetzung teilweise z. B. durch
das CASE-Tool sogar automatisierbar.
Nassi und Shneiderman haben diese Darstellung 1973 entwickelt, um durch klare Re-
gelungen die Kommunikation zwischen Entwicklern von Software zu erleichtern. Sie
schufen gleichzeitig ein Werkzeug für die Darstellung lesbarer, übersichtlicher und er-
weiterbarer Algorithmen.
Die Sinnbilder für Struktogramme nach Nassi-Shneiderman sind im Blatt 66 261 der
DIN seit 1985 festgelegt.
„Die äußere Form eines Sinnbildes ist immer ein Rechteck; die Unterteilung er-
folgt nur durch gerade Linien.
Die obere Linie eines jeden Sinnbildes bedeutet den Beginn der Verarbeitung,
die untere Linie das Ende der Verarbeitung.
Jedes Sinnbild kann als erste Innenbeschriftung einen Bezeichner tragen. Die
Größe der Gesamtdarstellung und die Unterteilung der Sinnbilder darf dem je-
weiligen Anwendungsfall entsprechend gewählt werden.“
Das elementare Sinnbild ist der Block, ein Rechteck.
Aktion
32 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 4.1:
(1) a : 5
(2) a : a · 15
(3) c : a/b
(4) x : 4 + a/7
(5) x : (9 + x)/77
INM11 33
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 4.2:
(1) Eingabe: x, y, z
(2) Eingabe: n, wn mit i 1(1)n
(3) Eingabe: m, n, matmn mit i 1(1)m und k 1(1)n
34 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 4.3:
(1) Ausgabe: : x, y, z
(2) Ausgabe: ,Es gibt keine Loesung fuer diese Gleichung‘
(3) Ausgabe: ,Der groesste gemeinsame Teiler betraegt‘ x
Aktion n
Beispiel 4.4:
Die Werte der Variablen a und b sollen vertauscht werden:
1. Aktion: a wird in h zwischengespeichert.
2. Aktion: a wird der Wert von b zugewiesen.
3. Aktion: b wird der Wert, der in h zwischengespeichert wurde, zugewiesen.
INM11 35
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 4.5:
Von einer Zahl (zahl ) soll ihr Absolutbetrag (Betrag) ermittelt werden. Der Algo-
rithmus beginnt mit der Eingabe der Zahl; nur wenn die eingegebene Zahl eine ne-
gative ganze Zahl ist, ist eine Aktion nötig, z. B. zahl –7 |–7| 7.
Eingabe: zahl
zahl < 0
Ja Nein
zahl := zahl · –1
Dieses Struktogramm (Abb. 4.2) ist eine Folge von drei Aktionen:
1. Aktion: Eingabe der Zahl durch den Nutzer, die Variable zahl bekommt einen Wert
zugewiesen.
2. Aktion: Alternative: Die Bedingung „Ist der Wert der Variable zahl kleiner als Null?“
wird geprüft. Abhängig vom Ergebnis wird entweder die Aktion im Ja-Zweig
(hier die Multiplikation mit -1) ausgeführt oder die leere Aktion im Nein-
Zweig (Nichts getan!).
3. Aktion: Ausgabe des angegebenen Textes „Betrag“ und des Wertes der Variablen zahl
erfolgt.
• Die mehrfache Auswahl ermöglicht die Wahl einer Aktion aus mehreren Aktionen,
1 aus n (Fallunterscheidung). Die Übereinstimmung des Fallausdrucks mit einer
der Fallkonstanten ist die Grundlage der Entscheidung für eine Aktion i, mit
i 1 … n.
36 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Variante 1:
<fallausdruck>
Hinweis:
Wenn der Fallausdruck einen Wert hat, der in der Menge der verschiedenen Fallkon-
stanten {<fall1>, <fall 2>, …, <fall n>} nicht vorkommt, dann wird keine Aktion aus-
geführt. Um diesen Fall zu berücksichtigen, sollte die Fallunterscheidung einen
sonst-Zweig enthalten.
Variante 2:
<fallausdruck>
Beispiel 4.6:
Innerhalb eines Algorithmus soll durch den Nutzer durch die Eingabe einer positi-
ven ganzen Zahl (wahl ) der weitere Fortgang des Algorithmus bestimmt werden.
Zur Auswahl stehen drei Rechenoperationen (ROp 1 bis ROp 3).
Durch Eingabe eines Wertes für wahl {1, 2, 3}, wird alternativ die jeweilige Re-
chenoperation ausgeführt. Wenn wahl {1, 2, 3} ist, dann wird keine der angebote-
nen Rechenoperationen ausgeführt. Ein Text wird in diesem Fall ausgegeben.
Eingabe: wahl
wahl
1 2 3 sonst
Ausgabe:
ROp 1 ROp 2 ROp 3
'keine Op'
INM11 37
© HfB, 02.12.20, Berk, Eric (904709)
38 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 39
© HfB, 02.12.20, Berk, Eric (904709)
4.2.4.3 Zählschleife
Eine durch einen Laufbereich gesteuerte Schleife wird im Folgenden Zählschleife ge-
nannt, sie ähnelt in Sinnbild und Ausführung der anfangsgeprüften Schleife.
Der Schleifenkörper der Zählschleife wird für jeden Wert eines gegebenen Laufbereichs
je einmal ausgeführt. Der Laufbereich wird mit einer Zählvariablen, die sich von einem
Startwert hin zu einem Endwert mit einer bestimmten Schrittweite ändert, durchlau-
fen.
Mit Eintritt in die Schleife ist die Anzahl
Fuer <zaehlvar> := <anfw> (Sw) <endw>
der Wiederholungen bekannt, diese soll-
te auch nicht manipuliert werden.
<schleifenkoerper>
Die Zählschleife wird eingesetzt, wenn ein vorher definierter Wertebereich lückenlos,
d. h. mit der angegebenen Schrittweite (Sw), vom Anfangswert (anfw) bis zum Endwert
(endw) durchlaufen werden soll.
Beispiel 4.9:
Ausgabe: fakul
Dieses Struktogramm des Algorithmus zur Berechnung von n! besteht aus einer Folge
von drei Aktionen:
• einer Eingabe (a),
• einer Zuweisung (b) und
• einer Zählschleife (c).
40 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Durch einen Trockentest und das Erstellen eines Werteverlaufsprotokolls lässt sich
die schrittweise Ausführung eines durch ein Struktogramm dargestellten Algorithmus
gut verfolgen. Als Hilfsmittel wird eine Tabelle genutzt, die Variablen und deren Verän-
derung werden in Spalten und Zeilen nacheinander in einzelnen Schritten als Simulati-
on der Ausführung des Algorithmus protokolliert.
Werteverlauf in Tabellenform zum Beispiel 4.9 – Berechnung von 4!:
Variablen/Spalten
Innerhalb einer Zählschleife, also in ihrem Schleifenkörper, kann wiederum eine Zähl-
schleife als Aktion auftreten, man nennt diese Aktion dann geschachtelte Schleife.
Ein typisches Beispiel für die Nutzung von zwei ineinander geschachtelten Zählschlei-
fen zeigt das folgende Struktogramm.
Der dargestellte Algorithmus realisiert
Eingabe: m (Zeilen), n (Spalten)
die Eingabe der Elemente einer Matrix
(mat) mit m Zeilen und n Spalten. Auf Fuer zeile := 1(1) m
ein Element der Matrix wird mit zwei
Fuer spalte := 1(1) n
Indizes, zeile und spalte, zugegriffen,
deshalb mat[zeile,spalte]. Da alle Ele- Eingabe: mat[zeile,spalte]
mente einen Wert erhalten sollen, kom-
men zwei geschachtelte Zählschleifen Abb. 4.7: Struktogramm: Eingabe einer
zum Einsatz. m, n-Matrix
INM11 41
© HfB, 02.12.20, Berk, Eric (904709)
Sei m 2 und n 3, dann werden durch die Eingabe in folgender Reihenfolge den Ele-
menten der Matrix Werte zugewiesen:
4.2.5 Modul
Um komplexe Probleme oder Aufgabenstellungen effektiv zu lösen, hat sich die Modu-
larisierung eines Algorithmus praktisch bewährt. Eine Aufgabenstellung wird nicht als
Ganzes gelöst, sondern in kleine überschaubare, z. T. bereits gelöste Teilprobleme zer-
legt. Die Lösungen der jeweiligen Teilaufgaben ergeben dann zusammenfassend die Ge-
samtlösung.
Beim Entwurf von Algorithmen wird deshalb dem sogenannten Top-down-Prinzip,
auch als Vorgehen durch schrittweise Verfeinerung bezeichnet, gefolgt.
Die Grundstruktur Modul unterstützt dieses Vorgehen. Der modulare Aufbau eines
Algorithmus ist die Grundlage klar strukturierter und damit gut lesbarer Programme.
Satz 4.1:
Ein Teilalgorithmus, ein Modul, ist eine in sich geschlossene Einheit. Es handelt sich
um eine Zusammenfassung einer Folge von Aktionen, die ein Teilproblem löst.
Das Modul erhält einen Namen und eine definierte Schnittstelle, die sogenannte Para-
meterliste. Informationen gelangen nur über diese Schnittstelle in das Modul hinein (in-
put) bzw. aus dem Modul heraus (output).
42 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 4.10:
Algorithmus zur Berechnung der Zahl π durch die Leibniz-Reihe.
1 1 1 1 1 ( 1)i
Die Leibniz-Reihe hat die Form: π 4 4
1 3 5 7 9 i 0 2i 1
Die Zahl π wird als Produkt von zwei Faktoren berechnet. Der erste Faktor ist die Kon-
stante 4, der zweite Faktor ist ein durch eine Reihe entwickelter numerischer Wert, eine
Summe mit unendlich vielen Summanden. Praktisch kann man die Berechnung nur mit
endlich vielen Summanden durchführen. Folgender Sachverhalt ist offensichtlich, je hö-
her die Anzahl der Summanden dieser Summe, desto genauer ist der berechnete Wert π.
INM11 43
© HfB, 02.12.20, Berk, Eric (904709)
Beispielrechnungen:
π 3,131593, bei 101 Summanden, i 0 bis100
π 3,140593, bei 1001 Summanden, i 0 bis 1000
π 3,141559, bei 30001 Summanden, i 0 bis 30000.
Das wechselnde Vorzeichen der Summanden wird durch den Zähler des Bruchs (–1i ) in
der Summenformel beschrieben. Da Aktionen in Struktogrammen nur elementare Ope-
rationen enthalten dürfen, wird zur Berechnung des Zählers ein Modul zur Berechnung
von (–1)i mit i ℕ benötigt.
Es ist zweckmäßig, ein universelles und wieder verwendbares Modul zu definieren, ein
Modul zur Berechnung von ax mit a ℝ, x ℕ, hoch(a, x, erg).
hoch(a, x, erg)
Input: a … Basis,
x … Exponent
Output: erg … Ergebnis der Berechnung ax
0 1 sonst
erg := 1
erg := erg * a
( 1)i
Die einzelnen Summanden der Reihe werden jeweils in zwei Aktionen
2i 1
berechnet:
1. Die Berechnung des Zählers (z) durch den Aufruf des Moduls hoch(–1, i, z)
z
2. Die Berechnung des Summanden mit
2i 1
Für die Berechnung von π durch die Leibniz-Reihe ergibt sich nun als Folge von Aktio-
nen: Die Eingabe der Anzahl der Summanden, die Zuweisung des Startwertes 4.0 für die
Berechnung von π (Variable pi ), die Initialisierung der Summe (sum) durch die Zuwei-
sung (sum : 0 ) und die Berechnung der Summe (sum) mit anz Summanden sowie ab-
schließend die Multiplikation mit der Konstante 4.
44 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Eingabe: anz
pi: = 4.0
sum := 0.0
Für i := 0(1)anz
hoch(–1, i, z)
sum := sum + z / (2 * i + 1)
pi:= pi * sum
Ausgabe: pi
Abb. 4.11: Struktogramm zur Berechnung von π durch die Leibniz-Reihe (Beispiel 4.10)
Zusammenfassung
In der Fachliteratur werden verschiedene Varianten der textlichen oder grafischen Dar-
stellung von Algorithmen genutzt. Das im Studienheft genutzte Struktogramm nach
Nassi-Shneiderman ist eine klar strukturierte und leicht lesbare grafische Darstellung ei-
nes Algorithmus bzw. eines Moduls. Die Grundstrukturen – elementare Aktion, Folge
(Sequenz), Auswahl (Selektion) und Wiederholung (Schleife) – wurden ausführlich dar-
gestellt.
Alle prozedurale Algorithmen können mithilfe dieser Grundstrukturen umgesetzt wer-
den.
Die Behauptung, dass der Programmierer aus einem Struktogramm relativ leicht ein
C-Programm erstellen kann, werden Sie am Ende des Studienhefts in den Programmier-
übungen bestätigen können.
In den folgenden Aufgaben zur Selbstüberprüfung werden Sie sich mit Struktogrammen
von Algorithmen, die Lösungen von bekannten mathematischen Aufgaben sind, ausei-
nandersetzen.
4.1 Zeichnen Sie das Struktogramm zu einem Algorithmus, der von einer beliebigen
durch Eingabe bereitgestellten natürlichen Zahl feststellt, ob es sich um eine gera-
de oder ungerade Zahl handelt. Eine entsprechende Ausgabe soll das Ergebnis des
Tests liefern.
4.2 Zeichnen Sie das Struktogramm zu einem Algorithmus, der von einer beliebigen
durch Eingabe bereitgestellten natürlichen Zahl die Quersumme berechnet.
4.3 Zeichnen Sie das Struktogramm zu einem Algorithmus, der von einer beliebigen
durch Eingabe bereitgestellten natürlichen Zahl alle von 1 verschiedenen Teiler
ermittelt. Wird ein Teiler gefunden, dann erfolgt eine Ausgabe.
INM11 45
© HfB, 02.12.20, Berk, Eric (904709)
46 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 5.1:
Lage eines Punktes P(x ; y) im kartesischen Koordinatensystem
Von einem Punkt P(x ; y) mit x und y ℝ soll die Lage im kartesischen Koordinaten-
system bestimmt werden. Einer Variablen Lage soll entsprechend der Lage des Punk-
tes im Koordinatensystem ein Zahlenwert zugeordnet werden.
Sei P(–3,4; 7,8) dann liegt der Punkt P im 2. Quadranten.
Der Wert der Variablen Lage {1, 2, 3, 4, 0, –1, –2} hat folgende Bedeutung:
Lage 1, der Punkt liegt im 1. Quadranten.
Lage 2, der Punkt liegt im 2. Quadranten.
Lage 3, der Punkt liegt im 3. Quadranten.
Lage 4, der Punkt liegt im 4. Quadranten.
Lage 0, der Punkt hat die Koordinaten (0, 0).
Lage –1, der Punkt liegt auf der y-Achse.
Lage –2, der Punkt liegt auf der x-Achse.
Eingabe: Die zu einem Punkt gehörenden Werte, die x- und die y-Koordinate, werden
eingegeben. Diese Werte können Kommazahlen sein, die Daten sind vom Datentyp
float.
Verarbeitung: Die sieben verschiedenen Möglichkeiten der Lage des Punktes sind:
Lage 1 1. Quadrant, x 0 und y 0
Lage 2 2. Quadrant, x 0 und y 0
Lage 3 3. Quadrant, x 0 und y 0
Lage 4 4. Quadrant, x 0 und y 0
Lage 0 Nullpunkt, x 0 und y 0
Lage –1 auf y-Achse, y 0 und x 0
Lage –2 auf x-Achse, x 0 und y 0
INM11 47
© HfB, 02.12.20, Berk, Eric (904709)
Die Mehrfachauswahl kann nicht als Grundstruktur verwendet werden. Der Fallaus-
druck und die Fallkonstanten sind in diesem Beispiel keine ganzzahligen Werte. Die
Grundstruktur für die Tests kann deshalb nur die Alternative sein. Da aber zur endgül-
tigen Entscheidung über die Lage des Punktes maximal 4 Entscheidungen notwendig
sind, sind Schachtelungen notwendig.
Beispiel: Gegeben sei der Punkt P(–3,4; 7,8).
Dann ist die Testreihenfolge:
1. x 0?
2. y 0?, also kein Nullpunkt und nicht auf einer Achse
3. y 0?, also Lage 2 oder Lage 3
4. x 0?, also Lage 2
Eingabe: x , y
x = 0?
Ja Nein
y = 0? y = 0?
Ja Nein Ja Nein
y > 0?
Ja Nein
x > 0? x < 0?
Ja Nein Ja Nein
Ausgabe: Lage
Abb. 5.1: Struktogramm „Lage des Punktes P(x; y) im Koordinatensystem“, Beispiel 5.1
Ausgabe: Die ermittelte Variable Lage ist vom Datentyp int, ein ganzzahliger Wert. Die
algorithmische Grundstruktur Mehrfachauswahl kann nun verwendet werden, um eine
konkrete Aussage zur Lage des Punktes zu liefern.
Lage
0 –1 –2 1 2 3 4
Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe
„Null-Punkt“ „auf x-Achse“ „auf y-Achse“ „im „im „im „im
1. Quadranten“ 2. Quadranten“ 3. Quadranten“ 4. Quadranten“
Abb. 5.2: Struktogramm zur Spezifizierung der Lage von P(x; y), Beispiel 5.1
48 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 5.2: Algorithmus zur Berechnung der Summe beliebig vieler reeller Zahlen
Eine beliebige Anzahl von reellen Zahlen {z1, z2, … zn ℝ, zi Ɑ 0} wird nacheinan-
der eingegeben. Die Zahlen werden nicht gespeichert.
Da die Anzahl der Zahlen beim Start der Eingabe nicht bekannt ist, wird durch die
Eingabe einer negativen Zahl (wert 0) die wiederholte Eingabe von Zahlen been-
det.
Die berechnete Summe (sum) der Zahlen und die ermittelte (gezählte) Anzahl
(zaehl) der eingegebenen Zahlen werden als Ergebnisse ausgegeben.
Eingabe: Über die Tastatur erfolgt die Eingabe der Zahlen, der Summanden. Dabei wird
zur Vereinfachung angenommen, dass der Nutzer korrekte Eingaben tätigt, d. h. als
Zeichen kommen in der Eingabe nur Dezimalziffern und der Dezimalpunkt vor. Diese
Zeichen werden auch in der richtigen Reihenfolge eingegeben.
Ausgabe: Die Werte von sum und Abb. 5.3: Struktogramm zum Algorithmus
zaehl werden ausgegeben. „Summe beliebig vieler reeller
Zahlen“, Beispiel 5.2
Eingabe: Keine Eingabe, der Wert für n, die Anzahl der Elemente und die einzelnen
Zahlen sind bereits im Speicher, Variable feld, sie sind bereits vorhanden (feldi mit
i {1 … n}).
Verarbeitung: Eine Variable min wird mit dem Speicherinhalt von Element feld[1] ini-
tialisiert. Nacheinander werden dann alle anderen, von 2 bis n, Feldelemente mit dem
aktuellen Minimum verglichen. Sollte ein Element des Feldes kleiner sein, dann wird es
das neue Minimum.
Ausgabe: Der Wert der Variablen min, also das Minimum, wird ausgegeben.
INM11 49
© HfB, 02.12.20, Berk, Eric (904709)
min := feld[1]
fuer i := 2(1)n
min := feld[i]
Ausgabe: min
Abb. 5.4: Struktogramm zum Algorithmus „Suche nach dem Minimum von n-Zahlen“.
Beispiel 5.3
Der Algorithmus „Suche nach dem Minimum von n-Zahlen“ ist ein Standardalgorith-
mus, deshalb ist es sinnvoll, ihn als Modul (Mini) zu definieren.
mini := f[1]
mini := f[i]
erg := mini
Abb. 5.5: Moduldefinition Mini(anz, f, erg) – „Suche nach dem Minimum von anz Zahlen“
Das folgende Struktogramm ist ein Grobentwurf eines Algorithmus, mit dem das Sor-
tieren eines gegebenen Feldes erfolgt. Durch die Anzeige des Feldes vor und nach dem
Sortiervorgang kann das Ergebnis des Sortierens geprüft werden.
50 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Ausgabe: feld[i]
Das Modul ist allgemein und wiederverwendbar. Der Datentyp der Elemente und die
numerische Ordnung sind für den Algorithmus nicht von Bedeutung.
Zu 2.: Idee des Sortierens durch „Nachbarschaftsvergleich“: Es werden jeweils zwei be-
nachbarte Feldelemente mit der Ordnungsrelation verglichen. Stehen sie nicht in der ge-
wünschten Ordnung, dann erfolgt der Tausch der beiden Elemente. Nach dem ersten
Durchlauf durch das gesamte Feld ist das gewünschte Element an der letzten Position.
Damit kann das Feld um ein Element verkürzt werden, und der Vergleich wird wie ge-
habt durchgeführt, so lange, bis das zu sortierende Feld nur aus einem Element besteht.
An einem Beispielfeld wird schrittweise der Sortiervorgang dargestellt. Dieses Feld
(feld ) mit sechs Elementen, ganzen Zahlen, wird absteigend sortiert. Im Ergebnis des
Sortierens wird die numerische Ordnung feldi feldi + 1 mit i {1 … n – 1} zwischen
beliebigen Feldelementen gelten.
INM11 51
© HfB, 02.12.20, Berk, Eric (904709)
feld: 12 34 4 17 33 9
Durch wird die notwendige Tauschaktion zwischen zwei benachbarten
Elementen angezeigt.
Start: 12 34 4 17 33 9
1. Durchlauf 34 12 4 17 33 9
17 4 33 9
33 4 9
9 4
Ergebnis 1. Durchlauf 34 12 17 33 9 4
2. Durchlauf verkürztes Feld 34 12 17 33 9 4
17 12 33 9
33 12 9
Ergebnis 2. Durchlauf 34 17 33 12 9 4
3. Durchlauf verkürztes Feld 34 17 33 12 9 4
Ergebnis 3. Durchlauf 34 33 17 12 9 4
4. Durchlauf verkürztes Feld 34 33 17 12 9 4
Ergebnis 4. Durchlauf 34 33 17 12 9 4
5. Durchlauf verkürztes Feld 34 33 17 12 9 4
Ergebnis 5. Durchlauf 34 33 17 12 9 4
Ende, Feld nur ein Element 34 33 17 12 9 4
Ende: 34 33 17 12 9 4
Pro Durchlauf wird ein Element an die richtige Position nach rechts verschoben. Damit
ist nach maximal n – 1 Durchläufen das Feld in der gewünschten Ordnung.
Nach jedem Durchlauf wird die Anzahl der zu vergleichenden Elemente um ein Element
verringert, d. h., beim Durchlauf i {1 … n – 1} werden maximal n – i Vergleiche aus-
geführt. Zwei geschachtelte Zählschleifen sind die Grundstruktur zur Umsetzung des
Sortierens durch „Nachbarschaftsvergleich“. Durch die Ordnungsrelation sind im Er-
gebnis die Elemente in der absteigenden (fallenden) Ordnung.
52 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Sort (anz, f)
Input: anz … Anzahl der Elemente
f … Feld
Output: f … sortiertes Feld
fuer i: = anz(–1) 2
fuer k := 1(1) i – 1
h := f[k]
f[k] := f[k+1]
f[k+1] := h
Im Ja-Zweig der Alternative (Abb. 5.8) realisiert eine Folge aus drei Zuweisungen das
Vertauschen von zwei Elementen des Feldes von ganzen Zahlen. Es ist sicher zweckmä-
ßig, dafür ein Modul zu definieren.
Diese Folge von drei Aktionen Das Modul swap (vertauschen) ist mit den for-
realisiert das Vertauschen von f [k] malen Parametern x und y definiert.
und f [k + 1]
swap (x, y)
h := f[k] input/output x, y
f[k + 1] := h hilf := x
x := y
y := hilf
Die Aktion des Aufrufs des Moduls swap(…) veranlasst die Ausführung des Moduls mit
den aktuellen Parametern.
Nun zurück zum Sortieren: Im dargestellten Ablauf des Sortiervorgangs ist dem kriti-
schen Betrachter bereits ein Nachteil des Algorithmus aufgefallen. Bereits im 3. Durch-
lauf ist kein Tausch von Elementen erfolgt. Das heißt, dass sich die Folge bereits in der
gewünschten Ordnung befindet. Der Algorithmus „bemerkt“ dies nicht und läuft stur
weiter. Daraus ergibt sich der Ansatz zur Verbesserung des Algorithmus.
Innerhalb der inneren Schleife wird nach der Aktion swap(…) ein Merker (die Variable
getauscht) auf den Wert 1 gesetzt. Die äußere Schleife, die Zählschleife, wird durch eine
endgeprüfte Schleife ersetzt. Die Verkürzung des Feldes wird mit dem Index i realisiert.
Abhängig vom Wert der Variablen getauscht wird der Sortiervorgang wiederholt oder
beendet.
INM11 53
© HfB, 02.12.20, Berk, Eric (904709)
Sort_verbessert (anz, f)
Input: anz … Anzahl der Elemente
f … Feld
Output: f … sortiertes Feld
i := anz
getauscht := 0
für k := 1(1) i – 1
getauscht := 1
i := i – 1
Zusammenfassung
An Beispielen wurde jeweils ausgehend von einer Problemstellung der Weg vom Lö-
sungsansatz bis zur Darstellung des Algorithmus in einem Struktogramm gezeigt. Bei-
spielhaft wurde die Schrittfolge der Verarbeitung von Daten anhand der Veränderung
der Werte der Variablen protokolliert. Das Nachvollziehen von Programmabläufen ist
ein wichtiges Werkzeug zur Prüfung eines Programms.
5.1 Nutzen Sie das gleiche Feld von ganzen Zahlen aus Beispiel 5.4 und protokollieren
Sie den Sortiervorgang wie er mit dem Algorithmus des Moduls Sort_verbessert()
abläuft.
54 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 55
© HfB, 02.12.20, Berk, Eric (904709)
prozedurale deklarative
FORTRAN LISP
BASIC COBOL
ALGOL PL1
PASCAL PROLOG
ADA SIMULA
C
PEARL SMALTALK
C++
JAVA PYTHON
Die Pfeile in Abb. 6.1 zeigen, welche Programmiersprache zum Ausgangspunkt für die
Entwicklung einer neuen Programmiersprache wurde. Es entstanden dadurch Ver-
wandtschaftsbeziehungen zwischen den Programmiersprachen. Die Weiterentwicklung
von C zu C++, der objektorientierten Programmiersprache, wird auch Gegenstand des
Studienhefts INM12 sein.
Die Programmiersprache C, die Sprache, in der Sie programmieren werden, ist eine Pro-
grammiersprache vom prozeduralen Sprachtyp. Sie gehört zu den imperativen (befehls-
orientierten) Sprachen. Insbesondere zu PASCAL besteht eine Verwandtschaft, z. B. das
Zeigerkonzept, der Datentyp Pointer, ist dafür ein Beleg.
C ist eine Allzwecksprache, sie ist geeignet für die Erstellung größerer Programmsyste-
me auf unterschiedlichen Rechnern und Rechnersystemen. Sie verfügt über eine hohe
Anzahl an vordefinierten, standardisierten Unterprogrammen.
56 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Definition 6.1:
Eine formale Sprache L über einem Alphabet A ist eine Teilmenge der Menge der
Zeichenfolgen über A, L(A) A*. Die Zeichenfolge l L(A) ist ein Satz der forma-
len Sprache.
Beispiel 6.1:
A1 {a, b, c, 0, 1, 2, 4, 5, 6, 7, 8, 9} ist ein spezielles Alphabet, eine Menge von
Grundsymbolen.
A1* {aaa , bb , 12, 45, b 23, } ist die Stern-Menge zu A1.
Zeichenfolgen, die mit Zeichen des Alphabets durch fortlaufendes Anfügen gebildet
wurden, sind Elemente von A*.
In dieser Menge A1* ist z. B. die Zeichenfolge ba234 enthalten, also gilt ba234 A1*.
In dieser Menge ist aber nicht die Zeichenfolge Z4u5 enthalten, weil ‚Z‘ und
‚u‘ A1.
Die Stern-Menge A* ist die Menge aller aus den Zeichen des Alphabets A bildbaren Zei-
chenfolgen.
Durch Regeln wird eine Untermenge von Zeichenfolgen, die aus Zeichen aus dem Al-
phabet A bestehen, festgelegt. Die Menge der regelgerechten Zeichenfolgen ist dann eine
Teilmenge von A*, die Sprache L(A) A*.
Beispiel 6.2:
Alphabet A1 {a, b, c, 0, 1, 2, 4, 5, 6, 7, 8, 9} und zwei Sprachregeln zur Sprache
L(A1).
Regel 1:
Die Zeichenfolgen beginnen mit einem Buchstaben.
und
INM11 57
© HfB, 02.12.20, Berk, Eric (904709)
Regel 2:
Die Zeichenfolgen haben nur maximal fünf Zeichen.
Es gilt:
a67 L(A1) (Regel 1 und 2 korrekt angewandt),
78c L(A1) (Regel 1 verletzt), aabcbcc L(A1) (Regel 2 verletzt),
x6789 A1* und damit L(A1) (das Zeichen x A1*).
Definition 6.2:
Die Sprachregeln, mit deren Hilfe sich korrekte Sätze aufbauen und analysieren las-
sen, fasst man unter der Bezeichnung Syntax zusammen. Es sind Regeln der Recht-
schreibung, zum Setzen von Satzzeichen und zur Grammatik.
Betrachten wir gleich konkret die Programmiersprache C, hier speziell die Frage:
Was ist ein C-Programm? Die Beschreibung der Syntax dieser Programmiersprache er-
folgt durch eine Menge von Grundsymbolen (Alphabet der Sprache) und durch die Re-
geln für die Bildung eines Satzes (C-Programm) unter Verwendung dieser Grundsymbo-
le. Die Menge der zulässigen, syntaktisch korrekten C-Programme ist so definiert.
Definition 6.3:
Die Semantik eines Satzes in einer Sprache ist sein Gehalt, seine Bedeutung.
Wenden wir uns auch hier gleich konkret der Programmiersprache C zu:
Die Semantik eines C-Programms ergibt sich aus der Betrachtung des Zusammenhangs
von Eingabe und Ausgabe: Was hat das Programm bewirkt?
Beachten Sie:
Ein C-Programm kann syntaktisch korrekt sein, aber es liefert dennoch ein fehler-
haftes, unsinniges oder nicht gewünschtes Ergebnis. Der Fehler kann bereits im vor-
gedachten Algorithmus oder in der Umsetzung in eine Folge von Anweisungen in
der gewählten Programmiersprache liegen.
58 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Definition 6.4:
Die Syntax einer formalen Sprache L(G) wird mit einer endlichen Menge von Syn-
taxdiagrammen definiert.
Eines der Syntaxdiagramme ist ausgezeichnet, es ist das Startdiagramm und mit dem
Startsymbol beschriftet.
Abb. 6.2 zeigt das Syntaxdiagramm Program. Es ist das Startdiagramm der Definition
der Syntax von C. Der Name Program ist das Startsymbol. Dieses Syntaxdiagramm de-
finiert den Aufbau eines Satzes eines C-Programms.
Betrachten Sie parallel zu den folgenden Erläuterungen über Syntaxdiagramme stets die
Abb. 6.2 als Beispiel.
Program
(7)
GlobalDeclaration (8)
Import (43)
FormalParameter (30)
FunctionImplementation (28)
INM11 59
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 6.3:
Die in Abb. 6.3 dargestellten vier Syntaxdiagramme definieren eine spezielle Spra-
che, die Sprache der Bezeichner (Namen für Variablen oder Konstanten).
Bezeichner
Bu Zi
Bu a 1
Bu … 2
Zi0 Z 3
A 4
Zi0
0 … 5
Zi Z 6
Die syntaktischen Variablen, Hilfssymbole, sind Bezeichner, Bu, Zi, Zi0; zu jeder gibt
es ein Syntaxdiagramm. Das Startdiagramm ist Bezeichner.
Die Grundsymbole, die syntaktischen Konstanten, sind die Elemente des Alphabets
der Menge K {1, 2, …, 9, 0, a …, z, A, …, Z }. Es ist die Menge, in der alle Ziffern des
Dezimalsystems und die Zeichen a … z (Kleinbuchstaben des Alphabets) und die Zei-
chen A … Z (Großbuchstaben des Alphabets) enthalten sind.
60 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Ein Satz Z der Sprache Bezeichner kann nur aus Zeichen aus der Menge K, der Menge
der Grundsymbole, gebildet werden. Durch das Anfügen von einzelnen Zeichen an die
anfangs leere Ausgangszeichenfolge Z entsprechend den Regeln entsteht ein syntaktisch
korrekter Satz.
Das Bilden, die Synthese, eines Satzes Z eines Bezeichners, könnte wie folgt ablaufen:
• Start beim Startdiagramm Bezeichner. Am Eingang ist Z ε, die leere Zeichenfolge.
• Man begibt sich auf den Weg durch das Syntaxdiagramm, die Linie führt zum Ein-
gang des Hilfssymbols Bu.
• Das Syntaxdiagramm Bezeichner wird verlassen, und das Syntaxdiagramm Bu wird
jetzt durchlaufen. Der Linie vom Eingang folgend sind alternative legale Wege durch
das Syntaxdiagramm möglich. Durchquert man das Oval mit der Beschriftung ‚A‘
wird die aktuelle Zeichenfolge Z verändert. Das Zeichen, mit dem das Oval beschrif-
tet ist, wird an Z angefügt, Z εA A.
Weiter auf dem Weg, der Linie folgend, erreicht man den Ausgang des Syntaxdia-
gramms Bu. (Das Hilfssymbol Bu wurde durch ein Element aus der Menge
K {a, …, z, A, …, Z } ersetzt.).
INM11 61
© HfB, 02.12.20, Berk, Eric (904709)
Die beiden folgenden Zeichenfolgen Z7 5a, Z8 77z dagegen sind syntaktisch nicht
korrekt. Es sind keine Bezeichner; die Zeichenfolge beginnt in beiden Fällen mit einer
Ziffer.
Auch die folgende Zeichenfolge Z9 Zähler ist syntaktisch nicht korrekt. Sie enthält
den Buchstaben „ä“, er ist nicht Element des Alphabets K (ä K), deshalb ist Z9 kein gül-
tiger Bezeichner.
Mit dem Beispiel 6.4 wird die Analyse eines Satzes einer Sprache, das Prüfen auf syn-
taktische Korrektheit einer Zeichenfolge, gezeigt.
Beispiel 6.4:
Eine Sprache Ausdruckssprache wird definiert durch:
• die syntaktischen Konstanten, die Menge der Grundsymbole
G {, , ∗, DIV, MOD, (,) 0, …, 9, A, …, Z, a, …, z }
• die Hilfssymbole, die syntaktischen Variablen:
EinfAusd, Term, Faktor, Bezeichner und Zahl (siehe Abb. 6.5)
• Das Hilfssymbol EinfAusd ist das Startsymbol, bzw. das Startdiagramm ist Einf-
Ausd.
EinfAusd Term
Term Faktor
+ + *
- - DIV
MOD
Faktor
Bezeichner
Zahl
( EinfAusd )
62 INM11
© HfB, 02.12.20, Berk, Eric (904709)
• Start: Am Eingang des Syntaxdiagramms EinfAusd, dem Startdiagramm, hat der In-
dex den aktuellen Wert 1, i 1. Das erste Zeichen von W ⴕ wird geprüft, W1ⴕ Z .
• Vom Eingang des Syntaxdiagramms der Linie folgend erreicht man eine Verzwei-
gung. Da W1ⴕ Z , kein Grundsymbol ist, ergibt sich keine Übereinstimmung mit
oder – (Vorzeichen). Es wird ein anderer Weg gewählt. Geradeaus gelangt man zum
Rechteck mit der Beschriftung Term, an dessen Ausgang die Marke M1 angebracht
wird. Das Syntaxdiagramm Term wird nun betrachtet. Auf legalem Weg erreicht
man das Rechteck Faktor, an dessen Ausgang wird die Marke M2 angebracht.
• Nun wird das Syntaxdiagramm Faktor betrachtet. Auf legalem Weg erreicht man
eine Verzweigung. Da W1ⴕ Z , durchquert man das Rechteck mit der Beschriftung
Zahl; i wird erhöht, i 2. Das erste Zeichen ist akzeptiert.
INM11 63
© HfB, 02.12.20, Berk, Eric (904709)
• Vom Ausgang des Syntaxdiagramms Faktor kehrt man zu M2 zurück. Die Marke M2
wird entfernt, und an der nachfolgenden Verzweigung entscheidet man über den
weiteren Weg. Da W2ⴕ Z , ist der Weg nach unten keine Option, also kehrt man zu
M1 zurück, entfernt die Marke M1 und entscheidet an der nachfolgenden Verzwei-
gung über den weiteren Weg. Da W2ⴕ , durchquert man das Oval mit der Be-
schriftung , erhöht i, i 3. Das zweite Zeichen ist akzeptiert.
• Auf legalem Weg gelangt man zum Rechteck mit der Beschriftung Term, an dessen
Ausgang wird die Marke M1 angebracht. Das Syntaxdiagramm Term wird nun be-
trachtet. Auf legalem Weg erreicht man das Rechteck Faktor, an dessen Ausgang
wird die Marke M2 angebracht. Das Syntaxdiagramm Faktor wird nun betrachtet.
Auf legalem Weg erreicht man eine Verzweigung. Da W 3ⴕ ( , durchquert man das
Oval mit der Beschriftung (; i wird erhöht, i 4. Das dritte Zeichen ist akzeptiert.
• Auf legalem Weg gelangt man zum Rechteck mit der Beschriftung EinfAusd, an des-
sen Ausgang wird die Marke M3 angebracht. Vom Eingang des Syntaxdiagramms
EinfAusd der Linie folgend erreicht man eine Verzweigung. Der Vergleich von
W 4ⴕ Z mit den Grundsymbolen + und – (Vorzeichen) zeigt, dass der Weg nach un-
ten keine Option ist. Geradeaus gelangt man zum Rechteck mit der Beschriftung
Term, an dessen Ausgang wird die Marke M4 angebracht. Das Syntaxdiagramm
Term wird nun betrachtet. Auf legalem Weg erreicht man das Rechteck Faktor, an
dessen Ausgang wird die Marke M5 angebracht. Das Syntaxdiagramm Faktor wird
nun betrachtet. Auf legalem Weg erreicht man eine Verzweigung. Da W 4ⴕ Z ,
durchquert man das Rechteck Zahl; i wird erhöht, i 5. Das vierte Zeichen ist ak-
zeptiert.
• Vom Ausgang des Syntaxdiagramms Faktor kehrt man zu M5 zurück, die Marke M5
wird entfernt, und man entscheidet an der nachfolgenden Verzweigung über den
weiteren Weg. Da W5ⴕ DIV , durchquert man das Oval mit der Beschriftung DIV;
i wird erhöht, i 6. Das fünfte Zeichen ist akzeptiert.
• Auf legalem Weg erreicht man das Rechteck Faktor, an dessen Ausgang wird die
Marke M5 angebracht. Das Syntaxdiagramm Faktor wird nun betrachtet. Auf lega-
lem Weg erreicht man eine Verzweigung. Da W6ⴕ Z , durchquert man das Rechteck
mit der Beschriftung Zahl; i wird erhöht, i 7. Das sechste Zeichen ist akzeptiert.
• Vom Ausgang des Syntaxdiagramms Faktor kehrt man zu M5 zurück, die Marke M5
wird entfernt und an der nachfolgenden Verzweigung über den weiteren Weg ent-
schieden. Da W7ⴕ , ist der Weg nach unten keine Option, also kehrt man zu M4
zurück, entfernt die Marke M4 und entscheidet an der nachfolgenden Verzweigung
über den weiteren Weg. Da W7ⴕ , wird das Oval mit der Beschriftung – durch-
quert; i wird erhöht, i 8. Das siebte Zeichen ist akzeptiert.
• Auf legalem Weg gelangt man zum Rechteck mit der Beschriftung Term, an dessen
Ausgang wird die Marke M4 angebracht. Das Syntaxdiagramm Term wird nun be-
trachtet. Auf legalem Weg erreicht man das Rechteck mit der Beschriftung Faktor,
an dessen Ausgang wird die Marke M5 angebracht‘. Das Syntaxdiagramm Faktor
wird nun betrachtet. Auf legalem Weg erreicht man eine Verzweigung. Da W 8ⴕ B ,
durchquert man das Rechteck mit der Beschriftung Name; i wird erhöht, i 9. Das
achte Zeichen ist akzeptiert.
• Vom Ausgang des Syntaxdiagramms Faktor kehrt man zu M5 zurück, die Marke M5
wird entfernt, und man entscheidet an der nachfolgenden Verzweigung über den
weiteren Weg. DaW9' ), ist der Weg nach unten keine Option. Vom Ausgang des
64 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Syntaxdiagramms Term kehrt man zu M4 zurück, die Marke M4 wird entfernt, und
man entscheidet an der nachfolgenden Verzweigung über den weiteren Weg. Da
W9ⴕ ), ist der Weg nach unten keine Option. Vom Ausgang des Syntaxdiagramms
EinfAusd kehrt man zu M3 zurück, die Marke M3 wird entfernt. Da W9ⴕ ), durch-
quert man das Oval mit der Beschriftung ); i wird erhöht, i 10. Das neunte Zeichen
ist akzeptiert.
• Vom Ausgang des Syntaxdiagramms Faktor kehrt man zu M2 zurück, die Marke M2
wird entfernt. Da kein weiteres Zeichen mehr zu prüfen ist, wird das Syntaxdia-
gramm Term verlassen. Vom Ausgang des Syntaxdiagramms Term kehrt man zu M1
zurück, die Marke M1 wird entfernt. Da kein weiteres Zeichen mehr zu prüfen ist,
wird das Syntaxdiagramm EinfAusd verlassen.
• Das Ende der Analyse ist erreicht, alle Zeichen wurden akzeptiert, auf legalem Weg
konnte der Durchlauf erfolgen. Alle Marken sind entfernt.
Dieses Beispiel illustriert den Ablauf der Syntaxkontrolle von Programmen, einer Zei-
chenfolge mit einer Vielzahl von Zeichen. Es wurde sicher auch deutlich, dass diese Auf-
gabe durch Algorithmen beschrieben werden kann, sie ist programmierbar.
INM11 65
© HfB, 02.12.20, Berk, Eric (904709)
In folgenden Schritten, unter Nutzung der Komponenten der IDE, entsteht ein Pro-
gramm:
1. Phase – Editieren:
Das Editieren, das Erstellen und wenn notwendig das Korrigieren des Quelltextes, er-
folgt mit dem syntaxbasierten Editor. Syntaxbasierte Editoren sind Texteditoren, die den
Programmierer beim regelgerechten Aufschreiben von Anweisungsfolgen in einer Pro-
grammiersprache unterstützen.
Durch die farbliche Gestaltung werden Grundsymbole der Programmiersprache hervor-
gehoben, das Einfügen von Klammern, entsprechend den Syntaxregeln, erfolgt automa-
tisch; durch das Einrücken von Texten werden strukturelle Aspekte unterstützt.
2. Phase – Übersetzen:
66 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Kompilieren Laden
Kompiler Lader, Linker
Objekt-
datei
Fehlermeldungen Maschinencode
Quelltext
Abarbeiten
Editieren
Eingabedaten Ausgabedaten
(Fehlermeldung)
Editor
Laufzeitsystem
Abb. 6.6: Aufgaben der Komponenten der IDE beim Erstellen eines C-Programms
Für das Erstellen von C-Programmen wird Ihnen die IDE DEV-C++ empfohlen. Eine
Anleitung zur Installation auf Ihrem PC sowie zu deren Nutzung finden Sie in Study-
Online.
Übung 6.1:
Schauen Sie sich jetzt noch einmal die Bedienoberfläche der IDE DEV C++ an.
Der Quelltext „Sparjahre.c“ (Beispiel in der Einleitung) wird in den Texteditor gela-
den, gegebenenfalls editiert und anschließend übersetzt. Die Abb. 1.3 und 1.4 in Ka-
pitel 1 zeigen entsprechende Bilder. Das Ausführen des Programms wird durch den
Menüpunkt „Ausführen“ im Menü „Ausführen“ oder die Taste (F10) ausgelöst.
INM11 67
© HfB, 02.12.20, Berk, Eric (904709)
Zusammenfassung
6.1 Gegeben ist die Menge der Grundsymbole A {0, 1, 2, …, 9} und die drei Syntax-
diagramme Zahl, Zi0 und Zi. Zahl ist das Startdiagramm.
Die Sprache vorzeichenlose ganze Zahl ist damit definiert.
a) Finden Sie drei verschiedene Beispiele für syntaktisch korrekte vorzeichenlose
Zahlen.
b) Prüfen Sie, ob die Ziffernfolge 01234 ein Satz dieser Sprache ist. Begründen Sie
Ihre Entscheidung.
Zahl Zi
0 1
Zi 2
Zi0 3
4
Zi0
0
5
Zi
6
Abb. 6.7: Zu Aufgabe 6.1: Syntaxdiagramme zur Sprache vorzeichenlose ganze Zahl
68 INM11
© HfB, 02.12.20, Berk, Eric (904709)
ROM
Fm Fc Fx Fi
FH FZ FE
Fm Fc Fx Fi
M C X I
MM CC XX II
6.8 a: Teil 1
FH FZ
C D X L
M C
D C
Fc Fx
FE
I V
Fi
6.8 b: Teil 2
INM11 69
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
In diesem Kapitel werden der grundlegende Aufbau eines C-Programms, das Al-
phabet der Sprache, die Menge der Grundsymbole sowie die Syntaxbeschreibung
von C durch die Menge der Syntaxdiagramme dargestellt. Kleine Beispielpro-
gramme entstehen durch das Umsetzen vorliegender Struktogramme in C-Pro-
gramme.
Der Aufruf und die Definition von Funktionen als wiederverwendbare Module
werden erläutert und mit Beispielen belegt.
Die Programmiersprache C ist eine Allzwecksprache. Sie ist nicht auf die vorrangige
Nutzung in einem bestimmten Anwendungsgebiet zugeschnitten. Der Sprachkern von
C umfasst relativ wenige Sprachelemente. Die zahlreichen vorgefertigten Bibliotheken
von Modulen erleichtern das Programmieren und damit ihre Verwendung für die Lö-
sung von Aufgabenstellungen mit Programmen in vielen Bereichen. C ist keine spezielle
Lehrsprache, und die vielfältigen Möglichkeiten der Umsetzung ein und derselben Auf-
gabe erschwert z. T. auch das Lehren dieser Sprache. Es kann deshalb in diesem Kapitel
nicht der komplette Sprachumfang beschrieben werden.
Ein C-Programm besteht aus mindestens einer Funktion, die den Namen ‚main‘
(Hauptprogramm) tragen muss.
// Program Einf.c
//Program (7)
//Import (43)
#include <stdio.h>
//Anfang Block (15)
int main(){
//Declaration (9), VariableDeclaration (12)
int i;
//StatementSequence(16), Statement(17)
//Assignment(18), Assignment1(19)
i = 15;
//FunctionCall (33)
printf("Die Zahl ist %d", i);
//ReturnStatement (32)
return 0;
//Ende Block (15)
}
70 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Das C-Programm Einf.c (Code 7.1) ist ein einfacher syntaktisch korrekter Quelltext.
Nutzen Sie parallel zu den folgenden Erläuterungen die „Syntaxdiagramme der Pro-
grammiersprache C“ (siehe Anhang D) zur Überprüfung.
Beginnend mit dem Syntaxdiagramm (7), dem Startdiagramm, wird die Syntax dieses
Quelltextes geprüft.
Ein Import, Syntaxdiagramm (43), ist notwendig. Die Bibliothek stdio.h wird für die
Routine zur Ausgabe, printf(…) (siehe Anweisungsfolge von main), benötigt.
Da keine globale Deklaration enthalten ist, folgt auf legalem Weg im Quelltext die Folge
der Grundsymbole int main (). Die Funktion main ist parameterlos.
In Syntaxdiagramm (15) wird der Aufbau eines Blocks definiert.
Zwischen den Grundsymbolen { … } (Block) steht mindestens eine Deklaration, Syntax-
diagramm (9). Mit VariableDeclaration, Syntaxdiagramm (12), ergibt sich der Datentyp
der Variablen als int (TypeIdent), der Bezeichner (Ident) i wird gewählt, damit ergibt
sich die Deklaration int i;. Danach kann in der Folge von Anweisungen (Statement-
Sequence), Syntaxdiagramm (16), mit dieser Variablen gearbeitet werden, z. B. i 15 ist
eine Zuweisung (Assignment), siehe Syntaxdiagramme (18) und (19). Die Zahl 15 ist ein
Ausdruck (Expression). Die Anweisung wird abgeschlossen durch ein Semikolon. Die
nächste Anweisung ist eine Ausgabe auf dem Bildschirm, die mit der Funktion
printf(…) realisiert wird. Dazu benutzt man die Syntax für einen Funktionsaufruf
(FunctionCall) nach Syntaxdiagramm (33). Der Name der Funktion wird notiert, und in
dessen Parameterliste stehen die aktuellen Parameter. Der erste aktuelle Parameter ist
ein Text, in dem ein Platzhalter (%d) für eine Variable vom Typ integer enthalten ist, der
zweite aktuelle Parameter, durch Komma vom ersten getrennt, ist der Variablenname i
(diese Variable wird ausgegeben). Da noch eine Anweisung folgt, schließt das Semikolon
die Anweisung ab.
Am Ende des Blocks der Funktion main wird nach erfolgreichem Ablauf der Folge der
Anweisungen der Wert 0 als Ergebnis geliefert, eine Return-Anweisung (ReturnState-
ment) wird eingefügt. Das Grundsymbol } schließt den Anweisungsblock ab.
INM11 71
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Das C-Programm Einf.c besteht nur aus der Funktion main(). Es hat folgenden grund-
legenden Aufbau:
Import-Anweisungen
Globale Deklaration
int main ( ) {
}
Dieser Aufbau entspricht dem Syntaxdiagramm (7) „Program“, dem Startdiagramm der
Menge der Syntaxdiagramme zu C (Anhang D), Abb. 7.3.
Program
(7)
GlobalDeclaration (8)
Import (43)
FormalParameter (30)
FunctionImplementation (28)
72 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
7.2.1 Grundsymbole
Die Grundsymbole der Programmiersprache C werden in folgende Gruppen eingeteilt:
• Schlüsselwörter
• Bezeichner
• Zahlen (Konstanten)
• Ausdrücke
• Kommentare
• Trennzeichen
• Operatoren
Betrachten wir diese Gruppen genauer:
Schlüsselwörter:
INM11 73
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Die Syntax von Zahlen, Bezeichnern und Ausdrücken wurde in der Aufgabe zur Selbst-
überprüfung 6.1 und in Abschnitt 6.2.2 in den Beispielen 6.3 und 6.4 erarbeitet. Die dort
erarbeiteten Regeln entsprechen denen der Programmiersprache C.
Elemente aus der folgenden Menge (Z ) von Zeichen können zur Bildung von Bezeich-
nern verwendet werden: Z {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y,
z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, _, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9}
Zur Beachtung:
In C ist die Groß- und die Kleinschreibung in einer Zeichenfolge, in Schlüssel-
wörtern und Bezeichnern zu beachten:
Beispiel: Else else, der Bezeichner test ist ungleich dem Bezeichner teSt.
Kommentare:
Kommentare sind für die Anweisungsfolge des C-Programms nicht von Bedeutung, sie
dienen dem besseren Verständnis, der Dokumentation des in ein C-Programm umge-
setzten Algorithmus.
Das Zeichen // am Beginn einer Zeile macht diese zu einer Zeile, in der Kommentare ste-
hen. Das Ende der Zeile ist auch das Ende des Kommentars.
Soll der Kommentar beliebig viele Zeilen umfassen, so wird er mit /* */ eingeschlos-
sen.
Trennzeichen:
Operatoren:
In Tab. 7.2 sind die Operatoren der Programmiersprache C zusammengestellt. Das Ope-
rationszeichen, der Zweck, der Rang und die Assoziativität des Operators sind jeweils
angegeben.
74 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Beispiel: Mit a=1 und b=2 hat Zuweisung c=++a + b++; also dadurch a=2, b=3,
c=4.
INM11 75
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
7.2.2 Syntaxdiagramme zu C
Die Syntaxregeln der Programmiersprache C sind durch die Menge der Syntaxdiagram-
me (1) bis (42) im Anhang D zu diesem Studienheft zusammengestellt (in StudyOnline
finden Sie diesen Anhang als separate Datei). Es ergibt wenig Sinn, sich diese Syntax-
diagramme nur anzuschauen. Nutzen Sie diese als Nachschlagwerk, als Hilfsmittel beim
Programmieren. Mit den folgenden Beispielen und Erläuterungen wird gezeigt, wie der
Programmierer dieses Hilfsmittel beim Erstellen eines C-Programms nutzt.
Beispiel 7.1:
Ein C-Programm zum Beispiel 5.1, „Lage eines Punktes P(x, y) im kartesischen Ko-
ordinatensystem“ ist zu entwickeln.
Das folgende Struktogramm, das sich aus den Abbildungen 5.1 und 5.2 ergibt, ist die
Grundlage des gesuchten C-Programms.
Eingabe: x , y
x = 0?
Ja Nein
y = 0? y = 0?
Ja Nein Ja Nein
y > 0?
Ja Nein
x > 0? x < 0?
Ja Nein Ja Nein
Lage
0 –1 –2 1 2 3 4
Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe Ausgabe
„Nullpunkt“ „auf x-Achse“ „auf y-Achse“ „im „im „im „im
1. Quadranten“ 2. Quadranten“ 3. Quadranten“ 4. Quadranten“
Abb. 7.4: Struktogramm „Lage eines Punktes P(x, y) im …“, aus Abb. 5.1 und 5.2
zusammengesetzt
76 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
IfStatement
(20)
( BoolExpression (6) ) Statement (17) else Statement (17)
SwitchStatement
(21)
switch ( Expression (6) ) {
Nun wird programmiert! Die Funktion main() wird implementiert, siehe Syntaxdia-
gramm Program (7). Die Vorüberlegungen 1 bis 3 werden umgesetzt. So entsteht der fol-
gende Code Lage.c.
// Program Lage.c
# include <stdio.h>
int main(){
//Deklaration
float x,y;
int Lage;
//Eingabe von x(%f..float)
scanf("%f",&x);
//Eingabe von y(%f..float)
scanf("%f",&y);
INM11 77
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Im syntaxbasierten Editor ist der Quelltext Lage.c (Abb. 7.6) durch die farbliche Hervor-
hebung in seiner Struktur und der Anweisungsfolge gut nachvollziehbar.
78 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Eingabe: zahl
zahl_h := zahl
summe := 0
Überlegungen:
1. Die Variablen zahl, zahl_h und summe müssen deklariert werden, dafür ist Datentyp
int vorzusehen.
2. Die Folge der Anweisungen enthält fünf Aktionen, eine Eingabe, zwei Zuweisun-
gen, eine Schleife und eine Ausgabe.
3. Die Schleife ist eine anfangsgeprüfte Schleife.
WhileStatement
(22)
while ( BoolExpression (6) ) Statement (17)
4. Der Operator MOD, ganzzahlige Operation zur Berechnung des Restes der Division
(siehe Tab. 7.2
Operatoren Punktrechnung) hat das Operatorzeichen %, der Operator DIV (ganzzah-
lige Division) wird mit dem Zeichen / umgesetzt.
5. Implementieren von main() zu Quersumme.c:
// Program Quersumme.c
# include <stdio.h>
int main(){
int zahl, zahl_h, summe;
// die Variablen
printf(" Quersumme einer Zahl \n\n");
// die Überschrift
printf(" Geben Sie eine ganze Zahl ein \t");
scanf("%d", &zahl);
//Eingabe von zahl (d auch int)
// die Zuweisungen und die Schleife ergänzen
return 0;
}
INM11 79
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Zur endgeprüften Schleife nach Nassi-Shneiderman (Abb. 7.9) gibt es in der Program-
miersprache C keine analoge Umsetzung! (siehe Anhang B).
Die endgeprüfte Schleife wird mit dem DoWhileStatement realisiert.
Diese Anweisung wird so gelesen: „tue … so lange <bed> erfüllt“.
Also muss die Bedingung im C-Programm angepasst werden!
„tue … so lange wdh=‘j‘ ”
DoWhileStatement
(23)
do Statement (17) while ( BoolExpression (6) ) ;
80 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
// Programm Sparjahre_nochmal.c
#include<stdio.h>
#include<conio.h>
int main(){
float zins,anf,end,guthaben;
int jahre;
char wdh ='n';
do {
system("cls");
printf("\n");
printf("\n Berechnung der Sparjahre \n");
printf(" Geben Sie das Anfangskapital ein: \t");
scanf("%f", &anf);
printf(" Geben Sie den aktuellen Zinssatz ein: \t");
scanf("%f", &zins);
end=2*anf;
printf("\n");
guthaben=anf;
jahre=0;
while(guthaben<end){
guthaben=guthaben+guthaben*zins;
jahre=jahre+1;
}
printf("Nach %d Jahren ist das Sparziel erreicht.\n", jahre);
printf("\n");
printf(" Moechten Sie die Rechnung wiederholen?(j)a/(n)ein");
wdh = getch();
} while (wdh == 'j');
return 0;
}
Im Code 7.4 sind die zusätzliche Include-Datei, die zusätzlich deklarierte Variable und
die zusätzlichen Anweisungen hervorgehoben (fett gedruckt).
Die Funktion getch() aus der Bibliothek conio.h nimmt von der Tastatur einen Buch-
staben ohne Bildschirmecho (keine Ausgabe) entgegen. Beachten Sie, nur das Zeichen ,j‘
führt zur Wiederholung der Anweisungsfolge. Alle anderen Buchstaben führen zum Be-
enden des Programms.
INM11 81
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
k k k 1
y f (x , k ,l ) * sin(x ) * cos(x ) l * 3.5
l l 1
für x 0.4 bis x 1.5 mit einer Schrittweite von Δx 0.1 mit k 5 und l 3 .
Die berechneten Werte sind in einer Tabelle mit zwei Spalten auszugeben: x | y.
m m!
Beachten Sie: Der Binomialkoeffizient wird berechnet durch
n !* (m n )!
n
Betrachtet man die zur Lösung der Aufgabe notwendigen Rechenschritte und damit den
zu realisierenden Ablauf eines Programms zu Beispiel 7.4, so stellt man fest:
• In der Funktion main wird y f (x, k, l ) für x 0.4 bis x 1.5 wiederholt (12-mal)
berechnet. Die algorithmische Grundstruktur ist eine Schleife.
• Pro Berechnung von y wird dreimal der Binomialkoeffizient berechnet.
• Zur Berechnung eines Binomialkoeffizienten wird dreimal die Funktion zur Berech-
nung von n! aufgerufen.
Zur Berechnung von y werden somit folgende Funktionen benötigt:
• die vordefinierten Funktionen sin(x) und cos(x) aus der Bibliothek (math.h),
• die Ausgabefunktion printf() aus stdio.h,
• eine Funktion zur Berechnung des Binomialkoeffizienten, binom(m,n) und
• eine Funktion fakul(n) zur Berechnung von n!.
Die Funktionen zur Berechnung des Binomialkoeffizienten und der Fakultät sind nicht
in einer der Standardbibliotheken von C definiert. Sie müssen vom Programmierer erst
als C-Funktionen programmiert werden.
82 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Anmerkung:
Beim Erstellen eines C-Programms für eine bestimmte Aufgabenstellung steht der
Programmierer oft vor der Fragestellung, ob es bereits für eine bestimmte Teilaufga-
be ein Modul, eine vordefinierte Funktion, gibt. Ist es notwendig und sinnvoll, eine
spezielle Funktion zusätzlich zu programmieren?
Im folgenden Abschnitt werden das Programmieren einer Funktion und die Syntax der
Definition von Funktionen erläutert.
Vorab ist es an dieser Stelle zweckmäßig, den Gebrauch der folgenden drei Begriffe
Funktionsdefinition, Funktionsdeklaration und Funktionsimplementation festzule-
gen.
• Eine Funktion, muss in einem C-Programm definiert werden. Die Definition einer
Funktion umfasst die Deklaration der Funktion und deren Implementation – Func-
tionSpecification(27).
• Die Deklaration einer Funktion erfolgt durch den Funktionskopf Function-
Heading(29). Der Typ der Funktion (der Typ des möglichen Funktionswertes oder
Rückgabewertes) sowie der Name der Funktion und die Parameterliste werden be-
kanntgegeben.
• Das Implementieren einer Funktion – FunctionImplementation(28) – ist das eigent-
liche Programmieren. Der zur Funktionsdeklaration passende Block, mit den lokalen
Deklarationen und den entsprechenden Anweisungen, wird in der Syntax von C no-
tiert.
FunctionImplementation
(28)
FunctionHeading (29) Block (15)
FunctionHeading
(29)
TypeIdent (2) Ident (2) ( FormalParameters (30) )
FormalParameters
(30)
ParamSection (31)
void
ParamSection
(31)
TypeIdent (2) Ident (2)
INM11 83
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Beispiel 7.5:
Eine Funktion zur Berechnung von n! ist als Funktion in C zu programmieren. Fol-
gende Schritte sind dazu nötig:
1. Ermitteln der Berechnungsvorschrift zu n!, siehe mathematische Grundlagen
2. Umsetzung des Algorithmus als Moduldefinition.
3. Codierung als C-Quelltext unter Beachtung der Syntax
5! 5 · 4! 5 · 24 120
4! 4 · 3! 4·6 24
3! 3 · 2! 3·2 6
2! 2 · 1! 2·1 2
Also 5! 120.
Oder man erkennt daraus die iterative Berechnung n! 1 · 2 · 3 … · n.
84 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
ForStatement
(24)
for ( Assignment1 (19) ; BoolExpression (19) ; Assignment1 (19) )
Statement (17)
// FunctionHeading (29)
// Input n, formaler Parameter
unsigned long fakul(int n){
// Block (15)
// VariableDeclaration (12)
int i;
unsigned long h=1;
// ForStatement (24)
for(i=1;i<n+1;i=i+1)
h=h*i;
// return Ergebnis - output h
return h;
}
Diese Funktionsdefinition könnte in einen Quelltext, in ein C-Programm, bei Bedarf auf-
genommen werden.
Ein Programmierer entwickelt mit zunehmender Programmiererfahrung eigene Vorlie-
ben in der Gestaltung syntaktisch korrekter C-Programme. Eine Empfehlung für die Ge-
staltung eines gut lesbaren Quelltextes sei deshalb an dieser Stelle gestattet.
Hinweis:
Die Deklaration einer Funktion erfolgt im Bereich der globalen Deklarationen
durch die Angabe des Funktionskopfes. Damit ist die Funktion dem Compiler „be-
kannt“.
Die Definition der Funktion wird in den Quelltext nach der Funktion main einge-
fügt.
INM11 85
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Die Möglichkeit, dass eine Funktion innerhalb einer anderen Funktion deklariert wer-
den kann, wird in diesem Studienheft bewusst nicht genutzt. Alle Funktionen sind glo-
bal deklariert. Die Funktion main (...) wird als Hauptprogramm verstanden und alle wei-
teren (Unter-)Funktionen befinden sich auf einer Hierarchieebene. Eine weitere
Hierarchieebene innerhalb der (Unter-)Funktionen gibt es nicht.
Der syntaxbasierte Editor der IDE DEV C++ unterstützt das Implementieren von C-
Quelltexten durch die farbliche Gestaltung des Textes und die Möglichkeit des Verän-
derns der Sichtbarkeit von Modulen (vgl. Abb. 7.14).
Abb. 7.14: Die Funktion main() und die (Unter-)Funktionen zu Beispiel 7.4
FunctionCall
(33)
FunctionIdent (2) ( ActualParameter (2) ) ;
Die aktuellen Parameter in der Aufrufanweisung einer Funktion müssen zu den forma-
len Parametern der Funktionsdefinition passen. Für den Aufruf einer Funktion benötigt
man keine Kenntnisse über die konkrete Implementation der Funktion.
86 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Besitzt die Funktion einen Rückgabewert, kann der Funktionsaufruf in einem Ausdruck
verwendet werden. Er kann beispielsweise auf der rechten Seite einer Zuweisung stehen
oder in einer Ausgabeanweisung.
Beispiel 7.6:
Funktionsaufrufe in einem Ausdruck/einer Zuweisung (Berechnung von y in
Beispiel 7.4):
y binom(k, l ) · sin(x) + binom(k, l – 1) · cos(x) + binom(k – 1, l ) · 3.5;
Ein Funktionsaufruf in einer Ausgabeanweisung:
printf(" 5! hat den Wert %d ,\n", fakul(5) );
Es wurde schon mehrfach darauf hingewiesen, dass der Sprachkern der Programmier-
sprache C im Vergleich zu anderen Programmiersprachen sehr klein ist. Es steht jedoch
eine Vielzahl von vordefinierten Funktionen in sogenannten Bibliotheken zur Verfü-
gung. Im Beispiel 7.4 werden u. a. die Standardfunktionen sin(x) und cos(x) aus der Bi-
bliothek der mathematischen Funktionen zur Berechnung des Wertes von y genutzt.
Die Direktive #include fügt andere Dateien (include-Dateien) in das Quellprogramm
ein. Dabei handelt es sich um Header-Dateien, die die Prototypvereinbarungen der Stan-
dardfunktionen, welche die C-Library bereitstellt, enthalten. Bereits bei der Installation
einer Softwareentwicklungsumgebung für die Programmierung mit C werden bestimm-
te include-Dateien in ein Unterverzeichnis kopiert.
In Tab. 7.3 sind einige wichtige include-Dateien genannt. Die jeweils durch sie bereitge-
stellten Funktionen werden kurz durch das zugeordnete Aufgabenspektrum charakteri-
siert.
Dateiname Aufgabenspektrum
assert.h Überprüfung von Bedingungen
ctype.h Typkonvertierungen und Typtests
float.h Fließkomma-Bibliothek
limits.h Ermittlung der Grenzen für Datentypen
math.h mathematische Funktionen, sqrt, sin usw.
stddef.h Standardkonstanten
stdio.h Standardeingabe und -ausgabe
stdlib.h Standardbibliotheksfunktionen zu Zufallszahlen und Arithmetik
string.h Funktionen für Daten des Datentyps string
time.h Funktionen und Konstanten zur Nutzung der Echtzeituhr
INM11 87
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Eingabe-/Ausgabe-Funktionen
In der Datei stdio.h, der Bibliothek zur Realisierung von Standardein- und -ausgabe, sind
die Funktionen zur formatierten Eingabe über die Tastatur und die Ausgabe auf dem
Bildschirm enthalten.
Die zur Eingabefunktion zugehörige Funktionsdeklaration lautet:
int scanf(const char *format [,parameter]…);
Mit der Funktion scanf() können Werte unterschiedlicher Datentypen formatiert ein-
gelesen werden. Eingelesen wird dabei von der Standardeingabe (stdin), das ist norma-
lerweise die Tastatur.
Der erste Parameter, der Formatstring *format, ist eine Folge von Zeichen, eine soge-
nannte Umwandlungsangabe. Eine Umwandlungsangabe beginnt mit % und endet mit
dem Umwandlungszeichen.
Beispiele: %d, %f usw.
So werden mit den Formatangaben die Datentypen der aktuellen Parameter, der Varia-
blen, für die Werte eingegeben werden können, festgelegt. Im Tastaturpuffer werden
eingegebene Zeichen zwischengespeichert.
Für den zweiten Parameter und die weiteren gilt folgende Schreibweise:
& <Bezeichner_aktueller_Parameter>
Da durch die Eingabe der Variablen ein neuer Wert zugewiesen wird, muss über die Ad-
resse (Adressoperator) der Zugriff auf den Speicherplatz der Variablen möglich sein.
(Später, wenn der Datentyp Zeiger/Pointer ausführlicher erläutert und für spezielle Auf-
gaben eingesetzt wird, werden Sie diesen Hinweis besser verstehen.).
Hinweis:
Ein typischer Anfängerfehler: scanf("%d", i); // falsch
Der Adressoperator wurde vergessen, das ist kein Syntaxfehler. Erst zur Laufzeit des
Programms entsteht ein Fehler, das Programm reagiert nicht mehr. Das ist fatal, das
Programm muss durch das Betriebssystem beendet werden.
Umwandlungsangabe Umwandlung
%d oder %i vorzeichenbehafteter Integer als Dezimalwert
%e, %f, %g Fließkommazahl
%c Zeichen
%o Integer als Oktalzahl einlesen
%s Zeichenkette einlesen
%x Hexadezimalwert
88 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
// Programm scanf.c
#include <stdio.h>
int main ( ) {
int i;
// Datentyp int, ganze Zahl
printf("Bitte geben Sie eine Zahl ein : ");
scanf("%d",&i);
// Wartet auf die Eingabe,
//die mit <enter> abgeschlossen wird.
printf("Die Variable hat den Wert %d\n",i);
//Ausgabe der ganzen Zahl
return 0;
}
Code 7.6: Quelltext mit Eingabe und Ausgabe einer ganzen Zahl
Die Bibliotheksfunktion printf() dient dazu, eine Zeichenkette (engl.: string) auf der
Standardausgabe auszugeben. In der Regel ist die Standardausgabe der Bildschirm. Der
erste formale Parameter, der Formatstring *format ist eine Folge von Zeichen, die als
konstante Zeichenfolge (zwischen " ") direkt im Programmcode angegeben wird.
In dieser Zeichenfolge können zwei Arten von Objekten vorkommen:
• druckbare Zeichen des ASCII-Codes, sie werden direkt in die Ausgabe kopiert, und
• Umwandlungsangaben.
Eine Umwandlungsangabe beginnt mit % und endet mit dem Umwandlungszeichen.
Es veranlasst die Umwandlung des zugeordneten Parameters in eine formatierte
Ausgabe. In Tab. 7.5, Tabelle der Formatbuchstaben für printf(), sind die mögli-
chen Formate aufgeführt:
Formatbuchstaben Beschreibung
d, i Integer-Zahl
u vorzeichenlose Integer-Zahl
c Zeichen, auch Leerzeichen
s Zeichenfolge ohne Leerzeichen
f reelle Zahlen mit Nachkommastellen (double)
e, E reelle Zahlen mit Exponentenschreibweise
p Zeigerwert
INM11 89
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
Hinweis:
Eine kontextsensitive Nebenbedingung ist beim Aufruf der Eingabe- und der Ausga-
befunktion zu beachten:
Die Anzahl der Umwandlungszeichen und der nach dem ersten folgenden weiteren
Parameter muss gleich sein.
Beispiel 7.7:
printf("Das %d. Feldelement hat den Wert\t %d\n", i, f[i]);
In der Zeichenfolge (Beispiel 7.7) sind zwischen „…“, in der Folge von Zeichen, zwei Um-
wandlungsangaben enthalten, je eine Integer-Zahl wird bei der Ausgabe auf dem Bild-
schirm in den Text eingefügt.
Es gilt die Zuordnung:
1. aktueller Parameter zum ersten Umwandlungszeichen und 2. aktueller Parameter
zum zweiten Umwandlungszeichen.
Zwischen dem Zeichen % und dem Umwandlungszeichen können noch zusätzliche op-
tionale Angaben gemacht werden, wie z. B. die Anzahl der dargestellten Dezimalstellen
vor und nach dem Dezimalpunkt einer Fließkommazahl.
Im Beispiel 7.7 werden mit %2d zur Ausgabe der Integer-Zahl zwei Stellen (Spalten auf
dem Raster des Bildschirms) genutzt.
Beispiel 7.8:
printf("Das %2d.Feldelement hat den Wert\t %d\n",i, f[i]);
In den Beispielen 7.7 und 7.8 kommen noch weitere Zeichen vor, es sind nicht-druckbare
Zeichen, sogenannte Backslash-Sequenzen.
Die Zeichenfolge "\t" fügt einen Tabulatorabstand in die Zeichenfolge ein.
Die Zeichenfolge "\n" fügt in die Zeichenfolge ein „new line“ (einen Sprung zum An-
fang der nächsten Zeile) ein.
Übung 7.1:
Testen Sie mit einem einfachen Programm die angegebenen Formatierungsmöglich-
keiten und schauen Sie sich die Effekte der Gestaltung von Ausgaben auf dem Bild-
schirm an.
90 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Programmiersprache C 7
Zusammenfassung
Die Syntax der Programmiersprache C ist durch die Menge der Grundsymbole (das Al-
phabet), die Syntaxregeln (Menge der Syntaxdiagramme) und die kontextsensitiven Ne-
benbedingungen vollständig beschrieben.
Der Aufbau eines einfachen C-Programms und die Erweiterung eines C-Programms
durch Module wurden erläutert. Die Modularisierung von Problemstellungen, das Zer-
legen von Aufgaben in Teilaufgaben, wird durch das vorgestellte Modulkonzept der Pro-
grammiersprache C zweckmäßig unterstützt.
Da der Datentyp Zeiger erst im Studienheft INM12 ausführlich behandelt wird, werden
nur Funktionen mit Wertparametern (engl.: call by value) implementiert.
Nach der Bearbeitung der Aufgaben zur Selbstüberprüfung sollten Sie in der Lage sein,
kleinere Problemstellungen durch das Bereitstellen und die Nutzung eigener Soft-
waremodule zu lösen.
7.1 Schreiben Sie ein C-Programm, das die Quadratzahlen zu den Zahlen 11 bis 25 be-
rechnet und auf dem Bildschirm anzeigt.
7.2 Schreiben Sie ein C-Programm, das x a für einen über die Tastatur eingegebe-
nen reellen Wert a mit der Genauigkeit eps 0.0005 berechnet.
Nutzen Sie für die Berechnung das folgende iterative Verfahren.
(a 1)
a) Das Iterationsverfahren beginnt mit dem Startwert x .
2
a
b) In jedem weiteren Iterationsschritt wird x 0.5 x gerechnet.
x
Das Verfahren wird abgebrochen, wenn eine ausreichende Genauigkeit erzielt ist,
d. h. wenn |(x · x) – a| eps ist.
Für die Berechnung des Betrags einer reellen Zahl w steht durch die include-Datei
math.h die Standardfunktion fabs(w) zur Verfügung.
7.3 Die in der Mathematik bekannte eulersche Zahl e 2,718 281 828 459 045 ... kann
als Grenzwert der folgenden unendlichen Reihe berechnet werden.
1 1 1 1 1
e mit k ℕ0 und 0! 1 ist definiert.
0! 1! 2! 3! k 0 k !
In einem C-Programm soll die eulersche Zahl mit einer vom Nutzer über die Tas-
tatur einzugebenden Anzahl (n, n ) von Reihengliedern berechnet werden. Tes-
ten Sie das Ergebnis der Rechnung für n 5, n 10, n 15 und n 20.
Beachten Sie die Ausgabe; man kann die Anzahl der Stellen nach dem Komma in
der Anweisung printf(...) anpassen.
Die Funktion fakul(…) muss auch modifiziert werden, der Typ des Ergebnisses
sollte double sein.
INM11 91
© HfB, 02.12.20, Berk, Eric (904709)
7 Programmiersprache C
xn
7.4 Schreiben Sie ein C-Programm, das den Ausdruck d für x 5.0 bis x 6.0
n!
mit der Schrittweite sw 0.1 tabelliert.
Die ganze Zahl n ist beliebig und wird über die Tastatur eingegeben. Nutzen Sie
die bereits erarbeiteten Funktionen.
92 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 93
© HfB, 02.12.20, Berk, Eric (904709)
2.3 Ein Jahr (A) ist kein Schaltjahr, wenn die Jahreszahl A nicht durch 4 teilbar ist.
Die durch 4 teilbaren Jahreszahlen, die keine Jahrhunderte sind (nicht teilbar
durch 100), sind Schaltjahre.
Jahrhunderte (teilbar durch 100) sind Schaltjahre, wenn sie auch durch 400 teil-
bar sind, andernfalls sind sie keine Schaltjahre.
Also 1972 ist ein Schaltjahr – teilbar durch 4.
1975 ist kein Schaltjahr – nicht durch 4 teilbar.
2000 ist ein Schaltjahr – ein Jahrhundert (teilbar durch 100) ist auch durch 400
teilbar.
1900 ist kein Schaltjahr – ein Jahrhundert (teilbar durch 100) ist nicht durch 400
teilbar.
Schrittfolge:
1. Eingabe A
2. Ist die Zahl A nicht durch 4 teilbar, dann ist es kein Schaltjahr,
andernfalls
Ist die Zahl nicht durch 100 teilbar, dann ist es ein Schaltjahr,
andernfalls
Ist die Zahl durch 400 teilbar, dann ist es ein Schaltjahr,
andernfalls ist es kein Schaltjahr.
3.1 Die beiden Datentypen short int werden mit 2 · 8 Bit intern dargestellt. Da jedes
Bit nur 0 oder 1 sein kann, sind 28 · 28 256 · 256 65536 verschiedene Bitfol-
gen möglich.
Das Dualsystem ist die Grundlage der Verschlüsselung der Zuordnung von Bit-
folge und Information. Vereinfachen wir uns die Betrachtung und prüfen nur
Bitfolgen/Dualzahlen mit 8 Bit.
Die Basis des Dualsystems ist 2, und die Stellenwerte sind Potenzen zur Basis 2,
2i mit i {0 … 7}.
Beispiele:
Das Bitmuster mit 8 Bit : 0000 0110 1 · 22 1 · 21 4 2 6dez
Das Bitmuster 0011 1010 1 · 25 1 · 24 1 · 23 1 · 21
32 16 8 2 58dez
94 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Codierung:
Dezimalzahl Bitmuster mit 8 Bit
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 1
2 0 0 0 0 0 0 1 0
3 0 0 0 0 0 0 1 1
4 0 0 0 0 0 1 0 0
… …
127 0 1 1 1 1 1 1 1
–128 1 0 0 0 0 0 0 0
–6 1 1 1 1 1 0 1 0
… …
–1 1 1 1 1 1 1 1 1
Analog für 16 Bit: 0 … 65535 oder –32767 … 0 … 32768 (siehe Tab. 3.1).
Die Beispiel-Bitfolge 0000 1010 1000 1101dual entspricht 2701dez.
3.2 In der Mathematik wurde bewiesen, dass zwischen zwei reellen Zahlen immer
noch eine weitere reelle Zahl liegt (am Beispiel 2 ). Der Wertebereich ist deshalb
nicht ordinal, in diesem Zahlenbereich ergeben diese Operationen keinen Sinn.
Der Datentyp float lässt nur Werte aus einem diskontinuierlichen Teilbereich der
reellen Zahlen zu. Der C-Standard z. B. trifft keine Festlegung, welcher Teilbe-
reich der reellen Zahlen durch den Datentyp float überdeckt wird.
Es muss damit gerechnet werden, dass verschiedene C-Systeme den Datentyp
float auch unterschiedlich realisieren. Häufig existiert der Datentyp darüber hi-
naus in verschiedenen Versionen.
3.3 Überlegungen:
Sei b 5 und pot 3, dann erg 53 5 · 5 · 5, dreimal die Basis b mit sich selbst
multiplizieren.
Sollte pot 0 sein, ist erg 1, also besser erg 1 · 5 · 5 · 5.
1. Aktion: Eingabe b und pot
2. Aktion: erg : 1, die Variable erg wird initialisiert.
3. Aktion: zaehl : 0
INM11 95
© HfB, 02.12.20, Berk, Eric (904709)
4. Aktion: Wiederhole solange zaehl pot die Aktionen 4.1 und 4.2:
4.1: Aktion: erg erg · b
4.2: Aktion: zaehl zaehl 1
5. Aktion: Ausgabe erg
Kontrolle am Beispiel durch Trockentest mit Werteverlaufsprotokoll:
3.4 Die beiden Zeichenfolgen „Der“ und „Zu“ sind zeichenweise rechnerintern mit
einem Byte (8 Bit) im ASCII-Code verschlüsselt:
Die Bitfolge 0100 0100 (dual) 44 (hexadezimal) 68 (dezimal) ,D‘
Die Bitfolge 0101 1010 (dual) 5A (hexadezimal) 90 (dezimal) ,Z‘
Der Test „Der“ „Zu“ liefert deshalb bereits beim Vergleich der ersten Buchsta-
ben das Ergebnis Ja, weil 68 90.
3.5 Angenommen die Angaben, die eine Bank von einem Kontoinhaber benötigt,
sind: Familienname (Name), Vorname und Geburtsdatum.
Sie vergibt eine Kontonummer, der Kontostand und eine Angabe zum Typ des
Kontos wird gespeichert, z. B. ‚Fest‘ oder ‚Giro‘.
Vom Datensatztyp datum ist die Komponente geboren, und die Komponente
wohnt ist ein Datensatztyp adresse. Beide müssen deklariert werden.
Der Datensatz Bankkonto enthält weiter folgende Angaben als Komponenten:
Name und Vorname sowie typ sind Felder, deren Elemente Zeichen sind.
Die Kontonummer ist eine vorzeichenlose ganze Zahl.
Der Kontostand ist eine gebrochene Zahl.
struct datum { int tag, monat, jahr; };
struct adresse { unsigned int PLZ; char strasse[15]; char
ort[20]; };
struct Bankkonto {
char Name[35], Vorname[15];
struct datum geboren;
struct adresse wohnt;
unsigned int Kontonummer;
float kontostand;
char typ[4];
};
96 INM11
© HfB, 02.12.20, Berk, Eric (904709)
4.1 In der Mathematik findet man die Aussage: Eine Zahl ist gerade, wenn sie ohne
Rest durch 2 teilbar ist. Die Teilbarkeit durch 2 wird mit der Operation MOD re-
alisiert, der Rest der Division wird ermittelt. Ist der Rest 0, dann handelt es sich
um eine gerade Zahl. Mit der Grundstruktur Alternative, 1 aus 2, werden die
zwei möglichen Ausgaben umgesetzt.
Eingabe: Zahl
Zahl MOD 2 = 0?
Ja Nein
Ausgabe: „Die Zahl ist gerade.“ Ausgabe: „Die Zahl nicht ist gerade.“
4.2 Die Quersumme einer positiven ganzen Zahl (zahl ) wird berechnet, indem die
Summe der einzelnen Ziffern ermittelt wird.
Wenn man eine ganze Dezimalzahl durch 10 dividiert, erhält man als Rest den
Wert der Ziffer mit dem kleinsten Stellenwert, und andererseits wird die Zahl
durch die Division durch 10 um eine Stelle verkürzt. Diese Operationen werden
wiederholt verwendet.
Beispiel: Dreistellige Zahl 129, die Berechnung erfolgt in folgenden Schritten:
Eingabe: zahl
zahl_h := zahl
summe := 0
INM11 97
© HfB, 02.12.20, Berk, Eric (904709)
4.3 Die Teiler (teiler ) einer ganzen Zahl (zahl ) ermittelt man, indem man nachein-
ander die Teilbarkeit durch die Zahlen 2 bis zahl lückenlos prüft. Die Operation
MOD wird verwendet. Ist der Rest der Division durch teiler 0, dann ist zahl
durch teiler teilbar.
Eingabe: zahl
4.4 Beispiel: Eine Dualzahl mit 8 Dualziffern sei gegeben: 0100 1101.
Die Dualzahl liegt als Feld mit n (hier 8) Elementen vom Datentyp int vor. Das
Element mit dem höchsten Index, also n (hier 8) hat den Stellenwert 20 1. Das
Element mit dem Index 1 hat den Stellenwert 2n – 1 (27 128).
Feldindex 1 2 3 4 5 6 7 8
dual[8]
0 1 0 0 1 1 0 1
(Dualzahl mit 8 Stellen)
Stellenwerte 27 26 25 24 23 22 21 20
dezi := 0
stellenwert := 1
Fuer i := n (–1) 1
stellenwert := stellenwert * 2
Berechnung: dezi 0 1 0 4 8 0 0 64 0 77
4.5 fakultaet_n(n, erg)
Input: n … Zahl
Output: erg … Ergebnis der Rechnung n!
Hilfsvariable i als Zähler
erg := 1
Fuer i := 1(1) n
erg := erg * i
98 INM11
© HfB, 02.12.20, Berk, Eric (904709)
ggT(x, y, teiler)
Input: zwei Zahlen x und y
{ Hilfsvariable r … Rest }
Output: teiler … größter gem. Teiler
r := x MOD y
x := y
y := r
Wiederhole bis r = 0
teiler := x
INM11 99
© HfB, 02.12.20, Berk, Eric (904709)
5.1 feld: 12 34 4 17 33 9
Durch wird die notwendige Tauschaktion zwischen zwei benachbarten
Elementen angezeigt.
Start: 12 34 4 17 33 9
1. Durchlauf 34 12 4 17 33 9
17 4 33 9
33 4 9
9 4
Ergebnis 1. Durchlauf 34 12 17 33 9 4
2. Durchlauf verkürztes Feld 34 12 17 33 9 4
17 12 33 9
33 12 9
Ergebnis 2. Durchlauf 34 17 33 12 9 4
3. Durchlauf verkürztes Feld 34 17 33 12 9 4
Ergebnis 3. Durchlauf 34 33 17 12 9 4
4. Durchlauf verkürztes Feld 34 33 17 12 9 4
Ergebnis 4. Durchlauf 34 33 17 12 9 4
Ende, kein Tausch 34 33 17 12 9 4
100 INM11
© HfB, 02.12.20, Berk, Eric (904709)
INM11 101
© HfB, 02.12.20, Berk, Eric (904709)
}
double fakul(int n){
int i;
double h = 1;
for(i = 1; i < n+1; i++)
h = h * i;
return h;
}
102 INM11
© HfB, 02.12.20, Berk, Eric (904709)
B. Literaturverzeichnis
Ernst, H. et al. (2015). Grundkurs Informatik. Grundlagen und Konzepte für die erfolg-
reiche IT-Praxis. Eine umfassende, praxisorientierte Einführung.
5. Aufl., ISBN 978-3-8348-0362-7, Heidelberg: Springer Vieweg.
Herold, H.; Lurz, B.; Wohlrab, J. (2012). Grundlagen der Informatik.
2. aktual. Aufl., ISBN 978-3-8632-6526-7, Pearson.
Louis, D. (2012). C++: Programmieren mit einfachen Beispielen.
1. Aufl., ISBN 978-3-8272-4770-4, Markt+Technik.
Nassi, I.; Shneiderman, B. (1973). Flowchart techniques for structured programming.
ACM SIGPLAN Notices.
Schneider, U. (Hrsg.) (2012). Taschenbuch Informatik.
ISBN 978-3-446-40754-1, 7. neu bearb. Aufl., Leipzig, München: Hanser.
Internetlinks
http://www.c-programmieren.com/C-Lernen.html#Einleitung
http://www.scoberlin.de/content/media/http/informatik/sthiemert/gesamt.pdf
https://de.wikipedia.org/wiki/C_(Programmiersprache)
Anmerkung:
Anregungen zur Gestaltung des Kapitels 6 ergaben sich aus der Lehrtätigkeit an der
TU Dresden, Fakultät Informatik, in der Professur „Grundlagen der Programmie-
rung“, Prof. Dr.-Ing. habil. Heiko Vogler.
Die Syntaxdiagramme des Lehrmaterials zu den Vorlesungen „Algorithmen und Da-
tenstrukturen“ und „Programmierung“ (Ausgabe 2012) wurden von uns übernom-
men, überarbeitet und für dieses Studienmaterial angepasst.
INM11 103
C. Grundstrukturen von Algorithmen C
Nassi-Shneiderman – Struktogramm Pseudocode Syntaxdiagramm von C
<bedingung>
Alternative
(20)
104
( BoolExpression (6) ) Statement (17) else Statement (17)
Falls <fallausdruck>
gleich <fall 1>, dann Aktion 1 SwitchStatement
<fallausdruck>
gleich <fall 2>, dann Aktion 2 (21)
switch ( Expression (6) ) {
sonst
<fall 1> <fall 2> … <fall n> gleich <fall n>, dann Aktion n
Aktion 1 Aktion 2 Aktion n Aktion ...
case CaseLabel (3) : StatementSequenz (16) BreakStatement (25)
sonst Aktion
Falls <fallausdruck>
<fallausdruck>
gleich <fall 1>, dann Aktion 1
Mehrfachauswahl
gleich <fall 2>, dann Aktion 2 default : StatementSequenz (16) }
INM11
Grundstrukturen von Algorithmen
© HfB, 02.12.20, Berk, Eric (904709)
Nassi-Shneiderman – Struktogramm Pseudocode Syntaxdiagramm von C
INM11
anfangsgeprüfte
So lange <bed> erfüllt,
tue WhileStatement
Wiederhole so lange <bed> Aktion / Aktionen. (22)
while ( BoolExpression (6) ) Statement (17)
<schleifenkoerper>
Der Schleifenkörper kann eine
Folge von Aktionen enthalten. In C hat die endgeprüfte Schleife folgenden Pseudocode:
tue
endgeprüfte Aktion
Tue solange <bed> erfüllt.
Aktion / Aktionen
Grundstrukturen von Algorithmen
Schleifenarten
Zählschleife Für den Zähler mit Anfangs-
wert (anfw) bis Endwert
Fuer <zaehlvar> := <anfw> (Sw) <endw> (endw) in der Schrittweise
(Sw) ForStatement
(24)
tue for ( Assignment1 (19) ; BoolExpression (6) ; Assignment1 (19) )
<schleifenkoerper>
Aktion / Aktionen
105
C
© HfB, 02.12.20, Berk, Eric (904709)
Program
(7)
GlobalDeclaration (8)
Import (42)
FormalParameter (30)
FunctionImplementation (28)
106 INM11
© HfB, 02.12.20, Berk, Eric (904709)
GlobalDeclaration
(8)
Declaration (9)
Functionspecificatation (27)
Declaration
(9)
ConstantDeclaration (10)
VariableDeclaration (12)
TypeDeclaration (13)
ConstantDeclaration
(10)
const TypeIdent (2) ConstDeclaration (11) ;
ConstantDeclaration
(11)
Ident (2) = ConstExpression (6)
VariableDeclaration
(12)
TypeIdent (2) Ident (2) ;
= ConstantExpression (7)
TypeDeclaration
(13)
EnumTypeDeclaration (35)
ArrayTypeDeclaration (14)
StructureTypeDeclaration (39)
ArrayTypeDeclaration
(14)
typedef TypeIdent (2) Ident (2) [ Number (4) ] ;
Block
(15)
{ Declaration (9) StatementSequence (16) ReturnStatement (32) }
INM11 107
© HfB, 02.12.20, Berk, Eric (904709)
StatementSequence
(16)
Statement (17)
Statement
(17)
Assignment (19)
IfStatement (20)
SwitchStatement (21)
WhileStatement (22)
DoWhileStatement (23)
ForStatement (24)
BreakStatement (25)
CompoundStatement (26)
FunctionCall (33)
ReturnStatement (32)
Assigment
(18)
Assignment1 (19) ;
Assigment1
(19)
Iden t (2) = Expression (6)
( TypeIdent (2) )
IfStatement
(20)
if ( BoolExpression (6) ) Statement (17) else Statement (17)
SwitchStatement
(21)
switch ( Expression (6) ) {
108 INM11
© HfB, 02.12.20, Berk, Eric (904709)
WhileStatement
(22)
while ( BoolExpression (6) ) Statement (17)
DoWhileStatement
(23)
do Statement (17) while ( BoolExpression (6) ) ;
ForStatement
(24)
for ( Assigment1 (19) ; BoolExpression (6) ; Assignment1 (19) )
Statement (17)
BreakStatement
(25)
break ;
CompoundStatement
(26)
{ StatementSequence (16) }
Declaration (9)
(27)
FunctionHeading (29) ;
FunctionImplementation (28)
FunctionImplementation
(28)
FunctionHeading (29) Block (15)
FunctionHeading
(29)
TypeIdent (2) Ident (2) ( FormalParameters (30) )
FormalParameters
(30)
ParamSection (31)
void
ParamSection
(31)
TypeIdent (2) Ident (2)
INM11 109
© HfB, 02.12.20, Berk, Eric (904709)
ReturnStatement
(32)
return Expression (6) ;
FunctionCall
(33)
FunctionIdent (2) ( ActualParameter (2) ) ;
EnumType
(34)
enum { element (5) } VarIdent (2) ;
TagName (2) , ,
EnumTypeDeclaration
(35)
typedef enum { element (5) } VarIdent (2) ;
TagName (2) ;
ArrayType
(36)
ElementType (2) Ident (2) [ Number (4) ] ;
RecordType
(37)
StructureType (38)
UnionType (41)
StructureType
(38)
struct { FieldList (39) ; } Ident (2) ;
TagName (5) ,
FieldList
(39)
TypeIdent (2) Ident (2)
,
StructureTypeDeclaration
(40)
typedef struct { FieldList (39) ; } Ident (2) ;
TagName (2)
110 INM11
© HfB, 02.12.20, Berk, Eric (904709)
UnionType
(41)
union { FieldList (39) ; } Ident (2) ;
TagName (2) ,
Import
(42)
#include < Ident (2) >
INM11 111
© HfB, 02.12.20, Berk, Eric (904709)
E. Abbildungsverzeichnis
INM11
112 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Abbildungsverzeichnis E
INM11 113
© HfB, 02.12.20, Berk, Eric (904709)
F. Tabellenverzeichnis
INM11
114 INM11
© HfB, 02.12.20, Berk, Eric (904709)
G. Sachwortverzeichnis
INM11
C F
Codierung ....................................... 24 Fallausdruck .................................... 36
Computer .......................................... 3 Fallunterscheidung .......................... 36
C-Programm .................................... 70 Feld ................................................. 26
Folge ......................................... 33, 35
D Formatangabe .................................. 88
Daten .............................................. 20 Formatbuchstabe ............................. 89
Programmdaten, Funktion .......................................... 70
Verarbeitungsdaten ....................... 3 Funktion, Aufruf .............................. 86
Datenobjekte ................................... 20 Funktionsdefinition .................... 72, 83
Datensatz ........................................ 27 Funktionsdeklaration ....................... 83
Datenstruktur ............................. 22, 26 Funktionsimplementation ................ 83
dynamische ................................. 29 Funktionskopf .................................. 84
statische ...................................... 26
INM11 115
© HfB, 02.12.20, Berk, Eric (904709)
G Sachwortverzeichnis
G N
Gleitkommaarithmetik ..................... 24 Nassi und Shneiderman .................... 32
Gleitkommazahlen ........................... 24 Nebenbedingung, kontextsensitiver .. 81
Grundsymbol ................................... 73
Grundsymbol von C ......................... 59 O
Operatoren ...................................... 74
H Ordnungsrelation ............................. 52
Hauptprogramm .............................. 70
P
I Parameter
Index ............................................... 26 aktueller ...................................... 43
Indirektionsoperator ........................ 29 formaler ...................................... 43
Information ....................................... 3 Pointer ............................................. 29
Informationsverarbeitung ................... 3 Programm .................................... 4, 11
Informationsverarbeitung, ausführbares ............................... 65
computergestützte ......................... 3 Programmiersprache ........................ 55
Interpreter ................................. 65, 66 deklarative .................................. 55
Iteration ........................................... 38 höhere problemorientierte ........... 55
imperative ................................... 56
K künstliche Sprache ...................... 55
Klassifikation der Datentypen .......... 21 maschinennah ............................. 55
Kommentar ...................................... 74 objektorientierte .......................... 56
Kompiler .................................... 65, 66 prozedurale ........................... 55, 56
Komponenten .................................. 27 Programmierumgebung ...................... 7
Konstante DEV-C++ ...................................... 8
syntaktische ................................ 60 Prototypen ....................................... 72
Korrektheit Prozedur .......................................... 55
syntaktische ................................ 62 Punkt-Operator ................................ 28
Künstliche Sprache .......................... 56
Q
L Quelltext .......................................... 65
Laufbereich ...................................... 38
Leibniz-Reihe ............................. 43, 44 R
Linker ........................................ 65, 66 Referenz-Operator ............................ 29
M S
Mehrfachauswahl ............................ 77 Schleife ............................................ 38
Modul .............................................. 42 anfangsgeprüfte ........................... 38
Modularisierung .............................. 42 endgeprüfte ................................. 39
Modulaufruf .................................... 43 geschachtelte ............................... 41
Moduldefinition ............................... 43 Schlüsselwort ................................... 73
Modulkonzept .................................. 29 Schnittstelle ..................................... 42
Selektion .......................................... 36
Semantik .......................................... 57
Sequenz ........................................... 35
116 INM11
© HfB, 02.12.20, Berk, Eric (904709)
Sachwortverzeichnis G
Software ............................................ 4 Z
Softwareentwicklungsumgebung, Zählschleife .......................... 38, 40, 85
integrierte ................................... 65 Zeichen, druckbares ......................... 89
Sortiervorgang ................................. 51 Zeiger .............................................. 22
Sprache, formale .............................. 57 Zeigervariable .................................. 29
Standardalgorithmen ....................... 16 Zugriff ............................................. 28
Standarddatentypen .................... 21, 25 Zugriff, wahlfrei .............................. 26
Startdiagramm ................................. 59 Zuweisung ....................................... 33
Startsymbol ..................................... 59 Zyklus ............................................. 38
Stern-Menge .................................... 57
Struktogramm ................................. 32
Grundstruktur, Aktion ................ 33
Syntax ........................................ 57, 58
syntaxbasierten Editor ..................... 78
Syntaxdiagramme ............................ 58
Syntaxdiagramme zu C .................... 76
Syntaxregeln .................................... 57
T
Top-down-Prinzip ............................ 42
Trennzeichen ................................... 74
Trockentest, Werteverlaufsprotokoll . 41
U
Umwandlungsangaben .................... 89
V
Variable ........................................... 22
syntaktische ................................ 60
Variable, syntaktische
Hilfssymbol ................................ 59
Verarbeitung ...................................... 3
Verbund ........................................... 27
W
Weg, legaler ..................................... 60
Wertebereich ........................ 22, 23, 24
Wiederholung ............................. 33, 38
INM11 117
© HfB, 02.12.20, Berk, Eric (904709)
118 INM11
© HfB, 02.12.20, Berk, Eric (904709)
H. Einsendeaufgabe Typ A
Grundlagen der Algorithmierung und der Programmierung Teil 1 Einsendeaufgabencode:
INM11-XX1-K03
1. Entwerfen Sie einen Algorithmus „Sparvertrag“ und stellen Sie diesen als Strukto-
gramm dar.
Der Wert des Endkapitals E aus einem Sparvertrag mit folgenden Konditionen soll
ermittelt und ausgegeben werden:
• Ein Anfangskapital A wird eingezahlt,
• die Bank gewährt einen konstanten Zinssatz zins,
• das Kapital wird am Ende eines jeden Jahres durch eine konstante Ratenzahlung
Rate erhöht,
• die Laufzeit des Sparvertrags beträgt n Jahre.
30 Pkt.
2. Entwerfen Sie eine Moduldefinition für das Modul „TagNr“.
Aus einem an das Modul übergebenen Datum (tag, monat, jahr) soll ermittelt wer-
den, der wievielte Tag des betreffenden Jahres dieser Tag ist. Berücksichtigen Sie da-
bei, dass das betreffende Jahr ein Schaltjahr sein kann.
30 Pkt.
3. Schreiben Sie ein C-Programm. In diesem Programm sollen folgende drei Aufgaben
in der angegebenen Reihenfolge gelöst werden:
• Ein Feld A mit n (z. B. n 12) ganzen Zahlen wird über die Tastatur eingegeben.
• Das komplette Feld A wird zur Kontrolle auf dem Bildschirm ausgegeben.
• Das Maximum des Feldes A (max) und seine Position (pos) im Feld werden er-
mittelt und auf dem Bildschirm ausgegeben.
40 Pkt.
INM11 119
© HfB, 02.12.20, Berk, Eric (904709)
$
Studienheft
INM12
$
INM12
Werden Personenbezeichnungen aus Gründen der besseren Lesbarkeit nur in der männlichen oder
weiblichen Form verwendet, so schließt dies das jeweils andere Geschlecht mit ein.
Falls wir in unseren Studienheften auf Seiten im Internet verweisen, haben wir diese nach sorgfältigen
Erwägungen ausgewählt. Auf die zukünftige Gestaltung und den Inhalt der Seiten haben wir jedoch
keinen Einfluss. Wir distanzieren uns daher ausdrücklich von diesen Seiten, soweit darin rechtswid-
rige, insbesondere jugendgefährdende oder verfassungsfeindliche Inhalte zutage treten sollten.
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
0619K03
Vorwort ....................................................................................................................... 1
INM12
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
Anhang
A. Lösungen der Aufgaben zur Selbstüberprüfung ....................................... 93
B. Literaturverzeichnis ..................................................................................... 110
C. Abbildungsverzeichnis ................................................................................ 111
D. Quellcodeverzeichnis ................................................................................... 112
E. Einsendeaufgabe Typ A .............................................................................. 115
INM12
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
INM12Grundlagen der Algorithmierung und Programmierung Teil 20619K03
INM12 1
© HfB, 02.12.20, Berk, Eric (904709)
2 INM12
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Die hervorgehobene Stellung der Programmiersprache C (bzw. C++) bei Anwen-
dern beruht unter anderem auf der Existenz von Sprachmitteln zur systemnahen
Programmierung. In C wird sehr effektiv und nutzerfreundlich der Zugriff auf
Bits und Bytes oder Adressen des Hauptspeichers unterstützt. Es ist möglich,
nicht nur mit den Daten selbst, sondern auch mit Verweisen auf diese zu arbeiten.
Die Grundlagen für ein solches Arbeiten werden durch den Datentyp Zeiger
bereitgestellt.
Mit diesem Ansatz besteht die Möglichkeit, auf zwei Wegen auf eine Variable
zuzugreifen, zum einen „klassisch“ über die Angabe des Variablennamens (Be-
zeichner) und zum anderen über die Adresse dieser Variablen, d. h. über die
Angabe der Abspeicherungsposition im Speicher.
Viele Algorithmen lassen sich unter Nutzung des Zeigerkonzeptes laufzeiteffi-
zienter implementieren. Mithilfe von Zeigern können komplizierte Datenstruktu-
ren einfach aufgebaut werden. Strukturen, die sich dynamisch ändern, lassen
sich ausschließlich mit dieser Technik realisieren.
Das kommende Kapitel greift die im Studienheft INM11 vermittelten Sprach-
grundlagen auf und behandelt die Grundprinzipien zur Arbeit mit Zeigern in
verschiedenen typischen Anwendungsfällen.
Nach dem Durcharbeiten dieses Kapitels werden Sie in der Lage sein, diese
Kenntnisse im Rahmen kleiner Projekte umzusetzen.
Beispiel 1.1:
swap (x,y)
input/output x,y
{vertauscht x und y mithilfe von hilf}
hilf:=x
x:= y
y:= hilf
INM12 3
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Basierend auf den Kenntnissen des Abschnitts „Das Modulkonzept von C – Funk-
tionen in C“ aus INM11 soll diese Funktion swap implementiert und in einer main-
Funktion genutzt werden. Dafür sind die Schritte Deklaration, Definition und Auf-
ruf der Funktion umzusetzen. Daraus ergibt sich der folgende Quelltext:
1 // Programm Tauschen.c
2 #include<stdio.h>
3 void swap (int x, int y);
4
5 int main(){
6 int zahl1=10,zahl2=20;
7 printf("Werte vor Aufruf von swap:\n");
8 printf("zahl1: %d, zahl2: %d\n",zahl1,zahl2);
9 swap(zahl1,zahl2);
10 printf("Werte nach Aufruf von swap:\n");
11 printf("zahl1: %d, zahl2: %d\n",zahl1,zahl2);
12
13 return 0;
14 }
15
16 void swap (int x, int y){
17 int hilf;
18 hilf=x;
19 x=y;
20 y=hilf;
21 }
Erläuterung:
Zeile 3: Vor Nutzung der Funktion wird diese durch die Deklarationsan-
weisung dem Compiler bekannt gemacht. Neben dem kompletten
Funktionskopf ist es auch möglich, nur die entsprechenden Typen
anzugeben, da der Compiler keine Bezeichner überprüft.
Zeilen 5–14: Die Verwendung der Funktion erfolgt in der main-Routine. In
Zeile 6 werden die Ganzzahlvariablen zahl1 und zahl2 ver-
einbart und gleichzeitig initialisiert. Vor und nach dem Aufruf der
Funktion swap werden jeweils die Variablenwerte protokolliert.
Zeilen 16–21: Hier erfolgt die Definition der Funktion swap. Als Eingabegrößen
finden die beiden formalen Parameter x und y vom Typ int
Verwendung. In Zeile 18 wird der lokale Bezeichner hilf bereit-
gestellt. Mit diesen drei Variablen erfolgt das Tauschen der Variab-
lenwerte.
Betrachtet man die Ausgabe des Programms, erkennt man jedoch, dass keine
Werteveränderung erfolgt.
Ausgabe:
Werte vor Aufruf von swap:
zahl1: 10, zahl2: 20
Werte nach Aufruf von swap:
zahl1: 10, zahl2: 20
4 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Datentyp Zeiger 1
Die Erklärung ergibt sich aus der in C festgelegten Art der Parameterübergabe.
Variable vom einfachen Datentyp werden als Parameter mit dem Prinzip „call by value“
übergeben (es wird mit einer Kopie der Variablen gearbeitet). Die Funktion kann nur le-
send, aber nicht schreibend auf solche Parameter zugreifen und nur ein Ergebnis über
den Funktionsnamen mithilfe der return-Anweisung zurückgeben. Diese Rückgabe-
funktionalität fehlt im dargestellten Beispiel, da sie hier nicht geeignet ist. Der Rückga-
betyp einer solchen Funktion, die, wie in unserem Beispiel keinen Wert zurückgibt, ist
daher mit void (leer) anzugeben.
Die Werte der aktuellen Parameter zahl1 und zahl2 werden bei der Übergabe an
die Funktion swap in die formalen Parameter x und y kopiert und nur innerhalb
von swap vertauscht. Es erfolgt keine Rückgabe von Ergebnissen an die aufrufende
Programmeinheit main.
Um dieses Problem zu lösen, muss die Parameterübergabe an die Funktion nach dem
auch in C bereitstehenden Prinzip „call by reference“ erfolgen. Man arbeitet hier mit
Adressen und Zeigern.
Neben der Übergabe von Adressen von Datenobjekten an Funktionen werden Zeiger in
C zur Übergabe von Funktionen als Argument für andere Funktionen, zur Arbeit mit
Feldern, zur dynamischen Verwaltung von Speicherbereichen sowie zum Aufbau kom-
plexer Datenstrukturen genutzt.
Viele Programme in C und Klassen in C++ sind ohne Zeiger nicht umsetzbar. Allerdings
ist fehlerhafte Zeigerprogrammierung auch ein häufiger Grund für Programmierfehler.
Stellen wir daher die exakte Zeigernutzung in den Mittelpunkt der folgenden Betrach-
tungen.
INM12 5
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Sie erinnern sich: Um Zeiger zu benutzen, werden folgende drei Schritte notwendig:
Die folgenden Quelltexte fassen diese Prinzipien nochmals zusammen. Die notwendigen
Programmierschritte sind jeweils als Kommentar angefügt:
Beispiel 1.2:
1 //Programm Zeiger.c
2 #include<stdio.h>
3
4 int main(){
5 int var;
6 int *ptr;
7 var=121;
8 ptr=&var;
9 printf("%i\n",*ptr);
10 return 0;
11 }
Erläuterung:
Zeile 5: Definition der Variablen var vom Typ int.
Zeile 6: Definition des Zeiger ptr vom Typ int*. Als Werte können Adressen
von int-Variablen zugewiesen werden.
Zeile 8: Der Zeiger ptr referenziert die Variable var. Er erhält als Wert die
Adresse der Variablen var zugewiesen. Dazu wird der Adressoperator (&)
genutzt.
6 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Datentyp Zeiger 1
Zeile 9: Der Wert der Variablen, die im Adressbereich steht, auf die der Zeiger ptr
zeigt, wird ausgegeben. Dazu ist der Dereferenz-Operator (*) anzuwenden.
Die Programmiersprache C bietet ein sehr flexibles Zeigerkonzept. Unter anderem steht
auch die Konstruktion Zeiger auf Zeiger zur Verfügung. Abb. 1.3 illustriert das
Beispiel Zeiger.c in grafischer Form. Eine solche Konstruktion wird u. a. benutzt, um
Parameter an die main-Funktion zu übergeben.
Beispiel 1.3:
ptr Zeiger
var Variable
121 Wert
1 //Programm Zeiger_auf_Zeiger.c
2 #include<stdio.h>
3
4 int main(){
5 int var;
6 int *ptr;
7 int **pptr;
8 var=121;
9 ptr=&var;
10 pptr=&ptr;
11 printf("%i\n", **pptr);
12 return 0;
13 }
Erläuterung:
Zeile 5: Definition der Variablen var vom Typ int.
Zeile 6: Definition des Zeiger ptr vom Typ int*. Als Werte können Adressen
von int-Variablen zugewiesen werden.
Zeile 7: Definition des Zeiger pptr vom Typ int**. Als Werte können Adressen
vom Typ int* zugewiesen werden.
INM12 7
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Zeile 9: Der Zeiger ptr referenziert die Variable var. Er erhält als Wert die
Adresse der Variablen var zugewiesen.
Zeile 10: Der Zeiger pptr referenziert die Zeiger-Variable ptr. Er erhält als Wert
die Adresse der Variablen ptr zugewiesen.
Zeile 11: Der Wert der Variablen, die im Adressbereich steht, auf die der
Zeiger pptr zeigt, wird ausgegeben. Dazu ist der Dereferenz-Operator (*)
zweifach anzuwenden.
Achtung:
Wird im Programm ein Zeiger verwendet, der zuvor nicht initialisiert wurde, kann
dies zu schwerwiegenden Fehlern führen. Deshalb muss ein Zeiger immer erst posi-
tioniert werden (im Beispiel: ptr=&var;), bevor auf die Speicheradresse zugegrif-
fen werden kann (z. B. mit *ptr), um den damit adressierten Wert zu benutzen.
Beispiel 1.4:
Das folgende Beispiel soll dazu dienen, den beschriebenen Formalismus der Zeiger-
nutzung weiter zu festigen.
Zwei int-Variable, zwei char-Variable und jeweils pro Datentyp eine Zeigervari-
able werden betrachtet.
Zum besseren Verständnis des Algorithmus erfolgt eine Werteprotokollierung in
einer Durchlauftabelle. Zusätzlich steht das Ausgabebild bereit.
// Programm Zeigernutzung.c
#include <stdio.h>
int main(){
int int_z1, int_z2, *zeig_int;
char bu_1, bu_2, *zeig_bu;
int_z1=7;
int_z2=-2;
bu_1='c';
zeig_int=&int_z2;
int_z2+=*zeig_int+1;
printf("%i\n",*zeig_int);
zeig_bu=&bu_1;
bu_2=*zeig_bu;
printf("%c\n",bu_2);
*zeig_int=*zeig_int+int_z1;
printf("%i\n",*zeig_int);
int_z2=int_z1* *zeig_int;
printf("%i\n",int_z2);
(*zeig_int)++;
printf("%i\n",*zeig_int);
return 0;
}
8 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Datentyp Zeiger 1
Mit int_z1=7; int_z2=-2; bu_1='c'; erhalten diese drei Variablen ihre Anfangs-
wertbelegung.
Mit zeig_int=&int_z2; wird mit dem Zeiger zeig_int die Variable int_z2
referenziert.
Durch int_z2+=*zeig_int+1 wird ein neuer Variablenwert berechnet. Die zusam-
mengesetzte Zuweisung lässt sich in int_z2=int_z2+(*zeig_int+1) auflösen. Da-
raus ergibt sich der Wert der rechten Seite der Zuweisung aus der Summe aus dem Wert
der Variablen, auf die zeig_int verweist (*zeig_int=-2), addiert um 1, und dem
alten Wert der Variablen int_z2 selbst. Das Ergebnis ist – 3 (int_z2=-2+(-2+1))
und wird in einer neuen Zeile ausgegeben.
In den nächsten Anweisungen werden die Zeichen verarbeitet.
Mit zeig_bu=&bu_1; wird der Zeiger zeig_bu initialisiert. Der Wert der dazu-
gehörigen Variablen (bu_1='c') wird der Variablen bu_2 zugewiesen.
(bu_2=*zeig_bu;). Zur Ausgabe kommt auch hier nach Zeilenschaltung, aus der
vorangegangenen Ausgabeanweisung, das Zeichen 'c'.
Im letzten Teil werden wieder Ganzzahlvariable bearbeitet. Der Inhalt der Speicherzelle,
auf die der Zeiger zeig_int verweist (Inhalt: *zeig_int), wird aus dessen altem
Wert, addiert mit dem Variablenwert int_z1, neu berechnet. Im Ergebnis entsteht 4.
Es erfolgt die Ausgabe dieses Wertes.
Weiter wird nun der Wert der Variablen int_z2 neu belegt. Dieser entsteht aus dem
Produkt des Wertes von int_z1 und dem Wert, der sich aus dem Dereferenzieren des
Zeigers zeig_int ergibt (Dereferenzieren: *zeig_int). Als Ergebnis erhält
man int_z2=7*4=28. Dieser Wert wird wiederum ausgegeben.
Die letzte Ausgabe wird durch die postfix verwendete Addition auf den Inhalt der von
zeig_int adressierten Speicherzelle (Inhalt: *zeig_int) bestimmt. Dieser Wert er-
gibt sich zu 29.
Um diesen für den C-Neuling relativ komplizierten Sachverhalt effektiv darzustellen,
wählen wir das Mittel der Durchlauftabelle. Beim Erstellen einer Durchlauftabelle wird
das Programm entsprechend den Wertebelegungen „Anweisung für Anweisung abgear-
beitet“. Dabei erfolgt bei einer Veränderung der Werte der Variablen ein Protokollein-
trag.
Werteverhalten:
7 -2 c c int_z2 bu_1
-3
4
28
29
INM12 9
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Ausgabe:
-3
c
4
28
29
Zusammenfassung:
Ist ptr ein Zeiger und var eine Variable und zeigt ptr auf var und ptr reprä-
sentiert eine gültige Adresse, so ist *ptr wie var selbst zu verwenden (*ptr ist die
Variable, die ptr referenziert).
Beispiel 1.5:
Im Beispiel werden zwei Zeiger initialisiert. zeig_index wird initialisiert mit der
Adresse von index. zeig_wert erhält den gerade besprochenen NULL-Zeiger zu-
gewiesen.
Sind beide Zeigerwerte nicht gleich, was im vorliegenden Fall gilt, wird dem
Zeiger zeig_wert der Zeiger zeig_index zugewiesen. Das bedeutet, beide Zei-
ger enthalten die gleiche Adresse, hier die der Variablen index, und repräsentieren
den gleichen Inhalt.
Bei der Zuweisung von Zeigern und deren Vergleich sollten sich diese auf Objekte
des gleichen Datentyps beziehen, da sonst Adressfehler zu befürchten sind.
// Programm Zeigervergleich.c
#include <stdio.h>
int main(){
int index;
int *zeig_index=&index;
int *zeig_wert=NULL;
10 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Datentyp Zeiger 1
Ausgabe:
Werte: 15 15
Adressen: 0028FEA4 0028FEA4
1.3.3 Adressrechnungen
Zeiger werden systemintern wie unsigned-Variablen dargestellt und besitzen entspre-
chend ihrem referenzierten Datentyp ein Längenattribut.
Die Adressarithmetik gestattet:
• Addition ganzer Zahlen zum Zeigerwert:
Der Zeiger wird um eine ganze Zahl von Bytes, entsprechend dem Datentyp, vor-
wärtsgesetzt.
• Subtraktion ganzer Zahlen vom Zeigerwert:
Der Zeiger wird um eine ganze Zahl von Bytes, entsprechend dem Datentyp, zu-
rückgesetzt.
• Subtraktion von zwei Zeigern:
Die Differenz zweier Zeiger entspricht der Anzahl von Bytes, bezogen auf den
Datentyp, die sich zwischen beiden Zeigern befinden.
• Vergleich von zwei Zeigern:
Es kann festgestellt werden, ob zwei Zeiger identisch sind (gleiche Größe = gleicher
Datentyp) oder ob ein Zeiger größer oder kleiner ist als der andere.
• Explizite Zeigerumwandlungen:
Zur Absicherung einer korrekten Adressrechnung sollte die explizite Zeigerum-
wandlung wie in der folgenden Art benutzt werden:
p_char = (char *)p_int;
Obwohl es in C möglich ist, Größen vom Typ char über den Platzhalter %c als
Zeichen und über den Platzhalter %i als Zahlenwert entsprechend ihrer Stellung in
der ASCII-Tabelle auszugeben, führt eine Zuweisung der Speicherplatzadresse an
eine Zeigervariable des anderen Types (p_char=p_int) in der Regel zu einem
Fehler:
error C2440: '=': 'int *' kann nicht in 'char *' konvertiert
werden
INM12 11
© HfB, 02.12.20, Berk, Eric (904709)
1 Datentyp Zeiger
Zuerst werden die drei Zeiger pi, pd und pc deklariert. Bei einer typischen Speicher-
breite (s. INM01, Kapitel 2) von 4 Byte für int und 8 Byte bei double bedeutet dies:
Mit pi+5 wird um 5 · 4 Byte, bei pd 3 · 8 Byte im Speicher vorwärtsgegangen. Für pc
erfolgt ein Rückwärtsgehen des Zeigers um 4 Byte.
pi++ bedeutet ein Vorwärtsgehen um 4 Byte zur nächsten int-Adresse (Inkrementie-
ren), pc++ das Vorwärtsgehen um 1 Byte zur nächsten char-Adresse und pd-- ein
Rückwärtsgehen (Dekrementieren) um 8 Byte zur voranliegenden double-Adresse.
Die Adressarithmetik gestattet nicht:
• die Addition von zwei Zeigern,
• eine Addition oder Subtraktion von float- oder double-Werten zum Zeigerwert,
• die Anwendung von Multiplikation und Division auf Zeiger.
Zusammenfassung
Der Datentyp Zeiger spielt in der Programmiersprache C eine zentrale Rolle. Er er-
möglicht einerseits die Laufzeiteffektivierung von Programmen, kann aber auch bei un-
sauberer, nicht sorgfältiger Programmierung zu Problemen mit fehlerhaftem Pro-
grammcode führen.
Nachdem Sie in diesem Kapitel die grundlegenden Elemente der Zeigernutzung kennen-
gelernt haben, werden Sie in den folgenden Lerneinheiten typische Anwendungsfälle
bei der praktischen Nutzung von Zeigern kennenlernen.
12 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Datentyp Zeiger 1
1.2 Schreiben Sie ein Programm, das zwei float-Werte einliest, diese addiert und das
Ergebnis ausgibt.
Dabei sollen für die Formulierung der Addition als Operanden keine einfachen
Variablen, sondern Zeiger verwendet werden!
1.3 Erkennen Sie den schwerwiegenden Fehler im folgenden Programm:
//fehlerhaft
#include <stdio.h>
int main(){
int int_zahl1,int_zahl2,*int_zeiger;
int_zahl1=12345;
*int_zeiger=int_zahl1;
int_zahl2=*int_zeiger;
printf("\n%i %i",int_zahl1, int_zahl2);
return 0;
}
INM12 13
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 2.1:
//Programm Tauschen_mit_Zeigern.c
//Parameteruebergabe mit call by reference
#include <stdio.h>
void swap (int *a,int *b);
int main(){
int zahl1,zahl2;
zahl1=10;
zahl2=20;
printf("\nAusgangswerte: %2i\t%2i",zahl1,zahl2);
swap(&zahl1,&zahl2);
printf("\nErgebniswerte: %2i\t%2i",zahl1,zahl2);
return 0;
}
14 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Der Prototyp void swap(int *a,int *b); zeigt bereits an, dass Argumente der
Funktion vom Typ "Zeiger auf int" verwendet werden. Der Aufruf der Funk-
tion erfolgt mit den Adressen der Funktionsargumente swap(&zahl1,
&zahl2);. Das hat zur Folge, dass main und swap auf die gleichen Speicher-
plätze zugreifen.
Betrachtet man die Definition der Funktion und deren Aufruf, erkennt man die gül-
tige Initialisierung von Zeigern, die hier mithilfe des Adressoperators erfolgt.
Aufruf: swap(&zahl1,&zahl2);
Zusammenfassung:
Sollen durch eine C-Funktion mehr als ein Ergebnis an die aufrufende Programmeinheit
zurückgegeben werden, sind Zeiger als Funktionsparameter zu benutzen.
Eine weitere Anwendungsmöglichkeit, einen Zeiger als Parameter zu nutzen, besteht
dann, wenn größere Datenmengen übergeben werden sollen. Hier ist der Zeigereinsatz
unumgänglich. Auf diesen Aspekt kommen wir später zurück.
INM12 15
© HfB, 02.12.20, Berk, Eric (904709)
Neben der Rekursionsvorschrift, die den rekursiven Aufruf steuert, muss auch eine
Abbruchbedingung zum Rekursionsende führen, sodass der Algorithmus terminiert.
Beispiel 2.2:
Ein klassisches Beispiel für die Programmierung der Rekursion ist sicherlich bei der
Berechnung der Fakultät n! = 1 * 2 * 3 * ... * n gegeben (s. INM11, Beispiel
in Kapitel 6).
Nach dem Wesen der rekursiven Nutzung wird die Funktion fakul im Algorith-
mus in sich selbst wieder aufgerufen. Als Ergebnis-Typ wurde long vereinbart.
Der Wert 12 dient als Ausgangsgröße für eine Beispielrechnung.
Die Rekursion lässt sich entsprechend dem in Abb. 2.2 dargestellten Schema ver-
folgen. Dabei wird für eine natürliche Zahl n deren Fakultätsberechnung nach der
Formel n! = n * (n-1)! realisiert. Zur Berechnung von (n-1)! dient die glei-
che Funktion fakul, die auch schon bei der Berechnung von n! verwendet wurde.
Am Ende der Rekursionskette steht 0!=1.
fakul(12)=
12*fakul(11)=
12*11*fakul(10)=
. . . =
12*11*10*9* . . . *2*fakul(1)=
12*11*10*9* . . . *2*1*fakul(0) mit fakul(0)=1
1 // Programm Fakultaet_1.c
2 #include <stdio.h>
3 long fakul(int zahl);
4
5 int main(){
6 int wert;
7 printf("\nFakultaetsberechnung fuer den Wert ");
8 scanf("%d",&wert);
9 printf("\nErgebnis: %d! = %ld",wert,fakul(wert));
10 return 0;
11 }
12
13 long fakul(int zahl){
14 long ergeb;
15 if(zahl>0)
16 ergeb=zahl*fakul(zahl-1);
17 else
18 ergeb=1;
19 return(ergeb);
20 }
21
16 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Erläuterung:
Zeilen 3, 12: Als Ergebnistyp der Funktion fakul wurde long int,
kurz long, festgelegt.
Zeilen 14, 15: Für Werte der Art zahl>0 erfolgt der Rekursionsaufruf.
Zeilen 16, 17: Für den Wert zahl=0 wird das Rekursionsende erreicht.
Beispiel 2.3:
Um den Mechanismus zwischen rekursivem Aufruf und Wertebereitstellung detail-
lierter darstellen zu können, wurden im Code 2.2 der Funktion fakul am Beginn
und Ende des Quelltextes Protokollausgaben hinzugefügt. In diesem Zusammen-
hang wurde die zur Funktion fakul lokale Variable ergeb zusätzlich definiert.
// Programm Fakultaet_2.c
#include <stdio.h>
long fakul(int zahl);
int main(){
int wert;
printf("\nFakultaetsberechnung fuer den Wert ");
scanf("%d",&wert);
printf("\nErgebnis: %d! = %ld",wert,fakul(wert));
return 0;
}
INM12 17
© HfB, 02.12.20, Berk, Eric (904709)
Ergebnis: 6! = 720
Achtung:
Die Arbeit mit globalen Variablen birgt Gefahren in sich. Bei großen Programmen
bestehen die Gefahr von Unübersichtlichkeit und die Möglichkeit der ungewollten Ver-
änderung der Variablenwerte. Im Regelfall sollte man auf ihre Nutzung verzichten.
Beispiel 2.4:
Zur Umsetzung des bereits bekannten Vertauschungsalgorithmus werden die zwei
Variablen zahl1 und zahl2 global vereinbart. Die Parameterübergabe für die
Funktion ztausch entfällt, wie auch eine Ergebnisrückgabe. Der zu vereinbarende
Prototyp lautet daher: void ztausch (void).
Die Funktionen main und ztausch greifen auf die gleichen Speicherzellen
für zahl1 und zahl2 zu.
1 //Programm globale_Variable.c
2 #include <stdio.h>
3
4 void ztausch (void);
5 int zahl1,zahl2;
6
7 int main(){
8 zahl1=10;
9 zahl2=20;
10 printf("\nAusgangswerte: %i\t%i",zahl1,zahl2);
11 ztausch();
12 printf("\nErgebniswerte: %i%9i",zahl1,zahl2);
13 return 0;
14 }
15
18 INM12
© HfB, 02.12.20, Berk, Eric (904709)
16 void ztausch(void){
17 int vhilf;
18 vhilf=zahl1;
19 zahl1=zahl2;
20 zahl2=vhilf;
21 }
Erläuterung:
Zeile 5: Globale Vereinbarung der Bezeichner zahl1 und zahl2.
Zeile 10: Der Abstand beider Ausgabewerte wird durch den Horizontaltabulator
(Backslash-Sequenz \t) gesteuert.
Zeile 12: Die Anordnung des zweiten Wertes erfolgt durch die Veränderung der
Feldbreite (%9i).
Ausgabe:
Ausgangswerte: 10 20
Ergebniswerte: 20 10
Dabei bedeuten:
int argc Anzahl der Parameter (mindestens einer) aus Zeigern auf Zeichen-
ketten
char *argv[ ] Zeiger auf ein Feld aus Zeigern auf Zeichen (d. h. ein Feld aus Zeigern
auf Zeichenketten)
Das erste vom Betriebssystem gelieferte Argument argc ist eine ganze Zahl, die der
Anzahl der gelieferten Kommandozeilenargumente entspricht. Dabei ist der Wert im-
mer um eins größer als die Anzahl der Argumente, da der erste Parameter immer den
Programmnamen enthält.
INM12 19
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 2.5:
Das vorliegende Beispielprogramm listet die übergebenen Argumente zeilenweise
aus.
//Programm Kommandozeilenargumente_1.c
#include <stdio.h>
Wird das Programm ohne zusätzliche Argumente aufgerufen, ergibt sich z. B. folgen-
de Ausgabe:
argc: 1
argv: D:\Kommandozeilenargumente_1.exe
Bei Aufruf mit den Argumenten abc 12 d 4 etwa nach dem Betriebssystem-
Prompt in der Art >Kommandozeilenargumente_1 abc 12 d 4 oder durch
Eintrag in ein dafür vorgesehenes Fenster der Entwicklungsumgebung (beim
Dev-C++ zu finden unter Ausführen → Parameter) folgt beispielhaft die Ausgabe:
argc: 5
argv: D:\Kommandozeilenargumente_1.exe
argv: abc
argv: 12
argv: d
argv: 4
Beispiel 2.6:
Da es sich bei den übergebenen Argumenten um Zeichenketten handelt, muss der
Programmierer deren Aufbereitung, z. B. eine Umwandlung in Zahlenwerte, selbst
übernehmen. Dafür stehen u. a. die Funktionen atoi (String zu int), atol (String
zu long), atof (String zu double) in stdlib.h zur Verfügung. Allerdings wird
durch diese Funktionen keine Eingabeüberprüfung möglich. Mehr Möglichkeiten
bieten die ebenfalls in dieser Headerdatei vorhandenen Umwandlungsfunktionen
strtod (Konvertierung nach double), strtol (Konvertierung nach long)
oder strtoul (Konvertierung nach usigned), deren Aufruf jedoch aufwendiger ist.
Im Beispiel werden zwei Strings mit je einer ganzen Zahl als Argumente übergeben,
im Programm in int-Größen umgewandelt und deren Summe ausgegeben.
1 //Programm Kommandozeilenargumente_2.c
2 #include <stdio.h>
3 #include <stdlib.h>
4 int main(int argc, char *argv[]){
5 int zahl1, zahl2;
20 INM12
© HfB, 02.12.20, Berk, Eric (904709)
6
7 if(argc < 3) {
8 printf("Mindestens 2 Argumente eingeben!\n");
9 printf("Aufruf: %s <zahl1> <zahl2> ...\n",
10 *argv);
11 return -1;
12 }
13 zahl1=atoi(argv[1]);
14 zahl2=atoi(argv[2]);
15 printf("Die Summe der uebergebenen Zahlen %d",zahl1);
16 printf(" und %d betraegt: %d\n", zahl2, zahl1+zahl2);
17 return 0;
18 }
Erläuterung:
Zeile 3: Die Funktion atoi verlangt das Einbinden von stdlib.h.
Zeilen 7–11: Die Anzahl der übergebenden Parameter wird geprüft. Ist daraus zu
schließen, dass zu wenige Werte für das Programm bereitgestellt
wurden, wird das Programm mit dem Rückgabecode -1 beendet.
Zeilen 13, 14: Die übergebenen Parameter argv[1] und argv[2] werden
nach int konvertiert.
Zeilen 15, 16: Aus drucktechnischen Gründen wird die Ergebnisausgabe auf zwei
separate Ausgabeanweisungen verteilt. Es entsteht dennoch nur eine
Ausgabezeile.
Werden beim Programmaufruf weniger als zwei Argumente übergeben, ergibt sich
als Ausgabe:
Mindestens 2 Argumente eingeben!
Aufruf: D:\Kommandozeilenargumente_2.exe <zahl1> <zahl2> ...
Für einen Aufruf mit wenigstens zwei Argumenten folgt als Ausgabe:
Die Summe der uebergebenen Zahlen 8 und 2 betraegt: 10
INM12 21
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 2.7:
double(*fzeiger)(double) Funktionszeiger kann auf eine Funktion zeigen,
die ein Argument vom Typ double besitzt und
einen double-Wert zurückgibt.
void(*fzeiger)(int,int) Funktionszeiger kann auf eine Funktion zeigen,
die zwei Argumente vom Typ int besitzt, aber
selbst keinen Wert zurückliefert.
int(*fzeiger)( ) Funktionszeiger kann auf eine Funktion zeigen,
der keine Werte übergeben werden, die selbst
aber ein Ergebnis vom Typ int bereitstellt.
Im Gegensatz dazu:
int *funk() Funktion mit Rückgabe eines int-Zeigers.
Die Adresse der Funktion wird durch Zuweisung der Art fzeiger=funktion(...)
bzw. durch Parameterübergabe bereitgestellt.
Der Aufruf erfolgt in der Form (*fzeiger) (parameterliste).
Beispiel 2.8:
Beim Aufruf der Funktion berech wird jeweils als zweites Argument selbst eine
Funktion übergeben.
Der Aufruf von berech geschieht mit i=2. Diese Größe wird innerhalb dieser
Funktion durch Vermittlung der Variablen wert an funkl bzw. funk2 weiter-
gereicht.
1 //Programm Funktionszeiger.c
2 #include <stdio.h>
3
4 int berech(int wert,int(*fzeiger)(int wert));
5 int funk1(int x);
6 int funk2(int x);
7
8 int main (){
9 int i=2;
10 printf("\nFunktion1 benutzt: %i",berech(i,funk1));
11 printf("\nFunktion2 benutzt: %i",berech(i,funk2));
12 }
13
14 int berech(int wert, int(*fzeiger)(int wert)){
15 return(2*wert+(*fzeiger)(wert));
16 }
17
18 int funk1(int x){
19 return(x*x+10);
20 }
21
22 int funk2(int x){
22 INM12
© HfB, 02.12.20, Berk, Eric (904709)
23 return(5*x+50);
24 }
Erläuterung:
Zeilen 4–6: Prototypanweisungen der beteiligten Funktionen.
Zeile 14: Statt der Funktion selbst wird in der Kopfzeile von berech der
Funktionszeiger benutzt.
Zeile 15: Der Aufruf der beiden Funktionen funk1 bzw. funk2 wird über
den Funktionszeiger gesteuert: (*zeiger)(wert).
Zusammenfassung
INM12 23
© HfB, 02.12.20, Berk, Eric (904709)
Der Umgang mit Funktionen ist eine grundlegende Fähigkeit, die vom C-Programmierer
verlangt wird. Anhand der folgenden Programmieraufgaben können Sie die erworbenen
Kenntnisse an unterschiedlichen Sachverhalten überprüfen.
2.1 Für einen Sachverhalt werden der Grundwert wert und der Prozentwert pwert
eingegeben. Aus diesen Werten ist der dazugehörige Prozentsatz psatz zu ermit-
teln.
Arbeiten Sie dazu
• mit einer Funktion prozent zur Berechnung des Prozentsatzes,
• mit einer weiteren Funktion ausgabe zur Ergebnisdarstellung und
• mit einer Programmstartroutine main.
Von hier aus sind die Funktionen prozent und ausgabe aufzurufen.
2.2 Für die Berechnung des Volumens eines Zylinders aus Durchmesser d und
Höhe h sind eine Methodendefinition und der Quelltext der Funktion anzugeben.
Der Funktionsprototyp sei:
double v_berech(double durchm, double hoehe)
2.3 Mit einer Funktion sign soll das Vorzeichen einer an diese Funktion überge-
benen ganzen Zahl gz ausgewertet werden. Die Funktion soll im Fall gz > 0
den Wert 1 und im Fall gz < 0 den Wert –1 zurückgeben.
Erstellen Sie einmal eine Variante, die nach dem Prinzip „call by value“, also ohne
Zeiger, arbeitet, und eine zweite Variante, die als Übergabe- und Rückgabepara-
meter nur Zeiger benutzt.
Testen Sie beide Funktionen mit einem geeigneten Testprogramm.
2.4 Schreiben Sie ein Unterprogramm arctan zur näherungsweisen Berechnung der
Funktion arctan(x) für |x| ≤ 1 unter Verwendung der Taylor-Entwicklung
arctan (x) = x – x3/3 + x5/5 – x7/7 + …
x 2i 1
Die Abbruchbedingung lautet ← eps *| si |,
2i 1
si … i-te Partialsumme, d. h. si = x – x3/3 + x5/5 – x7/7 + … ± x2i + 1/(2i + 1)
eps … relativer Fehler
Das Argument x und der relative Fehler eps sind als Parameter an das Unter-
programm zu übergeben.
In einem Hauptprogramm sind die Werte für x und eps einzulesen und der
ermittelte Wert auszugeben. Vergleichen Sie das Ergebnis Ihres Algorithmus mit
dem der C-Funktion "atan(x)" aus math.h.
24 INM12
© HfB, 02.12.20, Berk, Eric (904709)
3.1 Felder
Der statische Datentyp Feld (engl. array) gestattet die Zusammenfassung von Daten
gleichen Datentyps unter einem Namen. Der Zugriff erfolgt über diesen Namen, ver-
bunden mit einem Index. Sowohl bei der Feldvereinbarung als auch beim Feldzugriff
werden eckige Klammern gesetzt.
Der Feldname ist ein Platzhalter für die Anfangsadresse eines Feldes. Die Feldgröße
muss bereits zum Übersetzungszeitraum bekannt sein (bestimmte Compiler gestatten
jedoch auch eine Festlegung der Feldgröße zur Laufzeit).
Der Index eines Feldelementes beginnt immer mit null. Damit wird ein Feld mit n
Elementen mit den Indizes 0 bis n–1 durchnummeriert.
Die Syntax der Sprache C gibt folgende Möglichkeiten vor:
Felder deklarieren:
Es wird Speicherplatz für 10 Feldelemente vom Typ int bereitgestellt. Die Werte
der Feldelemente feld[0] bis feld[9] sind undefiniert.
Nicht angegebene Feldelemente werden mit dem Wert null initialisiert. Zu viele Feldele-
mente rufen eine Compilerfehlermeldung hervor.
INM12 25
© HfB, 02.12.20, Berk, Eric (904709)
Es wird Speicherplatz für 10 Feldelemente von Typ int bereitgestellt. Die Feld-
elemente wurden in folgender Weise initialisiert:
feld[0]=3, feld[1]=5, feld[2]=-11, feld[3] bis feld[9] besitzen den
Wert 0.
Erfolgt bei der Felddeklaration gleichzeitig die Initialisierung, kann die Angabe der
Anzahl der Feldelemente entfallen. Diese wird dann aus der Anzahl der Initialisierungen
entnommen.
Beispiel 3.3: Deklaration von Feldern mit gleichzeitiger Initialisierung ohne Angabe der
Feldgröße
double feld [ ]={1.1, 8.4, -5.6, 4.5};
Es wird Speicherplatz für 4 Feldelemente von Typ double bereitgestellt. Die Feld-
elemente wurden in folgender Weise initialisiert:
feld[0]=1.1, feld[1]=8.4, feld[2]=-5.6, feld[3]=4.5.
feldName[index]=ausdruck;
Beispiel 3.4:
Betrachten wir ein Programm zur Bestimmung von Primzahlen. Der benutzte Algo-
rithmus heißt „Sieb des Eratosthenes“ und ist bereits seit dem 3. Jahrhundert v. Chr.
bekannt. Das Prinzip besteht in der Berechnung aller Vielfachen einer jeden Zahl,
die demzufolge keine Primzahlen sein können.
Dieses einfache Programm zeigt die Deklaration und Verwendung eines Feldes a.
Dabei korrespondiert der Feldindex mit der zu untersuchenden Zahl.
Als Feldgrenze wird der Wert 100 gewählt und über die globale Konstante N bereit-
gestellt. Oft benutzt man dafür die Präcompiler-Direktive #define.
Das Feldelement a[1] wird mit dem Wert 0, der Kennung für keine Primzahl, be-
legt. Die Feldelemente 2 bis 100 werden mit dem Wert 1 initialisiert. Das Feldele-
ment mit dem Index 0 wird nicht weiter betrachtet und erhält auch den Wert 0 zu-
gewiesen.
Die geschachtelte Laufanweisung berechnet alle Zahlen, die keine Primzahlen sind,
und belegt die zugehörigen Feldelemente mit dem Wert 0.
Damit bleiben nur diejenigen Feldelemente mit a[i] = 1 übrig, deren Index i
eine Primzahl ist. Diese Elemente werden ausgegeben. Für if(a[i]) könnte
ausführlicher if(a[i]>0) stehen.
26 INM12
© HfB, 02.12.20, Berk, Eric (904709)
// Programm Primzahlen.c
#include <stdio.h>
#define N 100
int main(){
int i, j, a[N+1];
a[1] = 0;
for (i = 2; i<= N; i++) a[i] = 1;
return 0;
}
Beispiel 3.5:
Um die Effektivität von C nicht zu vermindern, findet beim Zugriff auf die Feld-
elemente keine Feldgrenzenüberprüfung statt. Hier steht der Programmierer selbst
in der Verantwortung, um die Korrektheit seines Programms abzusichern.
Zum Illustrieren dieses Problems wird ein Array mit 5 ganzzahligen Elementen
definiert. Es erfolgen zwei Ausgaben, einmal für fünf und zusätzlich eine weitere
Ausgabe für fälschlicherweise sieben Feldelemente des gleichen Feldes.
1 //Programm Feldgrenzen.c
2 #include<stdio.h>
3 int main(){
4 int i, feld[]={1,2,3,4,5};
5
6 for(i=0;i<5;i++)
7 printf("Index: %d Wert: %d bei Adresse: %p\n",
8 i,feld[i],&feld[i]);
9 printf("\n");
10 for(i=0;i<8;i++)
11 printf("Index: %d Wert: %d bei Adresse: %p\n",
12 i,*(feld+i),feld+i);
INM12 27
© HfB, 02.12.20, Berk, Eric (904709)
13
14 return 0;
15 }
Ausgabe:
Index: 0 Wert: 1 bei Adresse: 0028FE98
Index: 1 Wert: 2 bei Adresse: 0028FE9C
Index: 2 Wert: 3 bei Adresse: 0028FEA0
Index: 3 Wert: 4 bei Adresse: 0028FEA4
Index: 4 Wert: 5 bei Adresse: 0028FEA8
Erläuterung:
Zeile 4: Deklaration eines Arrays feld mit fünf Feldkomponenten (In-
dex 0 bis Index 4) sowie einer int-Variablen für den Feldindex.
Zeilen 6–8: Von den fünf Feldkomponenten werden in einer Laufschleife je-
weils Index, Wert und Adresse, hier mit dem Platzhalter für
Adressen %p, ausgegeben. Die Ausgabeanweisung wurde auf
zwei Zeilen aufgeteilt. Es wird die Indexschreibweise benutzt.
Zeilen 10–12: Die Laufanweisung beschreibt die Ausgabe von 7 Feldelementen
in Zeigerschreibweise.
Obwohl die Adressbereiche von Hardware- und Softwarebedingungen abhängig
sind, erkennt man jeweils die um 4 Byte anwachsenden Speicheradressen. Die
Ausgaben für die nicht vorhandenen Feldelemente werden entsprechend aus dem
Speicher als int-Größe ausgelesen: feld[6]: 8195304, feld[7]: 79. Dieser
fehlerhafte Arrayzugriff kann nicht nur lesend, sondern auch schreibend erfolgen.
3.2 Zeichenketten
Zeichenketten sind in C als spezielle Felder des Datentyps char aufzufassen. Sie wer-
den als Felder von Zeichen behandelt (in C++ wurde zusätzlich der Datentyp string
eingeführt).
Zur Bearbeitung von Zeichenketten wird intern ein spezielles Endeelement '\0'
(Nullbyte) angefügt. Dieses lässt sich bei einem Endetest abprüfen. Bestimmt man die
Länge einer Zeichenfolge (z. B. mit der Standardfunktion strlen aus string.h),
wird das Nullzeichen nicht mitgezählt.
28 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Bei der Definition von Zeichenketten kann sofort eine Initialisierung, wahlweise mit
einer Zeichenkette oder mit Einzelzeichen, erfolgen. Wird die Zeichenkette initialisiert,
kann die Angabe der Anzahl der Feldelemente entfallen.
Beispiel 3.7:
In der folgenden Abbildung werden die ersten drei Komponenten eines
Feldes vektor dargestellt: vektor=(vektor[0], vektor[1], vektor[2]).
Felder stehen dabei in einem zusammenhängenden Bereich und in fester Reihenfol-
ge im Speicher. Zusätzlich lässt sich ein freier Pointer zeiger explizit vereinbaren.
Dieser Zeiger kann beliebige Feldelemente referenzieren.
vektor
zeiger
&vektor[0]
INM12 29
© HfB, 02.12.20, Berk, Eric (904709)
Ist p die Basisadresse des Feldes und zeigt auf das erste Feldelement vektor[0],
dann zeigt p+1 auf das zweite Feldelement mit dem Index 1 und p+2 auf das
dritte Feldelement mit dem Index 2 usw.
Somit sind a[i] und *(a+i) korrespondierende Schreibweisen. Daraus folgt:
Zugriff auf Feldelemente (in Zeigerschreibweise):
ausdruck = *(feldName+index);
1 //Programm Feld_als_Zeiger.c
2 #include<stdio.h>
3
4 int main(){
5 int i;
6 int vektor[3];
7 int *zeiger;
8 int a=2;
9 *vektor=a;
10 *(vektor+1)=4;
11 zeiger=vektor;
12 //oder:
13 zeiger=&vektor[0];
14 *(zeiger+1)=a-3;
15 zeiger=&vektor[1];
16 *(zeiger+1)=a+1;
17 for(i=0;i<3;i++) //Ausgabe in Indexschreibweise
18 printf("\nvektor[%i]=%i",i,vektor[i]);
19 printf("\n");
20 for(i=0;i<3;i++) //Ausgabe in Zeigerschreibweise
21 printf("\n*(vektor+%i)=%i",i,*(vektor+i));
22
23 return 0;
24 }
*(vektor+0)=2
*(vektor+1)=-1
*(vektor+2)=3
Erläuterung:
Zeile 6: Deklaration eines Feldes vektor mit drei Feldkomponenten.
Automatisch wird intern der Zeiger vektor bereitgestellt.
Zeile 7: Vereinbarung des frei nutzbaren Zeigers zeiger.
Zeile 8: Vereinbarung einer Variablen a vom Typ int und Initialisierung
mit dem Wert 2.
30 INM12
© HfB, 02.12.20, Berk, Eric (904709)
zk1 und zk2 sind Zeiger, die mit der Felddefinition zur Verfügung gestellt werden und
fest an die Anfangsadresse des Feldes gebunden sind.
varz ist ein Zeiger, der variabel zu benutzen ist.
Bei Zuweisungen der Art zeiger_a=zeiger_b; dürfen konstante Zeiger nicht auf
der linken Seite der Zuweisung erscheinen. Ihnen darf keine neue Adresse zugewiesen
werden.
Somit sind Zuweisungen wie zk1=zk2; und zk2=zk1; nicht zugelassen. Das heißt,
in der Programmiersprache C ist eine Zuweisung von Zeichenketten nicht gestattet!
Will man den Inhalt einer Zeichenkette in eine andere Zeichenkette kopieren, muss die
Standardfunktion strcpy (string copy) aus string.h benutzt werden.
Funktionsprototyp:
Mit char* wird hier für den Datentyp Zeichenkette die Zeigerschreibweise benutzt.
Die Funktion kopiert die Zeichenfolge Quelle in die Zeichenfolge Ziel und liefert einen
Zeiger auf Ziel als Funktionswert.
INM12 31
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.8:
Im Programm Zeichenketten.c wird der Umgang mit Zeichenketten zusam-
menfassend dargestellt:
1 //Programm Zeichenketten.c
2 #include <stdio.h>
3 #include <string.h>
4
5 int main(){
6 int i;
7 char zk1[18]="eine Zeichenkette";
8 char zk2[]="auch Zeichenkette";
9 char *varz3="noch eine Zeichenkette";
10 char *varz4;
11
12 printf("1: zk1: %s\n",zk1);
13 //zk1=zk2;
14 strcpy(zk1,zk2);
15 printf("2: zk1: %s\n",zk1);
16 printf("3: varz3: %s\n",varz3);
17 varz4=zk1;
18 printf("4: varz4: %s\n",varz4);
19 varz3=varz4;
20 while(*varz3)
21 printf("%c ",*varz3++);
22 return 0;
23 }
Erläuterung:
Zeile 3: Um die Funktion strcpy nutzen zu können, muss das
Headerfile string.h eingebunden werden.
Zeile 7: Die Zeichenkette zk1 wird definiert und mit einem Wert belegt.
Dabei ist das Zeichenkettenendzeichen zu berücksichtigen.
Zeile 8: Der Zeichenkette zk2 wird bei der Definition ein Wert zugeord-
net. Dadurch kann die Angabe der Feldelemente entfallen.
Zeile 9: Die Vereinbarung erfolgt als Stringkonstante, die durch den
Zeiger varz3 referenziert wird. Dieser Zeiger zeigt auf die
Anfangsadresse der Konstanten, genauer auf die Adresse des
Buchstabens n'. Der Inhalt dieses Strings kann nicht verändert
werden.
Zeile 10: Vereinbarung des variablen Zeigers varz4 vom Typ char*.
32 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Zeile 12: Es erfolgt die Ausgabe der Zeichenkette zk1. Als Platzhalter in
der printf-Anweisung wird %s genutzt.
Zeile 13: Eine Zuweisung von Zeichenketten in C ist nicht zulässig.
Zeile 14: Mit der Bibliotheksfunktion strcpy aus string.h wird der
Inhalt von zk2 nach zk1 kopiert.
Zeile 15: Ausgabe von zk1.
Zeile 16: Ausgabe von varz3.
Zeile 17: Der variable Zeiger varz4 darf auf zk1 gestellt werden. varz4
erhält zk1 zugewiesen.
Zeile 18: Ausgabe von varz4.
Zeile 19: Der variable Zeiger varz3 darf auf varz4 gestellt
werden. varz3 erhält varz4 zugewiesen.
Zeilen 20, 21: Die Ausgabe von varz3 erfolgt zeichenweise und mit einem Zwi-
schenraumzeichen. Dafür wird schrittweise der Speicherbereich
der Zeichenkette so lange durchlaufen, wie die Wiederholungsbe-
dingung *varz3 wahr ergibt. In C ist jeder Ausdruck dann als
wahr aufzufassen, wenn sein Wert ungleich null ist. Der Ausdruck
*varz zeigt auf das aktuelle Zeichen des Stringspeicherbereiches.
Dieses wird erstmalig null, wenn das Zeichenkettenende '\0' er-
reicht wird. Solange noch Zeichen ungleich dem Zeichenkettenen-
dezeichen vorhanden sind, wird dieses ausgegeben und mit dem
postfix benutzten Inkrement-Operator das Weiterschalten zur
nächsten Stringkomponente gesteuert.
INM12 33
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.9:
Sort (anz, f)
Input: anz … Anzahl der Elemente
f … Feld
Output: f … sortiertes Feld
fuer i:=anz(-1)2
fuer k:=1(1)i-1
h:=f[k]
f[k]:=f[k+1]
f[k+1]:=h
Das Struktogramm in Abb. 3.2 enthält zwei geschachtelte Laufschleifen. Start- und
Endwerte dieser Schleifen sind einschließlich aufzufassen. Während in Strukto-
grammen Felder mit dem Index 1 beginnend beschrieben werden, besitzt in C jedes
Feld den kleinsten Index 0. Entsprechend muss bei der Übertragung des Algorithmus
in die Sprache C eine Anpassung erfolgen:
Aus für i:=anz(-1)2 folgt for(i=anz-1;i>=1;i--) oder besser
for(i=anz-1;i>0;i--).
//Programm Sortieren_1.c
#include <stdio.h>
int main(){
int i,feld[6]={12,34,4,17,33,9};
printf("\nAusgangsfeld:\n");
for(i=0;i<6;i++)
printf("%i ",feld[i]);
sort(feld,6);
printf("\nSortiertes Feld:\n");
for(i=0;i<6;i++)
printf("%i ",feld[i]);
return 0;
}
34 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Ausgabe:
Ausgangsfeld:
12 34 4 17 33 9
Sortiertes Feld:
34 33 17 12 4 9
Im Programm Sortieren_2.c ändert sich die Definition von sort und entsprechend
auch die Prototypanweisung.
Prototyp:
void sort(int *f, int anz) oder vereinfacht: void sort(int*,int);
Definition:
Zusammenfassung:
Eine/r der wichtigsten Datenstrukturen/Datentypen sind Felder. Felder verkörpern
eine Aneinanderreihung von Daten gleichen Datentyps. Diese werden in C als Array
dargestellt. Dabei muss vom Programmierer die Anzahl der Feldelemente angegeben
werden. Die meisten Compiler gehen davon aus, dass die Feldgröße bereits zur Com-
pilezeit festliegen muss. Der C99-Standard gestattet jedoch auch diese Festlegung
zur Laufzeit. Der Zugriff auf Feldelemente erfolgt über ihren Index und ist in Feld-
und Zeigerschreibweise möglich. Dieser Index wird in C mit null beginnend gezählt.
INM12 35
© HfB, 02.12.20, Berk, Eric (904709)
Structure Type
(38)
struct { FieldList (39) ; } Ident (2) ;
TagName (5) ,
Aus den []-Klammern in der Syntaxbeschreibung lässt sich die Möglichkeit ableiten,
alternativ einen Stukturnamen oder Strukturvariablennamen anzugeben:
Betrachten wir mögliche Schreibweisen nun an dem konkreten Datensatz datum mit
den Komponenten tag, monat und jahr.
Beispiel 3.10:
Deklaration von Strukturen:
struct datum{
int monat;
int tag;
int jahr;
};
struct datum{
int tag;
int monat;
int jahr;
}termin;
struct datum termin;
36 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Der Zugriff auf die Komponenten des Datensatzes erfolgt durch den Punkt-Operator (.)
bzw., wenn ein Zeiger auf die Struktur vereinbart wurde, mit dem Operator für Ele-
mentzugriff (->).
Beispiel 3.11:
Zugriff auf Strukturkomponenten:
Mit
struct datum t={1,1,2000};
struct datum *z=&t;
//Programm Termin.c
#include <stdio.h>
struct datum{
int tag;
int monat;
int jahr;
};
int main(){
struct datum t={1,1,2000};
struct datum *z=&t;
printf("Termin: %d.%d.%d\n",
t.tag,t.monat,t.jahr);
printf("Termin: %d.%d.%d\n",
z->tag,z->monat,z->jahr);
printf("Termin: %d.%d.%d\n",(*z).tag,
(*z).monat,(*z).jahr);
return 0;
}
INM12 37
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 3.12:
Die folgende typedef-Anweisung:
typedef struct{
int tag;
int monat;
int jahr;
}datum;
//Programm typedef.c
#include <stdio.h>
typedef struct{
int tag;
int monat;
int jahr;
}datum;
int main(){
datum t={1,1,2000};
datum *z=&t;
printf("Termin: %d.%d.%d\n",t.tag,t.monat,t.jahr);
printf("Termin: %d.%d.%d\n",z->tag,z->monat,z->jahr);
printf("Termin: %d.%d.%d\n",(*z).tag,(*z).monat,(*z).jahr);
return 0;
}
Zusammenfassung
38 INM12
© HfB, 02.12.20, Berk, Eric (904709)
3.1 Gegeben seien Messwerte, die durch ein eindimensionales Feld mit definierter
Größe vom Elementtyp reelle Zahlen bereitgestellt sind. Von diesen Werten sind
der Minimalwert, der Maximalwert sowie der Mittelwert zu bestimmen.
Erstellen Sie ein Programm, das aus als vorgegeben aufzufassenden Messwerten
die Ergebnisse ermittelt! Erarbeiten Sie eine Lösung, die auf klassische Art auf die
Feldelemente in Indexschreibweise zugreift und auch alternativ einen Zeiger dafür
nutzt.
3.2 Schreiben Sie eine Funktion vsumme, die aus einem zu übergebenden eindimensi-
onalen Feld und der Anzahl der Feldelemente die Summe aller Feldelemente
ermittelt. Dabei sind 10 Werte vom Datentyp float vorzusehen.
Nutzen Sie diese Funktion in einem Hauptprogramm.
3.3 Entwickeln Sie eine Funktion ersetze_zeichen, die in einer Zeichenkette ein
bestimmtes Zeichen sucht und mit einem anderen austauscht! Benutzen Sie als
Prototypanweisung
void ersetze_zeichen(char*s,char zei_alt, char zei_neu);
3.4 Definieren Sie eine Funktion zeik_verbind, die aus zwei Zeichenketten, z. B.
PROGRAMM und ENTWICKLUNG, eine resultierende Zeichenkette, im Beispiel
PROGRAMMENTWICKLUNG, erzeugt.
Die zu erstellende Lösung darf nicht die in C vorhandene Standardfunktion
strcat benutzen. Beide Ausgangszeichenketten sollen unverändert erhalten blei-
ben. Die Teilzeichenketten und die Ergebniszeichenkette sollen jeweils als Parame-
ter übergeben werden, sodass folgender Funktionsprototyp zu benutzen ist:
void zeik_verbind(const char*,const char*,char*).
INM12 39
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Beim Arbeiten mit Feldern, einem Vertreter statischer Datentypen, haben Sie
bereits kennengelernt, dass der Programmierer über den Datentyp der Feldele-
mente und über deren Anzahl entscheidet. Für so zu deklarierende Felder steht
damit deren Größe bereits zur Compilezeit fest, sie ist nachträglich nicht mehr
veränderbar.
Da jedoch der für Daten benötigte Speicher während der Laufzeit eines Pro-
gramms nicht in jedem Fall exakt im Voraus bestimmbar ist, wird praktisch
sicherheitshalber hinreichend viel Speicherplatz reserviert. Ein solches Arbeiten
erweist sich als extrem unökonomisch.
Deshalb bietet die Programmiersprache C auch die Möglichkeit, Speicherbereiche
während des Programmlaufs zu reservieren und dem laufenden Programm zur
Verfügung zu stellen. Ein solcher Speicher ist außerdem effektiv über Modulgren-
zen ansprechbar.
Damit wird es möglich, den Speicherbedarf für Daten auf die wirklich benötigte
Größe anzupassen.
Zur Umsetzung dieses Konzepts werden Zeiger genutzt, die die Startadresse des
zugeteilten Speichers als Wert zugewiesen bekommen. Diese reservierten
Speicherbereiche müssen aber spätestens bis zum Programmende wieder frei-
gegeben werden.
Zum Umsetzen dieser Funktionalität stehen in der Standardbibliothek in der
Header-Datei stdlib.h definiert die Bibliotheksfunktionen malloc(), calloc(), real-
loc() und free() zur Verfügung.
Nach einem Überblick zu diesen Funktionen werden Sie am Beispiel der einfach
verketteten Liste an die Nutzung dynamischer Datenstrukturen herangeführt.
Aufbauend auf diesen Kenntnissen sollten Sie in der Lage sein, sich die Arbeit mit
weiteren dynamischen Datenstrukturen zu erschließen.
4.1 Speicherkonzept
Ein laufendes Programm benutzt vier Speicherbereiche:
Heap: Dynamisch reservierter Speicher. Bei einer Speicheranforderung
wird der Heap-Speicher vergrößert, bei Freigabe wird er wieder
verkleinert.
Stack: Er wird von der Hardware direkt unterstützt und dient der Ver-
waltung der Funktionsaufrufe und lokaler Variabler.
Datenbereich: Zur Speicherung statischer Daten, die bis zum Programmende
verfügbar sind (globale und statische Variablen).
Programmbereich: Er dient zur Ablage des Maschinencodes des Programms.
Für die dynamische Speichernutzung ist der Heap-Speicher, auch Halden- oder Haufen-
Speicher genannt, wichtig.
40 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
Funktionsprototyp:
Bemerkung:
Der in der Definition verwendete Datentyp size_t ist ein zur Angabe der Größe eines
Speicherbereichs speziell definierter, vorzeichenloser Ganzzahldatentyp. Die Definition
von size_t erfolgt in der Regel in stddef.h.
Die Funktion free gibt den bereitgestellten Zeiger wieder frei. Dazu wird der Zeiger,
der auf den freizugebenden Speicherbereich zeigt, der Funktion free als Parameter
übergeben.
INM12 41
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Funktionsprototyp:
Beispiel 4.2:
Freigabe von reserviertem Speicher:
free(str); free(p); free(z);
Das folgende Beispiel zeigt das prinzipielle Arbeiten mit den Funktionen malloc()
und free(). Hier wird dynamischer Speicher für eine int-Größe und eine Zeichen-
kette bereitgestellt.
Beispiel 4.3:
1 //Programm malloc.c
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 int main(){
7 char *str;
8 int *p;
9
10 p=(int *)malloc(sizeof(int));
11 if(p != NULL) {
12 printf("\nSpeicher ist reserviert\n");
13 }else {
14 printf("\nKein freier Speicher vorhanden.\n");
15 }
16
17 if(p != NULL){
18 free(p);
19 p=NULL;
20 printf("Speicher freigegeben.\n\n");
21 }
22
23 if ((str = (char *) malloc(6)) == NULL) {
24 printf("Kein freier Speicher vorhanden.\n");
25 exit(1);
26 }
27 strcpy(str, "Hallo");
28 printf("Zeichenkette: %s\n", str);
29
30 free(str);
31 p=NULL;
32 printf("Speicher freigegeben.\n");
33 return 0;
34 }
42 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
Erläuterung:
Zeilen 2–4: Um die Funktionen printf, malloc, free und strcpy
nutzen zu können, müssen die Headerfiles stdio.h, stdlib.h
und string.h eingebunden werden.
Zeilen 7–8: Die Zeiger str vom Typ char* sowie p vom Typ int*
werden definiert.
Zeile 10: Der Zeiger p wird auf einen reservierten Speicherbereich für
eine int-Größe im Heap gestellt.
Zeilen 11–15: Anhand des zurückgegebenen Zeigers p wird überprüft, ob der
angeforderte Speicher zur Verfügung gestellt wurde.
Zeilen 17–21: Bereitgestellter Speicher wird freigegeben. Es empfiehlt sich zu
überprüfen, ob nur der reservierte Speicherplatz freigegeben
wurde. Da in der Regel die Informationen in diesem Speicher noch
weiterhin über den Zeiger erreichbar sind, wird empfohlen, nach
einer Freigabe noch den Null-Zeiger zuzuweisen.
Zeilen 23–26: Der Zeiger str wird auf einen reservierten Speicherbereich mit
der Größe von 6 Byte gestellt. Die Überprüfung dieser Aktion
erfolgt in einer komplexeren Schreibweise. Konnte kein Speicher
bereitgestellt werden, erfolgt mit exit der Programm-Abbruch.
Zeile 27: Steht Speicher zur Verfügung, wird in diesem Speicherbereich die
Zeichenkette "Hallo" eingetragen und anschließend in Zeile 28
ausgegeben.
Zeilen 30–31: Der reservierte Speicherbereich wird freigegeben sowie die Adresse
des Zeigers auf den Wert NULL gesetzt.
Funktionsprototyp:
Beispiel 4.4:
Bereitstellung eines Feldes aus 10 Speicherblöcken der Speichergröße des Datentyps
int:
int *p;
p=(int *)calloc(10,sizeof(int));
INM12 43
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Die Möglichkeit der Vereinbarung eines Feldes mit einer bestimmten Größe je nach
Bedarf zur Programmlaufzeit ist eine der gewünschten Anwendungen der dynamischen
Speichervereinbarung.
Sollen beispielsweise mit einem Programm ganze Zahlen in einer Datenstruktur Feld
verarbeitet und gespeichert werden, muss vor der Programmausführung die Feldgröße
festgelegt sein. Wenn die Anzahl der zu erwartenden Daten nicht genau bekannt ist,
muss vom Programmierer eine hinreichend große Anzahl von Feldelementen vorgese-
hen werden. Erschöpft sich dennoch die Speicherkapazität der definierten Datenstruktur
während der Programmabarbeitung, besteht keine Möglichkeit der Korrektur. Ein Über-
schreiten der Feldgrenze kann bis zum Programmabsturz führen.
Beispiel 4.5 illustriert das Vorgehen zum Bereitstellen eines dynamisch vereinbarten Fel-
des für ganze Zahlen unter Nutzung der Funktion calloc. In Beispiel 4.7 wird die
Möglichkeit gezeigt, die Größe eines dynamisch reservierten Speicherbereichs mit der
Funktion realloc zur Laufzeit nochmals zu verändern.
Beispiel 4.5:
Nach Definition des Zeigers *array wird diesem eine Folge von Speicherblöcken
des Datentyps int zugewiesen. Die Festlegung der Anzahl der Speicherblöcke ge-
schieht durch Eingabe der Größe size. Nach Nutzung des Speichers erfolgt am
Programmende seine Freigabe.
1 //Programm calloc.c
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 int main() {
7 int *array;
8 int size,wert,i=0;
9
10 printf("Feldgroesse: ");
11 scanf("%d", &size);
12 array = (int *)calloc(size,sizeof(int));
13 if(array == NULL) {
14 printf("Fehler bei calloc....\n");
15 exit(1);
16 }
17 while(i < size) {
18 printf("Wert fuer array[%d] eingeben : ", i);
19 scanf("%d", &array[i]);
20 i++;
21 }
22 printf("\nEingegebene Werte\n");
23 for(i=0; i < size; i++)
24 printf("array[%d] = %d\n", i, array[i]);
25
26 free(array);
27 array = NULL;
28 return 0;
29 }
44 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
Mit der Funktion realloc() (engl. reallocate memory block) ist es möglich, einen
vorher mit malloc() oder calloc() reservierten Speicherbereich zu vergrößern
oder zu verkleinern. Damit hat der Programmierer ein extrem dynamisches Werk-
zeug zur Hand.
Funktionsprototyp:
void * realloc ( void *zgr, size_t nsize );
Übergabeparameter nsize: Neue Größe des Speichers in Byte.
Übergabeparameter zgr: Zeiger auf bisher allozierten Speicherplatz.
Rückgabetyp: void-Zeiger unabhängig von einem Datentyp.
Beispiel 4.6:
Größenveränderung des bisher über den Zeiger p angesprochenen Speicherbe-
reichs auf n* der Speichergröße des Datentyps int:
int *p;
p=(int *)realloc(p,n*sizeof(int));
Beispiel 4.7:
1 //Programm realloc.c
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 int main() {
7 int *array;
8 int size,wert,i=0;
9
10 printf("Feldgroesse : ");
11 scanf("%d", &size);
12 array = (int *)calloc(size,sizeof(int));
INM12 45
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
13 if(array == NULL) {
14 printf("Fehler bei calloc....\n");
15 exit(1);
16 }
17
18 else
19 printf("Speicher fuer %d Elemente reserviert\n",size);
20
21 printf("\nneue Feldgroesse : ");
22 scanf("%d", &size);
23 array = (int *)realloc(array,size*sizeof(int));
24 if(array == NULL) {
25 printf("Fehler bei realloc....\n");
26 exit(1);
27 }
28
29 else
30 printf("Speicher fuer %d Elemente reserviert\n",size);
31
32 while(i < size) {
33 printf("Wert fuer array[%d] eingeben: ", i);
34 scanf("%d", &array[i]);
35 i++;
36 }
37 printf("\nEingegebene Werte\n");
38 for(i=0; i < size; i++)
39 printf("array[%d] = %d\n", i, array[i]);
40
41 free(array);
42 array=NULL;
43 return 0;
44 }
Programmabarbeitung:
Feldgroesse : 2
Speicher fuer 2 Elemente reserviert
neue Feldgroesse : 4
Speicher fuer 4 Elemente reserviert
Wert fuer array[0] eingeben: 1
Wert fuer array[1] eingeben: 2
Wert fuer array[2] eingeben: 3
Wert fuer array[3] eingeben: 4
Eingegebene Werte
array[0] = 1
array[1] = 2
array[2] = 3
array[3] = 4
46 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
Daten Zeiger
Die Liste entsteht durch Verkettung mehrerer Listenelemente. Dabei besitzen der Listen-
anfang (oft Kopf genannt) und das Listenende (Schwanz) eine herausgehobene Bedeu-
tung. Abb. 4.2 zeigt den prinzipiellen Aufbau einer linearen Liste.
Listenanfang,
Listenkopf Listenende
NULL
INM12 47
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Aus dieser Darstellung lassen sich folgende Eigenschaften für eine lineare Liste ableiten:
Beispiel 4.8:
Die grundlegende Idee der Listen ist, Listenelemente aneinanderzureihen, indem ein
Zeiger innerhalb eines Datensatzes auf das jeweils folgende Element verweist. Die-
ser Zeiger referenziert eine Adresse, die dem gleichen Typ wie der Datensatz selbst
entspricht. Ein Listenelement enthält einen Zeiger auf die nachfolgende Liste.
Als Beispiel betrachten wir eine lineare Liste, deren Daten nur aus einer ganzen Zahl
bestehen.
Dafür ergibt sich für ein Listenelement folgende Deklaration:
struct LListe {
int zahl;
struct LListe *next;
};
1 // Programm Liste_von_Hand.c
2 #include <stdio.h>
3 #include <stdlib.h>
4 struct LListe {
5 int zahl;
6 struct LListe *next;
7 };
8
9 int main(){
10
11 struct LListe *e1,*e2,*e3;
12 int i=1;
13 e1 = (struct LListe*) malloc
14 (sizeof(struct LListe));
15 e1->zahl = 11;
16 e1->next = NULL;
17
18 e2 = (struct LListe*) malloc
19 (sizeof(struct LListe));
20 e2->zahl = 22;
21 e2->next = NULL;
48 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
22
23 e3 = (struct LListe*) malloc
24 (sizeof(struct LListe));
25 e3->zahl= 33;
26 e3->next = NULL;
27
28 struct LListe *LAnfang;
29
30 LAnfang=e1;
31 e1->next = e2;
32 e2->next = e3;
33 e3->next = NULL;
34
35 struct LListe *temp=LAnfang;
36 while(temp != NULL){
37 printf("\n%i. Element mit Wert : %d",
38 i,temp->zahl);
39 temp=temp->next;
40 i++;
41 }
42 }
Beispiel 4.9:
Wir nutzen die bereits in Beispiel 4.8 vorgenommene Deklaration für ein Listen-
element vom Datentyp struct LListe. Den Quelltext der im main-Modul ver-
wendeten Anweisungen finden Sie in Code 4.10 am Ende dieses Beispiels.
struct LListe {
int zahl;
struct LListe *next;
};
Darauf aufbauend lässt sich eine Liste erzeugen. Dieser Aufbau erfolgt schrittweise.
Zuerst stellen wir das erste Listenelement, den Listenkopf LKopf, bereit:
INM12 49
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Da der Listenkopf ein Element der Liste ist, wird auch er mit Daten belegt. Für das
erste Element tragen wir für zahl den Wert 1 ein. Der Zeiger next ist auf NULL
zu setzen, da kein weiteres Element in der Liste existiert:
LKopf->zahl=1;
LKopf->next=NULL;
Ausgabe:
Ausgabe Kopfelement:
zahl: 1 next: 00000000
Nachdem der Listenkopf erstellt wurde, sollen weitere Listenelemente an die Liste
angefügt werden. Wir definieren dazu die Funktion addLelem.
Damit eine solche Funktion sinnvoll arbeiten kann, wird der Bezug zur Liste herge-
stellt, indem das aktuell betrachtete Listenelement übergeben wird.
Dazu muss dieses aktuelle Listenelement deklariert und initialisiert werden. Für den
Entwicklungsstand der Liste gilt:
struct LListe *aktElem;
aktElem=LKopf;
An dieses Element ist der neue Listenknoten anzufügen, der nun selbst als neues
aktuelles Element als return-Parameter zurückgegeben wird.
Somit ergibt sich die Funktionsdeklaration als Schnittstelle mit den formalen
Parametern bezug vom Typ struct LListe und z vom Typ int:
struct LListe* addLelem(struct LListe *bezug, int z)
In der Funktion wird ein lokales neues Element durch dynamische Speicherreservie-
rung erzeugt, mit dem Wert z (input) belegt, in die Listenstruktur eingekettet und
diese schließlich zurückgegeben (output).
50 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
11 }
12 }
Erläuterung:
Listenelemente ausgeben
Bei Überlegungen zur Ausgabe aller Listenelemente findet man folgenden Funkti-
onsprototypen: void printListe(struct LListe *bezug).
Eine Funktion zum Auflisten von Werten hat in der Regel den Rückgabetyp void.
Die gesamte Liste muss übergeben werden, d. h., als Argument beim Aufruf wird der
Listenkopf benutzt. Wir benutzen zum Durchlauf durch die Liste einen lokal dekla-
rierten Zeiger akt. Dieser wird beim Ausgeben listenelementweise bis zum Liste-
nende durch die Liste bewegt. Die Ausgaben werden dabei um einen Zähler ergänzt.
Der Aufruf erfolgt mit dem Bezug zum Listenkopf: printListe(LKopf);
INM12 51
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
9 zaehler++;
10 }
11 }
Erläuterung:
Listenelemente suchen
Um Listenelemente zu suchen, wird ähnlich wie beim Ausgeben aller Elemente die
gesamte Liste durchlaufen und deren Knoten mit dem Suchkriterium verglichen.
Wird das Element gefunden, bricht die Suche ab, das Element wird zurückgegeben.
Ist das Listenelement nicht vorhanden, erfolgt die Rückgabe von NULL.
In der aufrufenden Funktion, in unserem Beispiel im main-Programm, wird ein
Bezeichner element zur Verfügung gestellt, um die weitere Verarbeitung des
gefundenen Elements zu sichern:
struct LListe *element;
Erläuterung:
52 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
Zeile 9: Der Rückgabewert ist NULL, wenn kein Element ermittelt werden
konnte.
Der Aufruf von getLelem erfolgt mit dem Bezug zum Listenkopf:
element = getLelem(LKopf,33);
Ausgabe:
Ergebnis Suche:
zahl: 33 next: 00000000
... ...
... ...
... ...
INM12 53
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Erläuterung:
Gibt man nach diesem Löschvorgang die Liste wieder aus, ergibt sich die Ausgabe:
1. Element mit Zahl: 1
2. Element mit Zahl: 11
3. Element mit Zahl: 22
Löschen am Listenanfang:
Da das erste Listenelement dem Listenkopf entspricht, kann man dieses Element
nicht einfach löschen, denn der Listenkopf gewährt den Listenzugang.
Der Algorithmus dafür ist relativ schnell gefunden. Der Nachfolger des zu löschen-
den Listenkopfes muss als neuer Listenkopf festgelegt werden.
Immer dann, wenn Parameter direkt verändert werden sollen, bietet sich an, die Pa-
rameterübergabe „call by reference“ zu nutzen und bei Aufruf die Adresse des Argu-
ments zu übergeben. Da es sich bei dem Listenkopf bereits um einen Zeiger handelt,
entsteht hier die Struktur Zeiger auf Zeiger.
54 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
In Zeile 2 erfolgt das Überschreiben der Adresse des Listenkopfs (erstes Element der
Liste) mit der Adresse des Nachfolgers vom Listenkopf (zweites Element der Liste).
Der Aufruf hat in folgender Art zu erfolgen: loescheKopf(&LKopf);
Die dargestellten Betrachtungen entsprechen den Anweisungen des folgenden main-
Programms:
1 int main(){
2 struct LListe *LKopf;
3 struct LListe *aktElem;
4 struct LListe *element;
5 LKopf = (struct LListe*) malloc (sizeof(struct
6 LListe));
7
8 LKopf->zahl=1;
9 LKopf->next=NULL;
10
11 printf("Ausgabe Kopfelement:\nzahl: %d next: %p\n\n",
12 LKopf->zahl,LKopf->next);
13 aktElem=LKopf;
14 aktElem=addLelem(aktElem,11);
15 aktElem=addLelem(aktElem,22);
16 aktElem=addLelem(aktElem,33);
17
18 printListe(LKopf);
19
20 element = getLelem(LKopf,33);
21 printf("\nErgebnis Suche:\nzahl: %d next: %p\n\n",
22 element->zahl,element->next);
23
24 loescheLelem(LKopf,element);
25 printListe(LKopf);
26
27 element = getLelem(LKopf,1);
28 printf("\nErgebnis Suche:\nzahl: %d next: %p\n\n",
29 element->zahl,element->next);
30
31 printf("Loeschen Kopfelement:\n");
32 loescheKopf(&LKopf);
33 printListe(LKopf);
34
35 return 0;
36 }
INM12 55
© HfB, 02.12.20, Berk, Eric (904709)
4 Dynamische Speicherverwaltung
Ergebnis Suche:
zahl: 33 next: 00000000
Ergebnis Suche:
zahl: 1 next: 00640DE8
Loeschen Kopfelement:
1. Element mit Zahl: 11
2. Element mit Zahl: 22
Zusammenfassung
56 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dynamische Speicherverwaltung 4
4.1 Setzen Sie die folgende Datenstruktur aus Kapitel 2 des Heftes INM11 als lineare
Liste um.
struct datum{int tag, monat, jahr };
Implementieren Sie ein Programm, das einen Listenkopf erzeugt und die Funk-
tionen zum Anfügen eines Listenelements und zur Ausgabe der linearen Liste
enthält. Testen Sie die Funktionalität mit mindestens drei Listenelementen.
4.2 Erweitern Sie das Beispiel 4.9 um die Funktion zaehlLElement, die die Anzahl
der aktuellen Listenelemente ermittelt und als Ergebnis liefert. Überprüfen Sie Ihre
Implementation z. B. durch folgende Anweisung:
printf("\nAnzahl Listenelemente: %d\n",zaehlLElem(LKopf));
4.3 Erweitern Sie das Beispiel 4.9 um die Funktion addLelementAnf, die am
Listenkopf ein neues erstes Element einfügt. Orientieren Sie sich bei der Lösung
auch an der Funktion loescheKopf.
INM12 57
© HfB, 02.12.20, Berk, Eric (904709)
5.1 Spracherweiterungen
C++ ist eine objektorientierte Weiterentwicklung von C. Die Sprache enthält neben
Spracherweiterungen als Voraussetzung für das objektorientierte Programmieren
Sprachelemente zur Nutzung der eigentlichen objektorientierten Funktionalität.
Die wesentlichsten nichtobjektorientierten Erweiterungen werden im Folgenden dem
objektorientierten Programmieransatz vorangestellt.
Beispiel 5.1:
Für die einfache Ein- und Ausgabe in C++ stehen die Funktionen cin und cout
aus der Headerdatei iostream zur Verfügung. Die veraltete Datei iostream.h
wird von aktuellen Compilern nicht mehr genutzt.
Das Grundgerüst eines C++-Programms besitzt folgendes Aussehen.
1 //Programm Ein-Ausgabe.cpp
2 #include <iostream>
3 using namespace std;
4
5 int main(void){
6 double zahl;
58 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Bemerkung:
Der Operator „<<“ zeigt auf die Ausgabe, „>>“ zeigt auf die Variable.
Die Standardbibliothek von C++ wird im Namensraum std verwaltet. Mit using
namespace std; werden diese Funktionen im Programm verfügbar gemacht.
Beispiel 5.2:
Es wird möglich, konstante Werte mit dem Schlüsselwort const zu vereinbaren.
Der Compiler überwacht dann diese Festlegung und meldet eventuell auftretende
Schreibzugriffe.
1 //Programm const.cpp
2 #include <iostream>
3 using namespace std;
4
5 int main(void){
6 const int zahl=1;
7 zahl=2; // unzulässig
8 const int feld[] ={1,2,3};
9 feld[2]=6; // unzulässig
10 const int* z1=feld;
11 *z1=7; // unzulässig
12 z1=&zahl; // zulässig
13 const int *const z2=&feld[0];
14 z2=&feld[1]; //unzulässig
15 return 0;
16 }
INM12 59
© HfB, 02.12.20, Berk, Eric (904709)
Erläuterung:
Zeilen 6, 8: Werteveränderungen der int-Variablen sind nicht zulässig.
Zeile 10: Vereinbarung eines Zeigers auf const int, die Werteveränderung
in Zeile 11 ist nicht zulässig.
Zeile 13: Vereinbarung eines const-Zeigers auf const int, die Werte-
veränderung in Zeile 14 ist nicht zulässig.
5.1.3 Standardargumente
Eine neue Möglichkeit bei der Nutzung von Funktionen besteht darin, bestimmten
Argumenten sogenannte Standardwerte zuzuordnen. Diese Angabe erfolgt bei der
Funktionsdeklaration.
Dabei müssen die als Standardargumente genutzten Funktionsparameter immer an das
Ende der Parameterliste gestellt werden.
Beispiel 5.3:
Im Beispiel erwartet funktion zwei Argumente. Wird die Funktion mit einem
Argument aufgerufen, wird dieses an den formalen Parameter x übergeben. Der
Parameter y erhält den float-Wert 1.5 zugewiesen.
1 //Programm_Standardargumente.cpp
2 #include <iostream>
3 using namespace std;
4
5 float funktion(float x, float y=1.5f);
6
7 int main(void){
8 float a=1.8f,b=-5.2f, ergebnis1,ergebnis2;
9
10 ergebnis1=funktion(a,b); //Aufruf mit 2 Argumenten
11 ergebnis2=funktion(a);
12
13 cout<<"Ergebnis1: " <<ergebnis1<<'\n'
14 <<"Ergebnis2:"<<ergebnis2;
15 return 0;
16 }
17
18 float funktion (float x, float y){
19 return x+y;
20 }
Ausgabe:
Ergebnis1: -3.4
Ergebnis2: 3.3
60 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 5.4:
Unter dem Überladen von Funktionen versteht man hier eine Möglichkeit, Funk-
tionen gleichen Namens, aber mit unterschiedlicher Anzahl und Art von Funktions-
argumenten parallel zu verwenden. Entsprechend dem Aufrufkontext entscheidet
der Compiler, welche Form benutzt werden muss.
1 // Programm F_Ueberladen.cpp
2 #include <iostream>
3 using namespace std;
4 float berechnung(float,float,int);
5 float berechnung(float,float);
6
7 int main(){
8 int c;
9 float a,b;
10 cout << "\nWerte-Eingabe fuer a,b und c: ";
11 cin>> a >>b >>c;
12
13 switch (c){
14 case 0: cout <<"undefiniert";
15 break;
16 case 1: cout <<"Ergebnis: "<< berechnung(a,b);
17 break;
18 default:cout <<"Ergebnis: "<< berechnung (a,b,c);
19 }
20 return 0;
21 }
22
23 float berechnung (float a, float b)
24 {return a*b;}
25
26 float berechnung (float a, float b, int c)
27 {return a*b/c;}
Diese Entscheidung wird im Beispiel abhängig vom Wert der Variablen c getroffen.
Ausgabe:
Werte-Eingabe fuer a,b und c: 1.2 2.0 1
Ergebnis: 2.4
INM12 61
© HfB, 02.12.20, Berk, Eric (904709)
5.1.5 Referenzen
Beispiel 5.5:
In C++ besteht nun die Möglichkeit, für vereinbarte Bezeichner Referenzvariablen
zu definieren. Eine Referenz ist ein Alias-Name für eine bereits existierende Variab-
le. Damit wird der gleiche Bezeichner (Adresse und Wert) angesprochen. Referenzen
sind auch als Parameter von Funktionen zu benutzen. Damit entfällt die von Zeigern
bekannte aufwendige Schreibweise.
1 // Programm referenz.cpp
2 #include <iostream>
3 using namespace std;
4 void ztausch (int&,int&);
5
6 int main(void){
7 int zahl1=10,&a=zahl1;
8 int zahl2=20,&b=zahl2;
9 cout << "\nAusgangswerte: "<<zahl1<< " "<<zahl2;
10 cout << "\nAusgangswerte: "<<a<< " "<<b;
11 ztausch(zahl1,zahl2);
12 cout<<endl;
13 cout << "\nErgebniswerte: "<<zahl1<<" "<< zahl2;
14 return 0;
15 }
16 void ztausch(int &ap,int &bp)
17 {
18 int vhilf;
19 vhilf=ap;
20 ap=bp;
21 bp=vhilf;
22 }
Erläuterung:
Zeile 4: Prototypanweisung für ztausch. Die beiden Parameter werden
als Referenz vermittelt.
Zeilen 7, 8: a wird als Referenz zu zahl1 und b als Referenz zu zahl2
deklariert.
Zeilen 9, 10: Beide Anweisungen erzeugen die gleiche Ausgabe.
Zeilen 16–22: Der Zugriff auf zahl1 und zahl2 erfolgt über ap und bp. Die
Bezeichner ap und bp sind wie zahl1 und zahl2 selbst zu
verwenden.
Ausgabe:
Ausgangswerte: 10 20
Ausgangswerte: 10 20
Ergebniswerte: 20 10
62 INM12
© HfB, 02.12.20, Berk, Eric (904709)
INM12 63
© HfB, 02.12.20, Berk, Eric (904709)
Notwendige Methoden, die eine konkrete Arbeit mit dieser Datenstruktur Person
ermöglichen, wären in den Funktionen ErfassePerson, SuchePerson,
DruckePerson, LöschePerson usw. zu implementieren.
Sowohl die Attribute als auch Methoden sind gemeinsam Bestandteil der Klassende-
klaration.
Dabei ist es auch möglich, die Attribute Postleitzahl, Wohnort, Straße,
Hausnummer nicht als Einzelattribute, sondern als Objekt einer weiteren Klasse,
etwa Anschrift, zu verwenden. In gleicher Weise könnte man für die Attribute
Geburtstag, Geburtsmonat, Geburtsjahr die Klasse Datum deklarieren. Hier
wird mit der Nachnutzung bereits bestehender Klassen ein weiteres Konzept der objekt-
orientierten Programmierung sichtbar.
Zusammenfassung:
Beim objektorientierten Programmieransatz wird mit dem Datentyp Klasse eine Daten-
kapsel zur Verfügung gestellt, die Daten (Attribute) und Operationen (Methoden) auf
diesen Daten vereint.
64 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Eine Klasse wird global definiert. Am Ende der Definition ist ein „;“ anzugeben.
Die Grundidee besteht in einer Datenkapselung. Ein direkter Zugriff auf die Attribute
der Klasse wird in der Regel unterbunden (private). Dieser Zugriff geschieht über die
entsprechenden Methoden (public). Die Methoden besitzen einen privilegierten Zugriff
auf die Datenelemente, d. h., es ist keine Parameterübergabe notwendig, um diese in den
Methoden zu nutzen.
5.3.1 Standardkonstruktor
Beispiel 5.6:
Betrachten wir eine Klasse Rechteck. Vereinfachend gehen wir davon aus, dass ein
Rechteck mit der Angabe zweier Seitenlängen a und b ausreichend beschrieben ist.
Gewöhnlich sollten die Werte der Attribute nur über Methoden – nach ihrer Auf-
gabe getter- (d. h. frage ab) und setter- (d. h. setze) Methoden genannt – verändert
werden können.
Die konkreten Objekte der Klasse Rechteck verfügen über zwei Attribute (a und b)
und über fünf Methoden:
1 //Programm Standardkonstruktor.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Rechteck{
6 private:
7 float a,b;
8 public:
9 void setze_a(float x){a=x;}
10 float erhalte_a (void) {return a;}
11 void setze_b(float y){b=y;}
12 float erhalte_b (void) {return b;}
13 void ausgabe(void){
14 cout<<"\nRechteck: a= "<<erhalte_a()
15 <<" b= "<<erhalte_b()<<"\n";
16 }
17 };
18
INM12 65
© HfB, 02.12.20, Berk, Eric (904709)
19 int main(void){
20 Rechteck r;
21 r.ausgabe();
22 r.setze_a(3.1);
23 r.setze_b(8.0);
24 r.ausgabe();
25 return 0;
26 }
Die Objekte/Instanzen, d. h. die konkreten Vertreter der Klasse, werden mit der
Schreibweise Rechteck r erzeugt. Diese Bereitstellung erfolgt dabei über einen
sogenannten Konstruktor – eine spezielle Methode, die genauso benannt ist wie die
Klasse selbst, also Rechteck.
Da allerdings im Quelltext keine deklarierte Methode mit dem Namen Rechteck
als expliziter Konstruktor angegeben wurde, kommt hier der sogenannte Standard-
konstruktor zur Anwendung. Dieser Standardkonstruktor erzeugt das Objekt r,
initialisiert aber die Attribute nicht.
Die Anweisung r.ausgabe(); in Zeile 21 erzeugt z. B. folgende Ausschrift:
Rechteck: a= 3.76508e–039 b= 5.9539e–039.
Nach Abarbeitung der setter-Methoden stehen im Objekt r über r.a und r.b
Länge und Breite des Rechtecks zur Verfügung. Es wird ausgegeben: Rechteck:
a= 3.1 b= 8
Erläuterung:
Zeilen 6–7: Für die Aufnahme der Werte beider Seitenlängen sind
zwei float-Bezeichner vorgesehen. Diese werden im Sinne der
Datenkapselung private deklariert.
Zeile 8: Nach „außen“ bereitgestellte Methoden besitzen den
Zugriffsmodifizierer public.
Zeilen 9–12: Die Methoden zum Setzen und Abfragen der Klassenattribute
werden definiert.
Zeile 9: Das Attribut a erhält einen Wert.
Zeile 10: Der aktuelle Wert von a wird zurückgegeben.
Zeile 11: Das Attribut b erhält einen Wert.
Zeile 12: Der aktuelle Wert von b wird zurückgegeben.
Zeilen 13–16: Es erfolgt die Definition der Methode ausgabe. Die aktuellen
Werte der Attribute a und b werden nicht direkt, sondern über
die entsprechenden getter-Methoden bereitgestellt.
Zeile 20: Erzeugung der Instanz r durch Aufruf des Standardkonstruktors.
66 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Ausgabe:
Rechteck: a= 3.76508e-039 b= 5.9539e-039
Rechteck: a= 3.1 b= 8
Beispiel 5.7:
Die im Beispiel 5.6 vorliegende Definition der Klasse Rechteck erhält in diesem
Beispiel einen expliziten Konstruktor, der jeweils die Werte für die Attribute a
und b des Rechtecks setzt:
Rechteck(float x, float y)
{a=x;b=y;}
INM12 67
© HfB, 02.12.20, Berk, Eric (904709)
Die Instanz r2 wird erzeugt und bekommt danach die Werte von r1 zugewiesen.
Eine weitere mögliche Schreibweise ist: Rechteck r2(r1);
Der Ausdruck r2=r1; (ohne Rechteck) stellt im Gegensatz dazu die Nutzung des
Zuweisungsoperators „=“ dar, der auch automatisch für Klassen bereitgestellt wird.
1 //Programm Konstruktoren.cpp
2 #include <iostream>
3 using namespace std;
4 class Rechteck{
5 private:
6 float a,b;
7 public:
8 Rechteck(float x, float y)
9 {a=x;b=y;}
10 ~Rechteck(){}
11 void setze_a(float x){a=x;}
12 float erhalte_a (void) {return a;}
13 void setze_b(float y) {b=y;}
14 float erhalte_b (void) {return b;}
15 void ausgabe(void){
16 cout<<"\nRechteck: a= "<<erhalte_a()
17 <<" b= "<<erhalte_b()<<"\n";
18 }
19 };
20 int main(void){
21 //Rechteck r nicht mehr moeglich
22 Rechteck r1(5.5f,7.9f);
23 //cout<<"\nRechteck: a= "<<r1.a<<" b= "<<r1.b<<"\n";
24 r1.ausgabe();
25 Rechteck r2=r1;
26 r2.setze_a(7.9f);
27 r2.ausgabe();
28 return 0;
29 }
Bemerkungen:
Zeilen 8, 9: Konstruktordefinition
Zeile 10: Destruktordefinition
Zeile 21: Der Standardkonstruktor kann nicht mehr genutzt werden, da we-
nigstens eine explizite Konstruktordefinition (Zeilen 8 und 9) erfolgt
ist.
68 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Zeile 23: Ein direkter Zugriff auf r1.a sowie r1.b ist in der main-Funk-
tion, also außerhalb der Klasse, nicht möglich. Der Compiler meldet
z. B.: [Error] 'float Rechteck::a' is private.
Zeile 25: Kopierkonstruktoraufruf, r2 entsteht als Kopie von r1.
In der Ausgabe des Programms sind die beiden Rechteckinstanzen erkennbar:
Rechteck: a= 5.5 b= 7.9
5.3.3 Initialisierungslisten
Initialisierungen von Attributen können auch in einem speziellen Bereich zwischen
Argumentliste und Funktionskörper erfolgen. Er wird durch einen Doppelpunkt ab-
getrennt und damit beginnt die sogenannte Initialisierungsliste. Sie kann zur Initiali-
sierung von Datenelementen genutzt werden.
Die Reihenfolge der Initialisierungen hängt von der Reihenfolge der Deklarationen der
Attribute der Klasse und nicht von der Reihenfolge in der Liste ab.
Während in einigen Fällen (Konstanten, Referenzen …) nur dieser Weg bleibt, ist die
Initialisierungsliste auch auf einfache Datentypen anwendbar.
Schreibweise:
Klassenname(Formalparameterliste): Initialisierungsliste {
Anweisungen
}
Beispiel 5.8:
In unserem Fall kann der Konstruktor für Rechteck unter Nutzung von einer Initia-
lisierungsliste auch folgende Notation besitzen (Zeile 9):
Rechteck(float x, float y): a(x), b(y) {}
1 // Programm Initialisierungsliste.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Rechteck{
6 private:
7 float a,b;
8 public:
9 Rechteck(float x, float y): a(x), b(y) {}
10 ~Rechteck(){}
11 void setze_a(float x){a=x;}
12 float erhalte_a (void) {return a;}
13 void setze_b(float y) {b=y;}
14 float erhalte_b (void) {return b;}
INM12 69
© HfB, 02.12.20, Berk, Eric (904709)
15 void ausgabe(void){
16 cout<<"\nRechteck: a= "<<erhalte_a()
17 <<" b= "<<erhalte_b()<<"\n";
18 }
19 };
20
21 int main(void){
22 Rechteck r1(5.5 f.,7.9f);
23 r1.ausgabe();
24 Rechteck r2=r1;
25 r2.setze_a(7.9f);
26 r2.ausgabe();
27 return 0;
28 }
Schreibweise:
Klassenname::Komponentenname
Beispiel 5.9:
Im Interface der Klasse Rechteck wird die Methode erhalte_a durch die Angabe
ihres Prototyps deklariert: float erhalte_a (void);
In der Implementation ist der Klassenname vor dem Methodennamen anzugeben:
float Rechteck::erhalte_a (void) {return a;}
Damit wird zum Ausdruck gebracht, dass es sich um die Methode erhalte_a aus
der Klasse Rechteck handelt.
1 //Programm Standardparameter.cpp
2 #include <iostream>
3 using namespace std;
4
70 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Bemerkungen:
Zeilen 6–17: Angabe des Klasseninterface der Klasse Rechteck.
Zeilen 20–29: Implementation der Klasse Rechteck.
Zeile 32: Es erfolgt der Konstruktoraufruf von Rechteck, es werden beide
Standardparameter benutzt.
Zeile 34: Es erfolgt der Konstruktoraufruf von Rechteck, es wird ein Stan-
dardparameter benutzt.
Zeile 36: Es erfolgt der Konstruktoraufruf von Rechteck, beide Parameter
werden gesetzt.
INM12 71
© HfB, 02.12.20, Berk, Eric (904709)
Rechteck: a= 1.1 b= 2
Beispiel 5.10:
In unserem Beispiel erstellen wir die Dateien rechteck.h sowie rechteck.cpp.
Die Datei rechteck.h besitzt folgendes Aussehen:
1 //Datei rechteck.h
2
3 #ifndef H_RECHTECK
4 #define H_RECHTECK
5
6 //Schnittstelle der Klasse Rechteck
7 class Rechteck{
8 private:
9 float a,b;
10 public:
11 Rechteck(float x=1,float y=2);
12 ~Rechteck();
13 void setze_a(float);
14 float erhalte_a (void);
15 void setze_b(float);
16 float erhalte_b (void);
17 void ausgabe(void);
18 };
72 INM12
© HfB, 02.12.20, Berk, Eric (904709)
19
20 #endif
1 //Datei rechteck.cpp
2
3 #include <iostream>
4 using namespace std;
5
6 #include "rechteck.h"
7
8 //Implementation der Klasse Rechteck
9 Rechteck::Rechteck(float x,float y){a=x;b=y;}
10 Rechteck::~Rechteck(){}
11 void Rechteck::setze_a(float x){a=x;}
12 float Rechteck::erhalte_a (void) {return a;}
13 void Rechteck::setze_b(float x){b=x;}
14 float Rechteck::erhalte_b (void) {return b;}
15 void Rechteck::ausgabe(void) {
16 cout<<"\nRechteck: a= "<<a<<" b= "
17 <<erhalte_b()<<"\n";
18 }
Die dritte Datei wird schließlich durch das Testprogramm test.cpp bestimmt: Für
deren Übersetzung sind die Informationen aus rechteck.h notwendig.
1 //Datei Test.cpp
2
3 #include "rechteck.h"
4
5 int main(void){
6 Rechteck r1;
7 r1.ausgabe();
8 Rechteck r2(1.1f);
9 r2.ausgabe();
10 Rechteck r3(5.5 f.,7.9f);
11 r3.ausgabe();
12 return 0;
13 }
INM12 73
© HfB, 02.12.20, Berk, Eric (904709)
Für die Entwicklungsumgebung Dev-C++ ist über die Menüfolge „Datei → Neu →
Projekt“ ein C++-Projekt zu erzeugen, in das die genannten Dateien eingefügt wer-
den müssen. Im Menü „Projekt“ steht dafür u. a. der Menüpunkt „Zum Projekt hin-
zufügen“ zur Verfügung.
5.4 Methoden
Die Methoden einer Klasse genießen privilegierten Zugriff auf die Datenelemente dieser
Klasse.
Betrachten wir weitere Methoden, die unsere Klasse Rechteck sinnvoll erweitern.
Beispiel 5.11:
Für die Klasse Rechteck sollen zusätzlich zwei Methoden flaeche und
ist_kleiner_als definiert werden. Dabei dient die Methode flaeche der Größenbe-
stimmung der Rechteckfläche. Die Methode ist_kleiner_als gestattet einen Größen-
vergleich von Rechtecken zueinander. Dabei soll ein Rechteck kleiner sein als ein an-
deres, wenn es eine kleinere Fläche besitzt.
Betrachten wir mögliche Umsetzungen:
float flaeche(void)
Der Wert der Rechteckfläche wird aus den Seitenlängen a und b berechnet. Das
Ergebnis wird als float-Größe zurückgegeben.
Die Methode flaeche lässt sich einfach in folgender Weise definieren:
float Rechteck::flaeche(void){
return a*b;
}
Die Methode prüft, ob ein Rechteck flächenkleiner als ein anderes Rechteck ist. Das
Ergebnis wird als logische Größe in der Deutung wahr oder falsch zurückgegeben.
74 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Dabei bezieht sich die Angabe mit vorangestelltem Parameter r auf das im
Parameterteil angegebene Objekt r (r.flaeche()). Angaben ohne Objektangabe
(flaeche()) beziehen sich auf das Objekt, das die Methode aufruft.
1 // Programm Methoden.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Rechteck{
6 private:
7 float a,b;
8 public:
9 Rechteck(float , float );
10 ~Rechteck();
11 void setze_a(float);
12 float erhalte_a (void);
13 void setze_b(float y);
14 float erhalte_b (void);
15 void ausgabe(void);
16 float flaeche(void);
17 bool ist_kleiner_als (Rechteck r);
18 };
19
20 Rechteck::Rechteck(float x , float y ){a=x;b=y;}
21 Rechteck::~Rechteck(void){}
22 void Rechteck::setze_a(float x){a=x;}
23 float Rechteck::erhalte_a (void) { return a;}
24 void Rechteck::setze_b(float y){b=y;}
25 float Rechteck::erhalte_b (void) { return b;}
26 void Rechteck::ausgabe(void)
27 {cout<<"\nRechteck: a= "<<a<<" b= "
28 <<erhalte_b()<<"\n";
29 }
30 float Rechteck::flaeche(void){
31 return a*b;
32 }
33 bool Rechteck::ist_kleiner_als (Rechteck r){
34 return(flaeche()<r.flaeche());
35 }
36
INM12 75
© HfB, 02.12.20, Berk, Eric (904709)
37 int main(void){
38 Rechteck r1(2.0f,2.0f);
39 r1.ausgabe();
40 Rechteck r2(5.0f,2.1f);
41 r2.ausgabe();
42 cout<<"\nFlaeche von r1: "<<r1.flaeche();
43 cout<<"\nFlaeche von r2: "<<r2.flaeche();
44 if(r1.ist_kleiner_als(r2))
45 cout<<"\n\nr1 ist kleiner r2"<<endl;
46 return 0;
47 }
Im Verlauf des Programms entsteht das folgende Ausgabebild. Dabei erfolgt nur für
den hier zutreffenden Fall, dass r1 eine kleinere Fläche als r2 besitzt, eine Aus-
gabe.
Rechteck: a= 2 b= 2
Rechteck: a= 5 b= 2.1
r1 ist kleiner r2
Beispiel 5.12:
Um die Größe zweier Rechtecke miteinander zu vergleichen, würde man sich doch
gern im Quelltext der Schreibweise r1<r2 statt der bisherigen Lösung
r1.ist_kleiner_als(r2) bedienen.
C++ sieht die Definition einer „<“-Relation für Rechtecke vor, indem man die
Methode des Überladens von Operatoren benutzt.
Mithilfe des Operatorüberladens können (fast) alle Operatoren für eine selbstde-
finierte Klasse mit einer neuen Bedeutung belegt werden. Das Prinzip besteht darin,
den Operator wie eine normale Funktion überladen zu können. Der Funktionsname
leitet sich aus dem festen Bezeichner operator, gefolgt vom Operatorzeichen
selbst, ab. In unserem Fall ergibt sich dann als Name operator<.
Die Definition der neuen Operator-Funktion oder Methode erfolgt in bereits
bekannter Weise, indem lediglich der Name anzupassen ist:
bool Rechteck::operator< (Rechteck r){
return(flaeche()<r.flaeche());
}
76 INM12
© HfB, 02.12.20, Berk, Eric (904709)
1 //Programm Operatoren.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Rechteck{
6 private:
7 float a,b;
8 public:
9 Rechteck(float , float );
10 ~Rechteck();
11 void setze_a(float);
12 float erhalte_a (void);
13 void setze_b(float y);
14 float erhalte_b (void);
15 void ausgabe(void);
16 float flaeche(void);
17 bool ist_kleiner_als (Rechteck r);
18 bool operator< (Rechteck r);
19 };
20
21 Rechteck::Rechteck(float x , float y ){a=x;b=y;}
22 Rechteck::~Rechteck(void){}
23 void Rechteck::setze_a(float x){a=x;}
24 float Rechteck::erhalte_a (void) { return a;}
25 void Rechteck::setze_b(float y){b=y;}
26 float Rechteck::erhalte_b (void) { return b;}
27 void Rechteck::ausgabe(void){
28 cout<<"\nRechteck: a= "<<a<<" b= "
29 <<erhalte_b()<<"\n";
30 }
31 float Rechteck::flaeche(void){
32 return a*b;
33 }
34 bool Rechteck::ist_kleiner_als (Rechteck r){
35 return(flaeche()<r.flaeche());
36 }
37 bool Rechteck::operator< (Rechteck r){
38 return(flaeche()<r.flaeche());
39 }
40
41 int main(void){
42 Rechteck r1(2.0 f.,2.0f);
43 r1.ausgabe();v
44 Rechteck r2(5.0 f.,2.1f);
45 r2.ausgabe();
46 cout<<"\nFlaeche von r1: "<<r1.flaeche();
47 cout<<"\nFlaeche von r2: "<<r2.flaeche();
48 if(r1.ist_kleiner_als(r2))
49 cout<<"\n\nr1 ist kleiner r2"<<endl;
INM12 77
© HfB, 02.12.20, Berk, Eric (904709)
50 if(r1.operator<(r2))
51 cout<<"r1 ist kleiner r2"<<endl;
52 if(r1<r2)
53 cout<<"r1 ist kleiner r2"<<endl;
54 return 0;
55 }
Bei Abarbeitung werden mit allen drei Aufrufformen die gleichen Ausgaben erzielt:
Rechteck: a= 2 b= 2
Rechteck: a= 5 b= 2.1
5.6 Vererbung
Eines der tragenden Konzepte der objektorientierten Programmierung ist das Prinzip der
Vererbung. Aus einer bereits definierten Klasse (Oberklasse) können weitere Klassen
(Unterklassen) abgeleitet werden. Die neuen Klassen können die Oberklasse erweitern
oder einschränken.
Bevor die Vererbung am Beispiel der Klasse Rechteck gezeigt wird, verlassen wir unser
Beispiel kurz und betrachten zur Erklärung einen anderen Sachverhalt:
Beispiel 5.13:
Innerhalb einer Hochschule sollen Informationen zu Studenten und Dozenten
objektorientiert verwaltet werden. Eine erste einfache Lösung besteht darin, eine
Klasse zu definieren, die die Attribute Name und Vorname besitzt. Als Methoden
sollen der Konstruktor und eine Methode ausgabe vorgesehen werden.
Sie sollten in der Lage sein, den vorliegenden Quelltext selbstständig nachzuvoll-
ziehen.
Es wird der neu in C++ im gleichnamigen Header bereitgestellte Datentyp string
genutzt und in der Konstruktordefinition die Initialisierungslistenschreibweise ver-
wendet.
1 //Programm Person.cpp
2 #include <iostream>
3 #include <string>
4 using namespace std;
5
6 class Person {
7 private:
8 string vorname;
9 string nachname;
78 INM12
© HfB, 02.12.20, Berk, Eric (904709)
10 public:
11 Person(string, string);
12 void print(void);
13 };
14
15 Person::Person(string v, string n)
16 :vorname(v),nachname(n){}
17
18 void Person::print(void) {
19 cout <<endl<<nachname<<"," <<vorname;
20 }
21
22 int main(void){
23 Person p ("Alfred","Schulze");
24 p.print();
25 }
Ausgabe:
Schulze,Alfred
Die UML (Unified Modeling Language) dient zur Beschreibung von Klassen mithilfe von
Klassendiagrammen. Eine Klasse wird dabei durch ein Rechteck mit maximal drei Be-
reichen, in denen der Klassenname, gefolgt von den Datenelementen und den Metho-
den, angegeben wird. Dabei kann zusätzlich die Zugriffsart (+ für public, - für private
und # für protected) angegeben werden.
Die Klasse Person lässt sich folgendermaßen angeben:
Person
-vorname:string
-nachname:string
+Person()
+print():void
Unser Beispiel, die Klasse Person, soll nun in der Art erweitert werden, dass eine Un-
terscheidung zwischen Studenten und Dozenten erfolgen kann. Man könnte jetzt jeweils
eine neue Klasse erstellen und durch Kopieren der Daten sichern, dass die betreffenden
Vor- und Nachnamen in der neuen Klasse bereitstehen.
Dem Nutzer von Klassen bietet sich jedoch die Möglichkeit, durch Vererbung eine be-
reits definierte Klasse um Eigenschaften und Methoden zu erweitern.
Bei der Vererbung wird von einer Oberklasse (auch Super-, Vater- oder Basisklasse) eine
Unterklasse (auch Sub- oder Kindklasse) abgeleitet.
INM12 79
© HfB, 02.12.20, Berk, Eric (904709)
Oberklasse
Unterklasse
Die Unterklasse stellt eine Spezialisierung der Oberklasse dar, die Oberklasse ist als
Generalisierung der Unterklasse aufzufassen.
Für unser Beispiel soll ein Student zusätzlich durch die Angabe seines Studiengangs und
ein Dozent durch ein Lehrgebiet charakterisiert werden. Die durch Vererbung entste-
henden Klassen sollen Student und Dozent heißen. Die zu erzeugenden Klassendefi-
nitionen und die Vererbungsstruktur haben in der UML folgendes Aussehen:
Beispiel 5.14:
Person
#vorname:string
#nachname:string
+Person()
+print():void
Dozent Student
-lehrgebiet:string -studiengang:string
+Dozent() +Student()
+print():void +print():void
80 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Die Ableitung von Klassen wird in C++ durch folgende Schreibweise ausgedrückt:
class Unterklasse: public Oberklasse {
...
};
Der Compiler wird damit angewiesen, alle Klassenelemente aus der Oberklasse in
die Unterklasse zu übernehmen.
Die durch das Schlüsselwort public durchgeführte Vererbung ist die üblichste Art
und ändert die Zugriffsspezifikationen nicht. Dies bedeutet, dass innerhalb der
Unterklasse nicht direkt auf private deklarierte Klassenelemente der Oberklasse
zugegriffen werden kann.
Hier besteht die Möglichkeit, das Schlüsselwort private durch protected aus-
zutauschen oder den Zugriff über die in der Regel public vereinbarten setter- und
getter-Methoden zu realisieren.
Im Gegensatz zu anderen Methoden werden die Konstruktoren beim Ableiten von
Klassen aber nicht vererbt. Sie sind aber notwendig, um das Klasseninventar der
Oberklasse bereitzustellen. Wenn nicht der implizit generierte Default-Konstruktor
verwendet werden soll, muss der Konstruktoraufruf für die Oberklasse explizit an-
gegeben werden.
Erläuterung:
Zeile 1: Ableitung der Klasse Student von der Klasse Person.
Zeile 3: studiengang wird als zusätzliches Attribut vereinbart.
Zeilen 5–6: Der Konstruktor der Klasse und eine angepasste print-Funktion
sind zu definieren.
Zeilen 9–11: Der Konstruktor besitzt drei Parameter, die die Werte für vorname,
nachname und studiengang bereitstellen. Da die Datenelemente
vorname und nachname in der Klasse Person verwaltet werden,
sorgt der explizite Konstruktoraufruf für diese Klasse Person(v,n)
INM12 81
© HfB, 02.12.20, Berk, Eric (904709)
Erläuterung:
Zeile 1: Ableitung der Klasse Dozent von der Klasse Person.
Zeilen 3: lehrgebiet wird als zusätzliches Attribut vereinbart.
Zeilen 5, 6: Der Konstruktor der Klasse und eine angepasste print-Funktion
sind zu definieren.
Zeilen 9–11: Der Konstruktor besitzt drei Parameter, die die Werte für vorname,
nachname und lehrgebiet bereitstellen. Da die Datenelemente
vorname und nachname in der Klasse Person verwaltet werden,
sorgt auch hier der explizite Konstruktoraufruf für diese Klasse
Person(v,n) für deren Initialisierung. Schließlich erhält das
Datenelement lehrgebiet als weiteres Argument über die Ini-
tialisierungsliste seinen Wert. Der Anweisungsblock bleibt leer.
Zeile 13: In der für die Klasse zu redefinierenden print-Methode wird zur
Ausgabe von vorname und nachname explizit die Methode der
Klasse Person aufgerufen.
1 int main(void){
2 Person p ("Alfred","Schulze");
3 p.print();
4 Student s ("Hans","Meyer","Informatik");
5 s.print();
6 Dozent d ("Rainer","Lehmann","Programmierung");
82 INM12
© HfB, 02.12.20, Berk, Eric (904709)
7 d.print();
8
9 return 0;
10 }
Ausgabe:
Schulze,Alfred
Meyer,Hans,Student im Studiengang Informatik
Lehmann,Rainer,Dozent fuer Programmierung
Beispiel 5.15:
Kehren wir nochmals zum Sachverhalt Rechteck aus Beispiel 5.11 zurück. Betrachtet
man die Seitenlängen des Rechtecks r1 kann man nachvollziehen, dass ein Quadrat
auch als Spezialisierung eines Rechtecks aufgefasst werden kann. Geht man von
einer Klasse Rechteck aus, bietet sich die Klasse Quadrat als abgeleitete Klasse an.
Im Sinne der Spezialisierung wollen wir auch hier formal die Ableitung einer Klas-
se Quadrat von einer Klasse Rechteck betrachten. Worin besteht diese Spezialisie-
rung? Wir hatten gerade festgestellt, dass ein Quadrat als Rechteck mit gleich langen
Seiten aufzufassen ist.
Bei der Vererbung erbt die abgeleitete Klasse von der Oberklasse das aus Attributen
und allgemeinen Methoden bestehende Klasseninventar.
Für unser Vorhaben bedeutet dies, es muss kein zusätzlicher Speicherplatz für die
Seitenlänge des Quadrates vorgesehen werden, denn die Bezeichner a und b aus
der Klasse Rechteck stehen bereits zur Verfügung. Es ist aber noch dafür Sorge zu
tragen, dass die Seitenlängen gleich groß sind.
Soll die Klasse Quadrat von einer Klasse Rechteck abgeleitet werden, muss diese
Vererbungshierarchie in der Klassendefinition von Quadrat umgesetzt sein. Man
schreibt:
class Quadrat: public Rechteck{
...
};
INM12 83
© HfB, 02.12.20, Berk, Eric (904709)
1 //Programm Quadrat_1.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Rechteck{
6 private:
7 float a,b;
8 public:
9 void setze_a(float x){a=x;}
10 float erhalte_a (void) {return a;}
11 void setze_b(float y){b=y;}
12 float erhalte_b (void) {return b;}
13 void ausgabe(void){
14 cout<<"\nRechteck: a= "<<a
15 <<" b= "<<erhalte_b()<<"\n";
16 }
17 };
18
19 class Quadrat: public Rechteck{
20
21 };
22
23 int main(void){
24 cout<<"\nRechteck:"<<endl;
25 Rechteck r;
26 r.setze_a(3.1f);
27 r.setze_b(8.0f);
28 r.ausgabe();
29 cout<<"\nQuadrat:"<<endl;
30 Quadrat q;
31 q.setze_a(1.4f);
32 q.setze_b(1.4f);
33 q.ausgabe();
34 return 0;
35 }
Erläuterung:
Zeilen 6–7: Die Basisklasse Rechteck besitzt nur private deklarierte Da-
tenelemente. Der Zugriff darauf kann deshalb nur über public
deklarierte Methoden erfolgen.
Zeile 25: Der Standardkonstruktor der Klasse Rechteck wird gerufen.
Zeilen 26–27: Die korrekte Initialisierung der Datenelemente erfolgt über die
Methoden setze_a und setze_b.
84 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Rechteck: a= 3.1 b= 8
Quadrat:
Beispiel 5.16:
Im folgenden Beispiel soll die Klassendefinition von Quadrat sinnvoll erweitert
werden, indem ein Konstruktor für die Klasse Quadrat explizit vereinbart wird
und auch der Aufruf des Konstruktors der Oberklasse explizit erfolgen soll.
Die Konstruktordefinition für die Klasse Quadrat hat folgendes Aussehen:
Quadrat(float x):Rechteck(x,x){}
Bei dessen Aufruf werden die beiden Seitenlängen eines Rechteckobjektes mit dem
gleichen, durch die Variable x bereitgestellten float-Wert initialisiert. Da damit
die Wertebereitstellung erfolgt ist, kann der eigentliche Anweisungsblock {} in
diesem Fall leer bleiben. Bedingung für diese Konstruktion ist jedoch, dass ein Kon-
struktor der Klasse Rechteck, der zwei Parameter verarbeitet, auch vorhanden ist.
Schließlich wird die Methode ausgabe aus der Klasse Rechteck in der
Klasse Quadrat überschrieben (redefiniert), damit bei Instanzen der
Klasse Quadrat nur die Ausgabe einer Seitenlänge erfolgt: void
ausgabe(){cout<<"\nQuadrat: a= "<<a<<"\n";}
Für ein Objekt q der Klasse Quadrat erfolgt deren Nutzung in der Form:
q.ausgabe();.
Möchte man auf die von der Klasse Rechteck geerbte gleichnamige Methode zu-
greifen, schreibt man: q.Rechteck::ausgabe();.
1 // Programm Quadrat_2.cpp
2 #include <iostream>
3 using namespace std;
4 class Rechteck{
5 protected:
6 float a,b;
7 public:
8 Rechteck(float x=1,float y=2){a=x;b=y;}
9 void setze_a(float x){a=x;}
10 float erhalte_a (void) {return a;}
11 void setze_b(float y){b=y;}
12 float erhalte_b (void) {return b;}
13 void ausgabe(void){
14 cout<<"\nRechteck: a= "<<a<<" b= "
INM12 85
© HfB, 02.12.20, Berk, Eric (904709)
15 <<erhalte_b()<<"\n";
16 }
17 };
18
19 class Quadrat: public Rechteck{
20 public:
21 Quadrat( float x):Rechteck(x,x){}
22 void ausgabe(){cout<<"\nQuadrat: a= "<<a<<"\n";}
23 };
24
25 int main(void){
26 cout<<"\nRechteck:"<<endl;
27 r.ausgabe();
28 r.ausgabe();
29 cout<<"\nQuadrat:"<<endl;
30 Quadrat q(9.9f);
31 q.ausgabe(); //Methode ausgabe von Quadrat
32 q.Rechteck::ausgabe(); //Methode ausgabe von Rechteck
33 return 0;
34 }
Erläuterung:
Zeile 8: Der explizite Konstruktor der Klasse Rechteck wird in der Form de-
finiert, dass beide Parameter Standardargumente zugewiesen bekom-
men. Somit steht dieser Konstruktor auch als Standardkonstruktor
bereit.
Zeile 21: In der expliziten Definition des Konstruktors von Quadrat erfolgt der
Aufruf des expliziten Konstruktors von Rechteck. Beide Rechteck-
seiten müssen mit dem Wert der Länge der Quadratseite initialisiert
werden.
Zeile 30: Aufruf des expliziten Konstruktors für Quadrat.
Zeile 31: Nutzung der Methode ausgabe von Quadrat.
Zeile 32: Nutzung der geerbten Methode ausgabe von Rechteck.
Ausgabe:
Rechteck:
Rechteck: a= 1 b= 2
Quadrat:
Quadrat: a= 9.9
5.7 Klassenhierarchien
In der Regel sind mehrere Klassen zur Beschreibung eines problemorientierten Lösungs-
ansatzes notwendig. Stehen diese Klassen in einem Vererbungsverhältnis, führen wei-
terführende Spezialisierungen zur Bildung von Klassenhierarchien. Gerade diese Spe-
86 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel 5.17:
Im Folgenden wird beispielhaft eine Klassenhierarchie für geometrische Figuren auf-
gebaut. Als oberste Klasse in der Hierarchie steht die Klasse Figur. Hier werden als
Attribute die Seitenlängen a und b sowie setter- und getter-Methoden und die
Methode ausgabe bereitgestellt. Die schon bekannten Klassen Rechteck
und Quadrat ordnen sich in der ersten und zweiten Ableitungsebene ein. Für die
Klasse Quadrat sind nur der Konstruktor, der Destruktor und eine charakteristi-
sche Ausgabeanweisung notwendig. In der ersten Ableitungsebene wird außerdem
die Klasse Dreieck hinzugefügt, deren Objekte durch Grundseite, also a, und Hö-
he, also b, bestimmt werden. Methoden für die Flächenbestimmung, für den Grö-
ßenvergleich und eine Ausgabe sind analog zur Klasse Rechteck bereitzustellen.
Daraus entsteht die folgende Klassenhierarchie:
Figur
# a: float
# b: float
+Figur()
+~Figur()
+setze_a:void
+setze_b:void
+erhalte_a:float
+erhalte_b: float
+ausgabe(): void
Rechteck Dreieck
+ Rechteck() +Dreieck
+~ Rechteck +~Dreieck
+flaeche(): float +flaeche():void
+ausgabe(): void +ausgabe(): void
+operator<(): bool +operator<(): bool
Quadrat
+Quadrat
+~Quadrat
+ ausgabe():void
INM12 87
© HfB, 02.12.20, Berk, Eric (904709)
1 //Programm Klassenhierarchie.cpp
2 #include <iostream>
3 using namespace std;
4
5 class Figur{
6 protected:
7 float a,b;
8 public:
9 Figur(float x, float y);
10 ~Figur(void);
11 void setze_a(float x);
12 float erhalte_a (void);
13 void setze_b(float y);
14 float erhalte_b (void);
15 void ausgabe(void);
16 };
17
18 Figur::Figur(float x, float y){a=x;b=y;}
19 Figur::~Figur(void){}
20 void Figur::setze_a(float x){a=x;}
21 float Figur::erhalte_a (void) {return a;}
22 void Figur::setze_b(float y){b=y;}
23 float Figur::erhalte_b (void) {return b;}
24 void Figur::ausgabe(void){
25 cout<<"\na= "<<erhalte_a()
26 <<" b= "<<erhalte_b();
27 }
28 class Rechteck:public Figur{
29 public:
30 Rechteck(float x=1, float y=1);
31 ~Rechteck(void);
32 float flaeche(void);
33 bool operator< (Rechteck r);
34 void ausgabe(void);
35 };
36 Rechteck::Rechteck(float x, float y)
37 :Figur(x,y){}
38 Rechteck::~Rechteck(void){}
39 float Rechteck::flaeche(void){
40 return a*b;
41 }
42 bool Rechteck::operator< (Rechteck r){
43 return(flaeche()<r.flaeche());
44 }
45 void Rechteck::ausgabe(){
46 cout<<"\nRechteck mit den Seitenlangen:";
47 Figur::ausgabe();
48 cout<<" und Flaeche: "<<flaeche();
49 }
50
51 class Quadrat: public Rechteck{
52 public:
53 Quadrat(float x);
54 ~Quadrat(void);
88 INM12
© HfB, 02.12.20, Berk, Eric (904709)
55 void ausgabe(void);
56 };
57 Quadrat::Quadrat(float x):Rechteck(x,x){}
58 Quadrat::~Quadrat(void){}
59 void Quadrat::ausgabe(){
60 cout<<"\nQuadrat mit der Seitenlange:\n";
61 cout<<erhalte_a();
62 cout<<" und Flaeche: "<<flaeche();
63 }
64
65 class Dreieck:public Figur{
66 public:
67 Dreieck(float x=1, float y=1);
68 ~Dreieck(void);
69 float flaeche(void);
70 bool operator< (Dreieck d);
71 void ausgabe(void);
72 };
73 Dreieck::Dreieck(float x, float y)
74 :Figur(x,y){}
75 Dreieck::~Dreieck(void){}
76 float Dreieck::flaeche(void){
77 return 1.0/2.0*a*b;
78 }
79 bool Dreieck::operator< (Dreieck d){
80 return(flaeche()<d.flaeche());
81 }
82 void Dreieck::ausgabe(){
83 cout<<"\nDreieck mit Grundseite und Hoehe:";
84 Figur::ausgabe();
85 cout<<" und Flaeche: "<<flaeche();
86 }
87
88 int main(void){
89 Figur f(5.1f,3.3f);
90 cout<<"Figur mit der Seitenlange:";
91 f.ausgabe();
92 Rechteck r1;
93 r1.ausgabe();
94 Rechteck r2(4.1f);
95 r2.ausgabe();
96 r2.setze_b(8.0f);
97 r2.ausgabe();
98 Quadrat q(9.9f);
99 q.ausgabe();
100 Dreieck d(4.1f,3.0f);
101 d.ausgabe();
102 if(r1<r2) cout<<"\nr1<r2"<<endl;
103 if(r1<q) cout<<"r1<q"<<endl;
104
105 return 0;
106 }
INM12 89
© HfB, 02.12.20, Berk, Eric (904709)
Erläuterung:
Zeilen 5–16: Interface der Basisklasse Figur.
Zeile 6: Die Klassenattribute sind in einer Vererbungshierarchie
protected zu setzen.
Zeile 9: Im Konstruktor werden keine Standardparameter vorgesehen.
Zeile 18–27: Methodendefinitionen zur Klasse Figur.
Zeilen 29–36: Interface der Klasse Rechteck.
Zeilen 37–50: Methodendefinitionen zur Klasse Rechteck.
Zeile 37, 38: Im Konstruktor der Klasse Rechteck, er benutzt Standardargu-
mente (Zeile 31), wird explizit der Konstruktor der Klasse Figur
aufgerufen.
Zeilen 40–45: Die Methoden flaeche und operator< werden für die
Klasse Rechteck definiert.
Zeilen 46–50: Die Methode ausgabe wird für die Klasse Rechteck redefi-
niert. Dabei wird die Methode ausgabe der Klasse Figur auf-
gerufen.
Zeilen 52–57: Interface der Basisklasse Quadrat.
Zeilen 58–64: Methodendefinitionen zur Klasse Quadrat.
Zeilen 60–64: Die Methode ausgabe der Klasse Quadrat wird redefiniert. Da-
bei wird die geerbte Methode erhalte_a der Klasse Figur und
die aus Rechteck geerbte Methode flaeche aufgerufen.
Zeilen 66–73: Interface der Klasse Dreieck.
Zeilen 74–87: Methodendefinitionen zur Klasse Dreieck.
Zeilen 74–75: Im Konstruktor der Klasse Dreieck, er benutzt Standardargumen-
te (Zeile 68), wird explizit der Konstruktor der Klasse Figur auf-
gerufen.
Zeilen 77–82: Die Methoden flaeche und operator< werden für die
Klasse Dreieck definiert.
Zeilen 83–87: Die Methode ausgabe der Klasse Dreieck wird redefiniert. Da-
bei wird die Methode ausgabe der Klasse Figur aufgerufen.
Zeilen 89–107: Nutzung der Klassen Figur, Rechteck, Quadrat, Dreieck.
Zeile 93: Aufruf des Standardkonstruktors von Rechteck.
Zeile 95: Aufruf des Konstruktors von Rechteck mit einem Parameter.
Zeile 99: Aufruf des Konstruktors von Quadrat.
Zeile 101: Aufruf des Konstruktors von Dreieck.
Zeile 103: Vergleich der Rechteckobjekte r1 und r2 mithilfe des
Operators <.
Zeile 104: Vergleich der Rechteckobjekte r1 und des Quadratobjekts q mit-
hilfe des Operators <.
90 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Zusammenfassung
Das letzte Kapitel vermittelte Ihnen einen Überblick zum objektorientierten Program-
mieren mit C++. Die Begriffe Klasse und Objekt sind Grundlage für das objektorientierte
Programmiermodell. Mit der Definition einer Klasse wird ein neuer Datentyp erzeugt,
der Datenelemente (Attribute) und Methoden zum Verändern dieser Daten kapselt. Es
besteht die Möglichkeit, die Sichtbarkeit der Klassenbestandteile und damit den Zugriff
auf diese zu steuern.
Die Klassendefinition beschreibt als Bauplan die Struktur der zu instanzierenden
Objekte.
Objekte/Instanzen der Klasse werden mit einem Konstruktoraufruf erzeugt und mit dem
Destruktor wieder zerstört.
Konstruktoren besitzen den gleichen Bezeichner wie die Klasse und dienen dazu, die
Attribute mit Startwerten zu initialisieren. Konstruktoren besitzen keinen Rückgabetyp.
Destruktoren erhalten ebenfalls den gleichen Bezeichner wie die zugehörige Klasse. Die-
sem wird das Zeichen „~“ vorangestellt, er hat keinen Rückgabetyp und keine Parameter.
Wird kein Konstruktor explizit vereinbart, wird automatisch der Standardkonstruktor
zur Verfügung gestellt.
Ein wichtiges Konzept der objektorientierten Programmierung ist die Vererbung. Neue
Klassen werden auf Basis bestehender definiert. Dabei findet eine Spezialisierung von
der Ober- zur Unterklasse statt. Es entsteht eine Vererbungshierarchie. Die Unterklasse
erbt im Allgemeinen alle Eigenschaften/Attribute und Methoden der Oberklasse. Bei
diesem Vorgang können zusätzliche Attribute und Methoden definiert oder Methoden
redefiniert (überschrieben) werden.
Durch die dargestellten Beispiele sollten Sie in der Lage sein, eigene einfache Problem-
stellungen objektorientiert in C++ umzusetzen.
Definieren Sie eine Klasse Vektor2P zur Benutzung solcher Vektoren mit folgen-
den Forderungen:
Definieren Sie für diese Klasse einen Konstruktor, der es ermöglicht, einerseits
durch die Angabe von vier ganzen Zahlen Instanzen der Klasse Vektor2P ent-
sprechend der oben angegebenen Definition zu erzeugen und andererseits durch
die Angabe von zwei ganzen Zahlen einen Ortsvektor zum Punkt B (xb, yb) bereit-
zustellen (xa = ya = 0).
Stellen Sie eine Methode betrag bereit, die den Betrag des Vektors (Länge der
Strecke AB) ermittelt.
INM12 91
© HfB, 02.12.20, Berk, Eric (904709)
Für den Betrag gilt hier: | AB | (x b x a )2 ( y b x a )2
Für xa, ya, xb und yb sind die jeweils konkreten Werte vorzusehen.
Überladen Sie den Operator == der Klasse.
Dabei sollen zwei Vektoren dann gleich sein, wenn diese den gleichen Betrag
besitzen.
5.2 Definieren Sie eine Klasse Datum und davon abgeleitet eine Klasse Feiertag,
mit denen es möglich ist, folgende main-Routine abzuarbeiten:
int main(){
Feiertag T1(24,12,2012,"Heiligabend");
Feiertag T2(24,12,2012,"Christmas Eve");
T1.drucke();
T2.drucke();
if(T1==T2)printf("\nGleichheit");
return 0;
}
Definieren Sie dazu eine Klasse Datum, die auf Basis der drei Ganzzahlvariablen
tag, monat und jahr Terminangaben verwalten kann.
Stellen Sie einen Konstruktor der Klasse Datum bereit, der alle Klassenattribute
setzt.
Diese Klassenattribute sollen außerdem über getter- und setter-Methoden
(setzeTag, setzeMonat, setzeJahr, erhalteTag, erhalteMonat,
erhalteJahr) verfügbar gemacht werden.
In der Methode drucke der Klasse Feiertag soll die Methode ausgabeDatum
der Klasse Datum aufgerufen werden und auf der gleichen Zeile, mit einem Kom-
ma getrennt, die Bezeichnung des Feiertags hinzugefügt werden.
92 INM12
© HfB, 02.12.20, Berk, Eric (904709)
z1=&fzahl1;
z2=&fzahl2;
e=&ergebnis;
*e=*z1+*z2;
return 0;
}
INM12 93
© HfB, 02.12.20, Berk, Eric (904709)
1.3 Im Programm fehlt die Initialisierung des Zeigers. Er referenziert keine definierte
Speicherzelle.
Die Anweisung *int_zeiger=int_zahl1; ist durch die Anweisung
int_zeiger=&int_zahl1; zu ersetzen. Das geänderte und nunmehr korrekte
Programm lautet:
//Aufgabe_1_3.c
//korrekt
#include <stdio.h>
int main(){
int int_zahl1,int_zahl2,*int_zeiger;
int_zahl1=123456;
int_zeiger=&int_zahl1;
int_zahl2=*int_zeiger;
printf("\n%i %i",int_zahl1,int_zahl2);
return 0;
}
2.1 //Aufgabe_2_1.c
#include <stdio.h>
int main(){
float wert, pwert, psatz;
ausgabe(psatz);
return 0;
}
94 INM12
© HfB, 02.12.20, Berk, Eric (904709)
volumen(durchm, hoehe,vol)
A: Hinweis
Ausgabe: vol
Prozedurdefinition volumen:
volumen( d, h, v)
{ Input: d, h (Durchmesser, Hoehe) … reell >=0
Output: v (Volumen)
}
v:=pi/4*d*d*h
Quelltext:
//Aufgabe_2_2.c
#include <stdio.h>
int main(){
return 0;
}
INM12 95
© HfB, 02.12.20, Berk, Eric (904709)
2.3 //Aufgabe_2_3.c
#include <stdio.h>
int main(){
int a=-33,b=0,c=12;
printf("Ergebniswert: %d\n",sign(a));
printf("Ergebniswert: %d\n",sign(b));
printf("Ergebniswert: %d",sign(c));
return 0;
}
int sign(int gz){
int vz=0;
if(gz<0) vz=-1;
if(gz>0) vz=1;
return vz;
}
//Aufgabe 2.3_Zeiger.c
#include <stdio.h>
void sign(int *gz,int *vz);
int main(){
int a=-33,b=0,c=12,e;
2.4 //Aufgabe_2_4.c
#include <stdio.h>
#include <math.h>
int main(){
float x, eps;
printf("x= "); scanf("%f",&x);
printf("eps= ");scanf("%f",&eps);
96 INM12
© HfB, 02.12.20, Berk, Eric (904709)
system("pause");
return 0;
}
z=x;
summe=x;
do{
i=i+2;
z=-z*x*x;
s=z/i;
summe=summe+s;
}
while(fabs(s)>eps*fabs(summe));
return summe;
}
3.1 In den Lösungen wird die nicht behandelte, aber für Felder sinnvolle
Präcompilerdirektive #define zur Angabe der Anzahl der Feldelemente
benutzt, die hier in der Art #define bezeichner ersetzung verwendet
wird. Der Ausdruck #define N 5 sorgt dafür, dass im Quelltext das Zeichen N
durch das Zeichen 5 ersetzt wird. So bleibt die Behandlung der Feldgröße relativ
flexibel.
Felder sind zugleich Zeiger, die das erste Feldelement referenzieren. Somit ist der
Zugriff auf die Feldelemente in Feld- und Zeigerschreibweise möglich.
Entsprechend finden Sie drei mögliche Quelltexte für eine Lösung.
Variante 1: Indexschreibweise
//Aufgabe_3_1a_Feld.c
#include <stdio.h>
#define N 5
int main(){
float messwerte[N]={0.5f,1.72f,3.78f,-0.45f,11.8f};
float min,max,durchschnitt,summe,hilf;
int lauf;
summe=min=max=messwerte[0];
for (lauf=1;lauf<N;lauf++){
hilf=messwerte[lauf];
summe+=hilf;
if (hilf<min) min=hilf;
if (hilf>max) max=hilf;
INM12 97
© HfB, 02.12.20, Berk, Eric (904709)
}
durchschnitt=summe/N ;
printf("Ergebnisse:\nMinimalwert= %f\n",min);
printf("Maximalwert= %f\n",max);
printf("Durchschnitt= %f",durchschnitt);
return 0;
}
//Aufgabe_3_1_vordef_Zeiger.c
#define N 5
#include <stdio.h>
int main(){
float messwerte[N]={0.5f,1.72f,3.78f,-0.45f,11.8f};
float min,max,durchschnitt,summe,hilf;
int lauf;
summe=min=max=*messwerte;
for (lauf=1;lauf<N;lauf++) {
hilf=*(messwerte+lauf);
summe+=hilf;
if (hilf<min) min=hilf;
if (hilf>max) max=hilf;
}
durchschnitt=summe/N ;
printf("Ergebnisse:\nMinimalwert= %f\n",min);
printf("Maximalwert= %f\n",max);
printf("Durchschnitt= %f",durchschnitt);
return 0;
}
//Aufgabe_3_1_eigendef_Zeiger.c
#include <stdio.h>
#define N 5
int main(){
float messwerte[N]={0.5f,1.72f,3.78f,-0.45f,11.8f};
float min,max,durchschnitt,summe,hilf;
float *flzeiger;
int lauf=1;
flzeiger=messwerte;
summe=min=max=*flzeiger;
do {
flzeiger++;
hilf=*(flzeiger);
summe+=hilf;
98 INM12
© HfB, 02.12.20, Berk, Eric (904709)
if (hilf<min) min=hilf;
if (hilf>max) max=hilf;
lauf++;
}
while(lauf<N);
durchschnitt=summe/N ;
printf("Ergebnisse:\nMinimalwert= %f\n",min);
printf("Maximalwert= %f\n",max);
printf("Durchschnitt= %f",durchschnitt);
return 0;
}
3.2 Für die zu definierende Funktion ergibt sich der Rückgabetyp float. Die Sum-
me der Gleitkommazahlen führt wieder zu einer Gleitkommazahl. Zwei Argu-
mente werden übergeben. An erster Stelle der Parameterliste steht der Feldname.
Jedes Feld ist auch als Zeiger aufzufassen. Hieraus leitet sich der Typ float*
(Zeiger auf float) dafür ab. Der zweite Parameter beinhaltet die Anzahl der
Feldelemente und ist somit mit dem Typ int zu definieren. Die beiden lokalen
Parameter i und summe dienen der eigentlichen Summation. Die Beschrei-
bung dieses Algorithmenteiles erfolgt in klassischer Indexschreibweise, auch die
Zeigerschreibweise wäre möglich. Nach der letzten Summenbildung wird das
Ergebnis über die return-Anweisung an das rufende Programm vermittelt.
Im Hauptprogramm erfolgt der Aufruf der Funktion der Einfachheit halber in
einer printf-Anweisung. In der Gesamtheit von Prototypvereinbarung, Auf-
rufprogramm und Funktion ergibt sich die Lösung der Aufgabe.
//Aufgabe_3_2.c
#include <stdio.h>
int main() {
float vektor[]={1.5f,3.1f,8.6f,-7.05f,3.83f,
-4.0f,6.4f,11.09f,30.0f,-5.1f};
INM12 99
© HfB, 02.12.20, Berk, Eric (904709)
//Aufgabe_3_3.c
#define MAX 40
#include <stdio.h>
#include <string.h>
void ersetze_zeichen(char *s,char zei_alt,char zei_neu);
int main(){
unsigned char zkette[MAX];
ersetze_zeichen(zkette,'e','x');
printf("\n%s",zkette);
return 0;
}
100 INM12
© HfB, 02.12.20, Berk, Eric (904709)
}
}
3.4 Im main-Modul wird das Feld ergebnis mit 20 Elementen für die Aufnahme
der resultierenden Zeichenkette vereinbart. Damit kann der Aufruf
zeik_verbind("PROGRAMM","ENTWICKLUNG",ergebnis); erfolgen. Die
benutzen formalen Parameter der Funktion zeik_verbind sind t1, t2 und
erg.
Der Algorithmus lässt sich gut, unabhängig von der Art der Beschreibung der
Feldelemente, im folgenden Schema darstellen. Die Zeichenketten sind jeweils
ohne ihr Endezeichen lückenlos aneinanderzufügen und insgesamt mit dem Zei-
chenkettenende abzuschließen (erg19='\0').
//Aufgabe_3_4_Feld.c
#include <stdio.h>
#include <string.h>
int main() {
char ergebnis[20];
zeik_verbind("PROGRAMM","ENTWICKLUNG",ergebnis);
printf("\n%s",ergebnis);
return 0;
}
INM12 101
© HfB, 02.12.20, Berk, Eric (904709)
}
neui+=1;
i=0;
while(t2[i]!='\0'){
erg[neui+i]=t2[i];
i++;
}
erg[neui+i]='\0';
}
// Aufgabe_3_4_Zeiger_1.c
#include <stdio.h>
int main() {
char ergebnis[20];
zeik_verbind("PROGRAMM","ENTWICKLUNG",ergebnis);
printf("\n%s",ergebnis);
return 0;
}
102 INM12
© HfB, 02.12.20, Berk, Eric (904709)
// Aufgabe_3_4_Zeiger_2.c
#include <stdio.h>
int main(){
char ergebnis[20];
zeik_verbind("PROGRAMM","ENTWICKLUNG",ergebnis);
printf("\n%s",ergebnis);
return 0;
}
4.1 Im Vergleich zum Heftbeispiel 4.5 stellt die Aufgabe das Ansprechen der einzel-
nen Strukturkomponenten in den Fokus. Weiterhin ist zu beachten, dass die Zu-
weisung der Zeichenketten in C über strcpy (aus string.h) erfolgen muss.
Dies ist beim Bereitstellen der Listenstruktur und der Initialisierung des Listen-
kopfes bereits gut erkennbar.
//Aufgabe 4_1.c*/
#include <stdio.h>
#include <string.h>
struct datum {
int tag, monat, jahr;
};
struct person {
char Name[35], Vorname[15];
int Nummer;
struct datum geboren;
struct person *next;
};
int main(){
struct person *LKopf;
struct person *aktElem;
strcpy(LKopf->Name, "Meyer");
strcpy(LKopf->Vorname, "Rainer");
LKopf->Nummer=1;
(*LKopf).geboren.tag=8;
(*LKopf).geboren.monat=12;
(*LKopf).geboren.jahr=2000;
LKopf->next=NULL;
aktElem=LKopf;
printListe(LKopf);
INM12 103
© HfB, 02.12.20, Berk, Eric (904709)
aktElem=addLElem(aktElem,2,"Schulze","Gerd",12,4,1990);
aktElem=addLElem(aktElem,3,"Lehmann","Karin",21,6,1994);
printListe(LKopf);
return 0;
}
neuesElement->Nummer =p_Nummer;
strcpy(neuesElement->Name, p_Name);
strcpy(neuesElement->Vorname,p_Vorname);
(*neuesElement).geboren.tag=p_tag;
(*neuesElement).geboren.monat=p_monat;
(*neuesElement).geboren.jahr=p_jahr;
neuesElement->next=NULL;
return neuesElement;
}
}
printf("Nummer: %d\n",akt->Nummer);
printf("Name: %s\nVorname: %s\n",
akt->Name,
akt->Vorname);
printf("Geb.-Datum: %2i.%2i.%2i\n\n",
(*akt).geboren.tag,
(*akt).geboren.monat,
(*akt).geboren.jahr);
akt=akt->next;
}
}
104 INM12
© HfB, 02.12.20, Berk, Eric (904709)
4.3 Die Parametervermittlung erfolgt „call by reference“. Die Adresse des so über-
gebenen Listenanfangs wird gesichert. Das neu generierte Listenelement erhält
die Wertzuweisung für Zahl. Als Adresse des Nachfolgerelements wird alte
Anfangsadresse der Liste zugewiesen.
addLElementAnf(&LKopf,111);
addLElementAnf(&LKopf,222);
addLElementAnf(&LKopf,333);
INM12 105
© HfB, 02.12.20, Berk, Eric (904709)
//Aufgabe_5_1a.cpp
#include <iostream>
#include <cmath>
using namespace std;
class Vektor2P{
private:
int xa,ya,xb,yb;
public:
Vektor2P (int p1,int p2,int p3=0,int p4=0){
xb=p1; yb=p2;xa=p3;ya=p4;
}
float betrag(void){
return sqrt((xa-xb)*(xa-xb)+(ya-yb)*(ya-yb));
}
bool operator==(Vektor2P v){
return betrag()==v.betrag();
}
void ausgabe(void){
cout<<"\nVektor: A("<<xa<<","<<ya
<<")-->B("<<xb<<","<<yb<<")"<<endl;
}
};
int main(){
Vektor2P v1 (1,1,2,3);
Vektor2P v2 (2,2);
Vektor2P v3 (2,2,0,0);
v1.ausgabe();
v2.ausgabe();
v3.ausgabe();
if(v2==v3)
cout<<"\nv2 und v3 sind identisch."<<endl;
else
cout<<"\nv2 und v3 sind nicht identisch."<<endl;
if(v1==v3)
cout<<"\nv1 und v3 sind identisch."<<endl;
else
cout<<"\nv1 und v3 sind nicht identisch."<<endl;
return 0;
}
106 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Benutzt man diese Variante und trennt die Schnittstelle und Implementation für
die Klasse Vektor, ergibt sich:
//Aufgabe_5_1b.cpp
#include <iostream>
#include <cmath>
using namespace std;class Vektor2P{
private:
int xa,ya,xb,yb;
public:
Vektor2P (int, int, int, int);
float betrag(void);
bool operator==(Vektor2P);
void ausgabe(void);
};
void Vektor2P::ausgabe(void){
cout<<"\nVektor: A("<<xa<<","<<ya
<<")-->B("<<xb<<","<<yb<<")"<<endl;
}
int main(){
Vektor2P v1 (1,1,2,3);
Vektor2P v2 (2,2);
Vektor2P v3 (2,2,0,0);
v1.ausgabe();
v2.ausgabe();
v3.ausgabe();
INM12 107
© HfB, 02.12.20, Berk, Eric (904709)
if(v2==v3)
cout<<"\nv2 und v3 sind identisch."<<endl;
else
cout<<"\nv2 und v3 sind nicht identisch."<<endl;
if(v1==v3)
cout<<"\nv1 und v3 sind identisch."<<endl;
else
cout<<"\nv1 und v3 sind nicht identisch."<<endl;
return 0;
}
//Aufgabe 5_2.cpp
#include <iostream>
#include<string>
108 INM12
© HfB, 02.12.20, Berk, Eric (904709)
int main(){
Feiertag T1(24,12,2012,"Heiligabend");
Feiertag T2(24,12,2012,"Christmas Eve");
T1.drucke();
T2.drucke();
if(T1==T2)cout<<"\nGleichheit\n";
return 0;
}
INM12 109
© HfB, 02.12.20, Berk, Eric (904709)
B. Literaturverzeichnis
Ernst, H. u. a. (2015). Grundkurs Informatik. Grundlagen und Konzepte für die erfolgrei-
che IT-Praxis. Eine umfassende, praxisorientierte Einführung.
5. Aufl., Wiesbaden: Springer Vieweg.
Heiderich, N.; Meyer, W. (2016). Technische Probleme lösen mit C/C++. Von der Analyse
bis zur Dokumentation. 3. Aufl., München: Hanser.
Herold, H.; Lurz, B.; Wohlrab, J. (2012). Grundlagen der Informatik.
2. Aufl., Halbergmoos: Pearson.
Horn, C.; Kerner, I. O.; Forbrig, P. (Hrsg.) (2003). Lehr- und Übungsbuch Informatik.
Grundlagen und Überblick.
3. Aufl., München, Wien: Fachbuchverlag Leipzig, Hanser.
Nassi, I.; Shneiderman, B. (1973). Flowchart techniques for structured programming.
ACM SIGPLAN Notices.
Schneider, U. (Hrsg.) (2012). Taschenbuch Informatik.
7. Aufl., München: Fachbuchverlag Leipzig, Hanser.
Internetlinks
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/
110 INM12
© HfB, 02.12.20, Berk, Eric (904709)
C. Abbildungsverzeichnis
INM12
INM12 111
© HfB, 02.12.20, Berk, Eric (904709)
D. Quellcodeverzeichnis
INM12
112 INM12
© HfB, 02.12.20, Berk, Eric (904709)
INM12 113
© HfB, 02.12.20, Berk, Eric (904709)
114 INM12
© HfB, 02.12.20, Berk, Eric (904709)
E. Einsendeaufgabe Typ A
Grundlagen der Algorithmierung und Programmierung Teil 2 Einsendeaufgabencode:
INM12-XX1-K03
1. Ermitteln Sie im Trockentest die Werte der Variablen i, j, k und b nach Ablauf
des Programms. Geben Sie außerdem in einer Tabelle die Veränderung dieser Vari-
ablenwerte während der Programmabarbeitung an.
/*Einsendeaufgabe INM02_1.c*/
int main(){
int i,j,k=0,n=12;
float b=2.0f;
for(i=3;i<n;i++) {
if(i%2){
j=i*i;
}
else{
b+=5.1f;
continue;
}
k=i+j;
if(k>81) break;
}
return 0;
}
20 Pkt.
2. Gegeben ist ein Feld f aus n (7 n 10) selbst vorzugebenden Gleitkommazahlen
vom Datentyp double.
Definieren Sie eine Funktion berechne, die dieses Feld übergeben bekommt und
die Anzahl derjenigen Feldelemente bestimmt, deren Werte größer als das arith-
metische Mittel der vorgegebenen Feldelemente sind.
INM12 115
© HfB, 02.12.20, Berk, Eric (904709)
E Einsendeaufgabe Typ A
Benutzen Sie als Prototyp int berechne (double *f, int n) bzw. int
berechne (double f[], int n).
Nutzen Sie diese Funktion zur Ausgabe der Länge der Zeichenkette
"TESTZEICHENKETTE"!
25 Pkt.
4. Ein Kreis werde bestimmt durch die Koordinaten des Mittelpunkts xm und ym
sowie durch den Radius r, jeweils vom Datentyp float.
Erzeugen Sie eine Klasse Kreis mit folgenden Forderungen:
Definieren Sie für diese Klasse einen Konstruktor, der es ermöglicht, einerseits
durch die Angabe von drei Werten Instanzen der Klasse Kreis zu erzeugen und
andererseits durch die Angabe von einem Wert Kreise mit einem zu bestimmenden
Radius am festen Kreismittelpunkt xm=0 und ym=0 bereitzustellen.
Als setter- und getter-Methoden sind setze_Mittelpunkt, setze_Radius,
erhalte_xm, erhalte_ym sowie erhalte_Radius bereitzustellen.
int main(){
Kreis k1 (3.1f);
cout<<"Kreis k1:"<<endl;
k1.ausgabe();
cout<<"Durchmesser: "<<k1.durchmesser()<<endl;
cout<<"Flaeche: "<<k1.flaeche()<<endl;
cout<<"Umfang: "<<k1.umfang()<<endl;
Kreis k2(5.5 f., 1.2 f., 4.1f);
cout<<"\nKreis k2:"<<endl;
116 INM12
© HfB, 02.12.20, Berk, Eric (904709)
Einsendeaufgabe Typ A E
k2.ausgabe();
k2.setze_Mittelpunkt(0.5 f.,0.5f);
k2.ausgabe();
cout<<"Flaeche: "<<k2.flaeche()<<endl;
cout<<"Umfang: "<<k2.umfang()<<endl;
return 0;
}
30 Pkt.
Gesamt: 100 Pkt.
INM12 117
© HfB, 02.12.20, Berk, Eric (904709)
$
Studienheft
SEI11
$
SEI11
Werden Personenbezeichnungen aus Gründen der besseren Lesbarkeit nur in der männlichen oder
weiblichen Form verwendet, so schließt dies das jeweils andere Geschlecht mit ein.
Falls wir in unseren Studienheften auf Seiten im Internet verweisen, haben wir diese nach sorgfältigen
Erwägungen ausgewählt. Auf die zukünftige Gestaltung und den Inhalt der Seiten haben wir jedoch
keinen Einfluss. Wir distanzieren uns daher ausdrücklich von diesen Seiten, soweit darin rechtswid-
rige, insbesondere jugendgefährdende oder verfassungsfeindliche Inhalte zutage treten sollten.
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
0118K07
Vorwort ....................................................................................................................... 1
1 Einleitung .................................................................................................................. 3
1.1 Lebenszyklus einer Softwareentwicklung ................................................ 3
1.2 Methodische Ansätze und grundlegende Definitionen ........................... 5
Zusammenfassung .................................................................................................... 7
Aufgaben zur Selbstüberprüfung ............................................................................ 7
2 Phasenmodelle ......................................................................................................... 8
2.1 Klassisches Wasserfallmodell ..................................................................... 9
2.2 Spiralmodell ................................................................................................. 19
2.3 V-Modell ...................................................................................................... 21
2.4 Diamantmodell ............................................................................................ 24
2.5 Neuere Methoden ........................................................................................ 27
Zusammenfassung .................................................................................................... 33
Aufgaben zur Selbstüberprüfung ............................................................................ 33
3 Softwareergonomie ................................................................................................. 34
3.1 Definition und Rechtsgrundlagen ............................................................. 34
3.2 Softwareergonomische Dialoggestaltung ................................................. 37
3.3 Praktische Anwendungen .......................................................................... 41
Zusammenfassung .................................................................................................... 45
Aufgabe zur Selbstüberprüfung ............................................................................... 45
SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Inhaltsverzeichnis
Anhang
A. Lösungen der Aufgaben zur Selbstüberprüfung ....................................... 81
B. Literaturverzeichnis ..................................................................................... 84
C. Abbildungsverzeichnis ................................................................................ 85
D. Sachwortverzeichnis .................................................................................... 87
E. Einsendeaufgabe .......................................................................................... 89
SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Vorwort
SEI11Phasenmodelle und Planung von Softwareprojekten0118K07
Spätestens seit der Softwarekrise der 60er-Jahre ist der Ruf nach besser planbarem, sys-
tematischerem Vorgehen laut geworden. Nach und nach hat sich daraus das heutige
Software Engineering entwickelt. Weitgehend standardisiert, liefert es für alle an der
Entwicklung Beteiligten methodische Ansätze zur effektiven und ökonomischen Soft-
wareerstellung. Obwohl Software in der Regel nur recht kurzlebig ist, haben sich erst in
jüngerer Zeit die Methoden zu ihrer Erzeugung stabilisiert. Noch vor wenigen Jahren
standen viele verschiedene Ansätze in Konkurrenz, doch mit der Einführung der Unified
Modeling Language (UML) hat sich eine Methode etabliert, die alle anderen Konkurren-
ten weit hinter sich gelassen hat. Es besteht daher Aussicht, dass die beschriebenen Me-
thoden und Verfahren für die nächsten Jahre aktuell bleiben, da ihre Akzeptanz und Ver-
breitung sehr groß ist.
Software Engineering ist also eine selbstverständliche Methode zur effizienten Entwick-
lung von Programmen. Im Prinzip kann man zwei Arten Software unterscheiden: An-
wendungsprogramme und Steuerprogramme. Überlappungen sind möglich. Anwen-
dungsprogramme sind Programme, die von Usern benutzt werden können, die keine
tieferen DV-Kenntnisse besitzen. Sie müssen entsprechend benutzerfreundlich sein und
leicht bedienbar. Steuerprogramme dagegen bedürfen in der Regel keines Benutzers in
diesem Sinne, sondern sie laufen „von allein“ und steuern bestimmte (meist physikali-
sche) Systeme, z. B. einen Fahrstuhl.
Relativ unabhängig von dieser Unterscheidung sind die methodischen Ansätze bei der
Softwareentwicklung. Hier werden immer gewisse Entwicklungsphasen durchlaufen, in
denen systematisch Teilschritte realisiert werden, bis schließlich das fertige Produkt vor-
handen ist. Das vorliegende Heft deckt den ersten Teil dieser Entwicklungsphasen ab,
nämlich im Wesentlichen die Planung des Softwareprojekts. Zu diesem Zweck wird
nach einer Einleitung in die Problematik der Softwareentwicklung (Kapitel 1) einiges
über Phasenmodelle gesagt, es werden die wichtigsten dieser Modelle vorgestellt
(Kapitel 2). Kapitel 3 widmet sich den Erfordernissen der Softwareergonomie, und in
Kapitel 4 wird schließlich die Planungsphase eines Softwareprojekts genau durchleuch-
tet und es werden einige methodische Ansätze aus diesem Bereich beschrieben.
Der Planung kommt überhaupt eine grundlegende Bedeutung zu. Viele Softwareprojek-
te scheitern an unzureichender Planung. Es kann gar nicht oft genug darauf hingewiesen
werden, dass eine gute Planung das A und O des erfolgreichen Gelingens einer Syste-
mentwicklung ist. So wie ein Architekt zuerst den „Blueprint“ eines Bauvorhabens er-
stellt und Bauarbeiter nicht einfach drauflosmauern, so muss ein Softwareentwickler ei-
nen „Bauplan“ für das Anwendungsprogramm erstellen. Fehler in den Bauplänen
können sowohl für Hausbauer als auch für Softwareentwickler je nach Schwere glei-
chermaßen katastrophal sein. Deshalb kommt dem Fach Software Engineering eine so
wichtige Bedeutung zu.
Viel Erfolg beim Durcharbeiten dieses Heftes!
SEI11 1
© HfB, 02.12.20, Berk, Eric (904709)
2 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
1 Einleitung
Dieses Kapitel beschäftigt sich mit der Geschichte des Software Engineering so-
wie dessen Grundprinzipien. Sie sollen nach Durcharbeiten dieses Kapitels in der
Lage sein,
• die Probleme der Softwarekrise zu nennen,
• den Lebenszyklus eines Softwareentwicklungsprojekts zu beschreiben,
• methodische Ansätze zur Lösung der Probleme bei der Softwareentwicklung
zu nennen.
Es wird außerdem eine Definition von Software Engineering gegeben sowie
dessen Abdeckungsbereich untersucht.
SEI11 3
© HfB, 02.12.20, Berk, Eric (904709)
1 Einleitung
hafter Planung lag. Planungsfehler wurden oft erst sehr spät entdeckt und waren nur mit
erheblichem Aufwand zu korrigieren. Außerdem wiesen die Pflichtenhefte häufig In-
konsistenzen (Widersprüche) auf, die zunächst nicht bemerkt wurden. Diese Situation
bezeichnet man heute als die Softwarekrise der 60er-Jahre.
Es wurden viele Anstrengungen unternommen, um aus dieser Krise herauszukommen.
Es wurde z.B. zunächst versucht, die Planungsphase zu standardisieren, wobei ingeni-
eurwissenschaftliche Methoden als Vorbild dienen sollten. Die Grundidee war, besagte
Planungsphase systematisch zu erarbeiten und methodische Ansätze dafür zu entwi-
ckeln. Man erhoffte sich dadurch weniger Planungsfehler und eine Codierung, die Pro-
gramme lieferte, die die geforderten Spezifikationen so gut wie möglich erfüllten.
Entwicklungs-
aufwand
ideal
real
Zeit
Abb. 1.1 stellt den idealen Lebenszyklus einer Softwareentwicklung dem seinerzeit rea-
len gegenüber. Im Idealfall steckt man den meisten Aufwand in die Planungsphase, so-
dass die sich anschließende Codierungsphase ohne großen Aufwand vonstattengeht und
auch keine wesentlichen Korrekturen erforderlich sind. Im Realfall dagegen war die Pla-
nungsphase nicht sonderlich intensiv durchdacht und die Codierung war demzufolge
fehlerhaft und korrekturanfällig. Die wohlbekannten Gesetze von Edward A. Murphy
finden leider auch hier ihre Anwendung.
Murphys Gesetze
• Alles dauert länger, als man denkt.
• Alles ist teurer, als man denkt.
• Alles ist komplizierter, als man denkt.
• Wenn ein Fehler passieren kann, dann passiert er auch.
(Murphy war Optimist.)
4 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Einleitung 1
Diese etwas scherzhafte Formulierung von Murphys Gesetzen weist auf die Pro-bleme
hin, die mit der Entwicklung von Software verbunden sein können.
Ergebnis Ergebnis
System mit externe
spontane System-
geplanten
Aktivität umgebung
Reaktionen
Reaktionen Reaktionen
Ein System mit geplanten Reaktionen fängt also alle Reaktionen seiner Umgebung in
kontrollierter Weise ab. Stellt ein System z. B. ein Anwendungsprogramm auf einem
Rechner dar, also einen Ausschnitt aus der Realität, der DV-technisch mit einem entspre-
chenden Programm abgebildet wird, dann ist insbesondere die I/O-Schnittstelle eine
Quelle möglicher Probleme. Das kann z. B. eine unsinnige Tastenkombination auf der
Tastatur des Computers sein, während das Anwendungsprogramm läuft. Auf keinen
Fall darf es dann zum Absturz des Programms kommen, sondern es muss eine kontrol-
lierte Reaktion des Systems erfolgen.
Dies zu erreichen klingt zunächst einfacher, als es ist, denn meistens ist es schwierig, alle
Eventualitäten „vorherzudenken“. Deswegen ist z. B. auch der Programmieraufwand für
Plausibilitätsüberprüfungen einer Maskeneingabe häufig der aufwendigste Teil der je-
weiligen Maskenprogrammierung. Natürlich sind geplante Reaktionen auch in Rechen-
algorithmen zu berücksichtigen, beispielsweise muss eine mögliche Eingabe, die zur Di-
vision durch die Zahl Null führt, abgefangen werden.
Benutzen wir also zukünftig den Begriff Software, so wird nachfolgend immer davon
ausgegangen, dass es sich dabei um Systeme mit geplanten Reaktionen handelt.
SEI11 5
© HfB, 02.12.20, Berk, Eric (904709)
1 Einleitung
Dies ist allerdings noch lange nicht ausreichend, um effiziente Software zu entwickeln,
denn es sind weitere Probleme damit verbunden, die systematisch gelöst werden müs-
sen. Einige dieser Probleme sind:
• Komplexe Anforderungen sind oft schwer zu verstehen.
• Softwareentwicklung erfordert absolute Präzision, ein hohes Maß an Kreativität, Er-
fahrung im Entwickeln komplexer Programme und echtes Teamwork.
• Unter Stressbedingungen kann die Fehlerrate ansteigen.
• Es ist eine hohe Qualifikation der Mitarbeiter erforderlich.
An einen Entwickler werden zudem noch gewisse spezifische Anforderungen gestellt,
wie z. B. die Fähigkeit, Wichtiges von Unwichtigem zu trennen, oder die richtige Ein-
schätzung der Komplexität einer Anforderung.
Um komplexe und aufwendige Anforderungen einer systematischen Lösung zuführen
zu können, sind drei wichtige Prinzipien einzuhalten:
1. die Modellierung der Realität durch Abstraktion,
2. die Reduktion der Komplexität durch Strukturierung und
3. Fokussierung auf die Lösung von Teilproblemen.
Es gibt computergestützte Hilfsmittel zur Durchführung dieser Aufgaben, die man all-
gemein CASE-Tools nennt (CASE = Computer Aided Software Engineering). Es wird
dringend empfohlen, solche Tools wann immer möglich einzusetzen. Diese Werkzeuge
sind in der Lage, schon bei der Planung und dem Entwurf der Anwendung Inkonsisten-
zen zu entdecken. Dadurch reduziert sich die Fehlerrate beträchtlich. Außerdem können
viele dieser Tools das Planungsergebnis direkt in ein relationales oder objektorientiertes
Datenbankschema umsetzen und erzeugen so z. B. physikalische Tabellenstrukturen.
Nachfolgend soll nun eine allgemeine Definition des Begriffs Software Engineering ge-
geben werden.
Software Engineering
Unter Software Engineering versteht man die Wissenschaft, effiziente Software auf der
Basis ingenieurmäßiger Methoden und ökonomischer Gesichtspunkte zu entwickeln. Sie
umfasst die strukturierte Analyse, den Entwurf und die Realisierung sowie das Testen
und Warten eines Programms mittels methodischer Ansätze.
Bis zu den 80er-Jahren des 20. Jahrhunderts unterteilte man die Entwicklung eines Soft-
waresystems zunächst nur in die zwei Phasen Systemanalyse und Programmierung. Die
Systemanalyse umfasste damals die Problembeschreibung, die Beschreibung des Ist-
und Sollzustandes, die Input-/Output-Beschreibung des gewünschten Systems, einen
Projektplan, ein semantisches Datenmodell, ein logisches Datenmodell, Datenflussdia-
gramme/Programmstruktogramme und ein Lösungskonzept. Die sich daran anschlie-
ßende Programmierung beschäftigte sich mit der eigentlichen Codeerzeugung, ggf. ei-
nem „Tuning“ des Codes sowie schließlich dem Test und der Wartung der Software.
6 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Einleitung 1
Diese beiden Phasen sind heute allerdings nur Teile detaillierterer Phasenmodelle und
umfassen demzufolge auch nicht mehr alle oben genannten Komponenten, sondern die-
se sind ausgelagert und in andere, eigene Entwicklungsphasen integriert.
Gelegentlich wird eine Unterteilung des Software Engineering in zwei Komponenten
vorgeschlagen:
1. Konstruktionssystematik
Darunter versteht man die Ansammlung der benutzten Methoden, Werkzeuge und
Ergebnisse.
2. Produktionssystematik
Diese beschreibt die Entwicklungsschritte, deren Reihenfolge, gesetzte Termine so-
wie die anfallenden Kosten.
Es sei darauf hingewiesen, dass diese zwei Komponenten nichts mit den Phasen des Soft-
ware Engineering zu tun haben! Die Phasen fließen eher in die zweite Komponente ein,
während die erste einfach die Betriebsmittel zu ihrer Durchführung bereitstellt.
Diese Zweiteilung ist allerdings nicht unbedingt erforderlich, da die angewendeten Be-
triebsmittel entweder gewissen Standards entsprechen oder, falls nicht, in den jeweili-
gen Phasen beschrieben werden.
Die Lösung der Probleme der Softwarekrise des letzten Jahrhunderts erhoffte man sich
also durch Phasenkonzepte. Der Entwicklungszyklus einer Anwendung wird in einzelne
Phasen unterteilt und jede Phase enthält genau definierte Eingaben, Ausgaben und Tä-
tigkeiten. Man unterscheidet dabei zwischen linearen und nichtlinearen Phasenmodel-
len. Bei linearen Phasenmodellen muss eine Phase vollständig abgearbeitet sein, bevor
die nächste Phase begonnen werden kann. Bei nichtlinearen Phasenmodellen ist das
nicht so; hier können einzelne Phasen auch mehrmals durchlaufen werden, ohne immer
vollständig abgeschlossen zu sein. Solche Phasenmodelle sind Inhalt des nächsten Kapi-
tels.
Zusammenfassung
1.1 Zeigen Sie auf, wo das systematische Vorgehen des Software Engineering seine
Wurzeln hat und welche Parallelen es auf anderen Gebieten, z. B. den Ingenieur-
wissenschaften, hat.
1.2 Es seien zunächst die beiden Unterteilungen des Software Engineering „Planung“
und „Realisierung“ einer Softwareentwicklung betrachtet. Kommentieren Sie da-
mit Abb. 1.1, indem Sie die unterschiedlichen Kurven („ideal“ und „real“) quanti-
tativ den beiden Unterteilungen zuordnen.
SEI11 7
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
In diesem Kapitel werden einige der gängigen Phasenmodelle gezeigt. Es werden
die Vor- und Nachteile dieser Modelle sowie ihr Einsatzbereich erläutert. In der
Praxis sind viele Modelle im Einsatz, sodass wir uns hier nur auf die wichtigsten
beschränken.
Nach Durcharbeiten dieses Kapitels sollen Sie insbesondere folgende Kenntnisse
und Fertigkeiten besitzen:
• den Sinn und Zweck von Phasenmodellen aufzeigen
• die Phasen des klassischen Wasserfallmodells nennen und inhaltlich be-
schreiben
• das Spiralmodell nach Boehm beschreiben und dem Begriff des Prototypings
zuordnen
• das V-Modell beschreiben
• das Diamantmodell erläutern
• agile Methoden nennen
8 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Initialisierung
DV-Konzept
DV-Entwurf
Rückkopplungen Implementierung
(unerwünscht)
Test
Installation
Wartung
Das Wasserfallmodell zählt zu den linearen Phasenmodellen. Das heißt, eine Phase kann
erst begonnen werden, wenn die davorliegende vollständig beendet ist. Wir schauen uns
jetzt die einzelnen Phasen genauer an.
Phase 1: Initialisierung
Die Initialisierungsphase startet das geplante Entwicklungsprojekt. In der Regel wird da-
mit begonnen, dass ein Unternehmen das geplante Projekt ausschreibt oder sich direkt
an ein Softwareentwicklungsunternehmen wendet mit der Bitte um eine Angebotser-
stellung. Zur Erstellung eines Angebotes ist es natürlich erforderlich, dass eine erste
Analyse des Problems erfolgen muss, aus dem die notwendige Information für das An-
gebot ableitbar ist. Ein Problem des angebotserstellenden Unternehmens ist dabei, dass
einerseits ein detailliertes Angebot erst erfolgen kann, wenn hinreichende Informatio-
nen über das Projekt vorliegen, und andererseits eine solche erste Problemanalyse be-
SEI11 9
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
reits recht arbeitsaufwendig werden kann, je nachdem, welchen Umfang das geplante
Projekt haben soll. Lehnt der Kunde das Angebot ab, ist die für die erste Analyse inves-
tierte Zeit verloren. Deshalb wird ein Entwickler zunächst eine recht grobe Analyse
beim Kunden vornehmen, die aber trotzdem eine erste Kostenabschätzung sowie eine
erste zeitliche Abschätzung des Projektverlaufs möglich macht.
Eine sich mittlerweile immer mehr verbreitende Variante ist, dass sich ein Softwareent-
wicklungsunternehmen bereits die Angebotserstellung bezahlen lässt. Dies ist insbeson-
dere im Bereich Multimediaprojekte der Fall, z. B. bei der Entwicklung von E-Com-
merce-Anwendungen. Dies liegt u. a. daran, dass in diesem Bereich ein strukturiertes,
methodenbasiertes Vorgehen in Ermangelung geeigneter Modelle kaum zu finden ist.
Deshalb wird schon für eine Angebotserstellung oft eine umfangreiche Detailanalyse er-
forderlich, und dieser Aufwand muss dann auch honoriert werden. Der Kunde hat aber
auch einen Vorteil von dieser Vorgehensweise, denn er hat dann eine ausführliche Ana-
lyse des Entwicklers vorliegen, die er – selbst wenn er das Angebot ablehnt – weiterver-
werten kann (diese kann er z. B. einem anderen Systementwickler zur Verfügung stel-
len).
Die Initialisierungsphase enthält in jedem Fall mindestens folgende Punkte:
• Problembeschreibung, in der Zweck und Rechtfertigung für das geplante
Unterfangen genannt wird
• Projektziele (gewünschtes Ergebnis des Projekts)
• grobe Projektbeschreibung: welche bereits installierte Hard- und Software ist vor-
handen, welche zusätzliche Hard- und Software muss angeschafft werden, sind
Netzwerkkomponenten erforderlich etc.
• grober Projektplan, der eine personale und zeitliche Aufwandsplanung und erste
grobe Projektorganisation enthält
• Kostenabschätzung
• Angebot an den Kunden
Den Kunden interessiert dabei natürlich in erster Linie, was die Durchführung des Pro-
jektes kostet und wie lange die Entwicklungszeiten sind. Diese dürfen also auf gar kei-
nen Fall fehlen.
Eine zuverlässige Aufwands- und Kostenabschätzung birgt jedoch die größte Problema-
tik. Kunden bevorzugen in der Regel Festpreise, aber gerade die setzen eine realistische
Kostenabschätzung voraus und wie beschrieben würde das eine intensive Problemana-
lyse erfordern. Erfahrene Entwickler haben hier deutliche Vorteile, da sie oft ein recht
sicheres Gefühl für Art und Umfang anstehender Entwicklungsaufgaben haben.
Falls Festpreise gewünscht sind, dann ist es in jedem Fall ratsam, einen geeigneten Puffer
für nicht vorhersehbare Fälle (vgl. Murphys Gesetze aus Kapitel 1, Seite 4) einzuplanen.
Manche Entwickler veranschlagen z. B. einen Faktor 2 für die Entwicklungskosten im
Angebot an den Kunden gegenüber dem zunächst tatsächlich abgeschätzten Aufwand,
vor allem dann, wenn viele Unsicherheiten hinsichtlich der Problemanalyse bestehen.
10 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Wenn keine Festpreise erforderlich oder wenigstens nicht verbindlich sind, so kann ver-
einbart werden, dass eine genaue Aufwandsschätzung erst nach Abschluss der nächsten
Phase vorgelegt werden muss, also wenn das DV-Konzept vorliegt. In diesem Fall möch-
te der Kunde jedoch häufig wenigstens einen Kostenvoranschlag für die Erstellung des
DV-Konzepts haben, der dann im Angebot angegeben wird.
Eine andere Möglichkeit, die Unsicherheiten bei Festpreisen wenigstens zu mildern, be-
steht darin, dass ein Festpreis mit einer „Unsicherheitsspanne“ vereinbart wird. So kann
beispielsweise ein Festpreis von 25.000 € verhandelt werden, wobei ein Spielraum von
± 15 % möglich ist. Damit besteht ein Spielraum von 3.750 €, der ggf. noch auf den Fest-
preis aufgestockt werden kann. Natürlich wünscht der Kunde in der Regel dann auch
eine genaue Begründung für solche Zusatzkosten.
Die zeitliche Dauer der Initialisierungsphase sollte so gewählt werden, dass für den Soft-
wareentwickler kein zu hohes Risiko für den Fall entsteht, dass der Kunde das Angebot
ablehnt. Da diese Phase der Angebotserstellung dient und normalerweise nicht bezahlt
wird, sollte der Entwickler hier seine Zeit angemessen investieren. So sollten selbst bei
Projekten mit einer finanziellen Größenordnung von beispielsweise 50.000 € höchstens
14 Tage für die Initialisierungsphase investiert werden; ein Dilemma kann dabei sein,
dass man einerseits einen möglichst niedrigen Festpreis ansetzen möchte (um unterhalb
eventueller Konkurrenzangebote zu liegen) und andererseits gerade für eine knappe Kal-
kulation eine ziemlich ausführliche Problemanalyse benötigt, deren Erstellung wieder-
um zeitaufwendig ist. Hier muss der Entwickler ein ausgewogenes Verhältnis finden. So
etwas ist aber oft nur durch einige Erfahrung zu gewinnen.
Ein Ergebnis der Initialisierungsphase ist die Erstellung eines Angebots an den Kunden.
Nachfolgend ist ein Beispiel für ein solches Angebot zu sehen.
SEI11 11
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Supersoft AG 03.11.2014
Koboldweg 8
60321 Frankfurt am Main
12 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
formation aus dem Kunden herauszuholen. Meistens kennt der Systemanalytiker das
Problemfeld des Kunden kaum, während der Kunde normalerweise nicht weiß, welche
Information für die genaue Systemanalyse wichtig ist. DV-Fachmann und Kunde müs-
sen daher einen gemeinsamen Ansatzpunkt finden. Grob unterteilt besteht ein Fachkon-
zept aus zwei Teilen, nämlich der Anforderungsanalyse, in der der Ist- und der Sollzu-
stand von Hard- und Software erarbeitet werden, und der Systemanalyse, in der eine
erste semantische Datenmodellierung vorgenommen wird. Letztere beschreibt, was
die Anwendung leisten soll, wie sie prinzipiell aufgebaut sein muss und wie das erreicht
werden kann. Das Ergebnis ist dann das Papier, das Pflichtenheft (je nach Sicht auch
manchmal Lastenheft) oder allgemein einfach nur Spezifikation genannt wird.
Im DV-Konzept wird damit also im Prinzip der Weg vom Istzustand zum Sollzustand in
einer für Kunde und Analytiker gleichermaßen verständlichen Sprache beschrieben. Da-
her ist das Pflichtenheft überwiegend verbal abgefasst und enthält wenig Abstraktionen.
Dies birgt jedoch die Gefahr, dass hier Inkonsistenzen entstehen können. Das heißt, es
könnte z. B. auf Seite 10 des Pflichtenhefts etwas stehen, was einer Anforderung auf Sei-
te 25 direkt widerspricht. Solche Widersprüche werden u. U. erst sehr spät bemerkt, im
ungünstigsten Fall erst beim Testen der Software. Dies führt dann zu den unerwünsch-
ten Rückkopplungen im Wasserfallmodell, denn es muss im Pflichtenheft eine Korrektur
gemacht werden, die alle nachfolgenden Phasen betreffen kann. So etwas ist dann meis-
tens auch relativ zeit- und kostenaufwendig.
Das Pflichtenheft sollte daher sehr genau und umsichtig entworfen werden. Manchmal
kann es hilfreich sein, hier sogar schon Elemente des DV-Entwurfs (z. B. ER- oder UML-
Diagramme) zu verwenden, sofern der Kunde in der Lage ist, diese zu verstehen.
Eine verbale Beschreibung von Daten und deren Zusammenhängen nennt man auch se-
mantisches Datenmodell.
Im Pflichtenheft sollten enthalten sein:
• (semantische) Modellierung der projektrelevanten Teilausschnitte der Realität
• Beschreibung des Istzustands, ggf. unter Zuhilfenahme von Organigrammen, Funk-
tionsabläufen und/oder bereits vorhandenen Datenmodellen
• Beschreibung des Sollzustands, ggf. unter Zuhilfenahme von Organigrammen,
Funktionsabläufen und/oder bereits vorhandenen Datenmodellen
• Beschreibung der Ein- und Ausgaben des zu entwickelnden Programms
• Beschreibung von Datenstrukturen (Länge und Art der Daten, z. B. Gleit- oder Fest-
kommazahlen, wie lang können maximal die jeweils abzuspeichernden Zeichenket-
ten sein)
• Beschreibung des Datenaufkommens, d. h. der Datenmengen, Datenherkünfte etc.
• Beschreibung des Wegs von der Dateneingabe zur Datenausgabe (macht beispiels-
weise dies im Istzustand bereits jemand „von Hand“, so ist diese Person zu befragen
und der genaue Funktionsablauf zu beschreiben; eventuelle Berechnungsformeln
etc. sind anzugeben)
• Beschreibung der geplanten Datenmanipulationsmöglichkeiten
SEI11 13
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
14 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
… Zunächst werden die Felder mit den Inhalten aus der Tabelle A90UMW ge-
füllt, und zwar alle Datensätze, bei denen die Felder KU_ZE und/oder
KENZZGES gefüllt sind. Dazu die Folgedatensätze, bei denen die Felder TEL
noch gefüllt sind, aber nur bis zum nächsten gefüllten Feld KU_ZE und/oder
zum nächsten Telefoneintrag. Nun wird von der Tabelle BUERO_90 aus gear-
beitet. Alle Datensätze, die in den Feldern BURO_ART, GB_KURZ,
KFZ_KENN und/oder ANSPRECH ein Kurzzeichen enthalten, werden mit den
Feldern KU_ZE und KENZZGES auf Übereinstimmung geprüft, und zwar je-
des Feld, das gefüllt ist. Ist Übereinstimmung vorhanden, muss noch der Ort
überprüft werden. Dazu Feld ORT_ aus Tabelle BUERO_90 mit dem Ortsna-
men im Feld NAMSITZ ab der Zeile mit dem gefundenen Code im Feld KU_ZE
bis zum nächsten Eintrag im Feld KU_ZE und innerhalb derselben Blocknum-
mer prüfen. Wird auch hier Übereinstimmung gefunden, dann werden die
Felder aus BUERO_90 in die Tabelle ANSPRE übernommen, und zwar in die
Zeile, in der der Code steht. Wird zu einem Datensatz in der Tabelle
BUERO_90 keine Übereinstimmung gefunden, wird der Datensatz an das
Ende der Tabelle ANSPRE angehängt, und zwar für jedes ausgefüllte Codefeld
ein Datensatz, hinter jedem hinzugefügten Datensatz werden drei weitere
Zeilen mit dem Firmenkurzzeichen, einer Block- und Zeilennummer gefüllt
und der relevante Ansprechpartnercode wird in das Feld KU_ZE eingetragen.
Nach diesem Abgleich werden alle Datensätze, in denen die Felder KU_ZE
und/oder KENZZGES gefüllt sind, aber denen noch keine Adresse zugeordnet
ist, mit der Adresse der jeweiligen Gesellschaft gefüllt (Straße, PLZ und Ort,
Postfach, PLZ zum Postfach und Ort aus Tabelle gesell). Da es in der Gesell-
schaftstabelle zwei Datensätze geben kann, ist zu prüfen, ob unter NAMSITZ
innerhalb des Blocks ein Ort aufgeführt ist (Feld NAME=1). Ist kein Ort auf-
geführt, dann ist immer die Adresse aus dem Datensatz 1/2 zu nehmen. Ist
ein Ort aufgeführt und er stimmt mit der Adresse von Satz 2/2 überein, dann
ist diese Adresse zu nehmen, sonst wieder die Adresse von Satz 1/2. Nun ist
jedem Datensatz, der mit einer Adresse gefüllt ist, aber noch keinen Eintrag
im A90UMW-Teil der Tabelle hat (außer dem Gesellschaftskurzzeichen), über
die Beziehung zur vertansprech-Tabelle ein Ansprechpartner aus dieser Ta-
belle zuzuordnen. Ist nur ein Ansprechpartner vorhanden, wird dieser ge-
nommen.
Sind mehrere Ansprechpartner vorhanden, wird der Ansprechpartnercode
(Feld abt) in Tabelle vertansprech mit dem Code in KU_ZE verglichen. Bei
Übereinstimmung erfolgt die Übernahme der Inhalte der Felder funktion, an-
sprech, telefon (telefax, funktel, email jeweils in die nächsten Zeilen, die be-
reits angelegt wurden). Ist keine Übereinstimmung vorhanden, wird der Da-
tensatz mit der niedrigsten Zeilennummer genommen, in dem ein Name
eingetragen ist. Ganz zum Schluss müssen noch die Zeilen, die nur Gesell-
schaftskurzzeichen, Block- und Zeilennummern enthalten, gelöscht werden
…
SEI11 15
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Dieses semantische Datenmodell ist schon recht ausführlich und gibt bereits einige Vor-
schläge zur Implementierung. Das ist allerdings nicht immer so. Oft werden hier nur
Andeutungen gemacht, die dann erst im DV-Entwurf in dieser Detaillierungstiefe aus-
geführt werden. Doch die Faustregel, nämlich: „Je früher ins Detail gegangen wird, umso
besser“, sollte wann immer möglich angewendet werden.
Phase 3: DV-Entwurf
Die eigentliche Entwicklung des Anwendungssystems geschieht hier. Das Ergebnis wird
auch manchmal Feinkonzept genannt. Dabei spielt diese Phase die Rolle, die z. B. beim
Hausbau die Erstellung eines Bauplans, des „Blueprints“, spielt. So wie ein Architekt auf
dem Papier das zukünftige Haus plant (nach den Wünschen des Auftraggebers, bei uns
im Pflichtenheft niedergeschrieben), so plant der Entwickler in der Phase DV-Entwurf
die Einzelheiten der DV-Anwendung. Und ebenso wie beim Architekten ist das Ergebnis
auch in der Datenverarbeitung ein Bauplan im wahrsten Sinn des Wortes, der zum gro-
ßen Teil tatsächlich aus grafischen Elementen auf Papier, man könnte es ein Programm-
Blueprint nennen, besteht. Das Ergebnis dieser Phase ist also eine Programmspezifikati-
on, in der alle Einzelheiten beschrieben sind, die zur eigentlichen Programmierung des
Systems erforderlich sind. Dazu gehört übrigens auch eine detaillierte Planung von Test-
strategien zwecks Verifikation der entwickelten Programme. Diese Phase ist schwer-
punktmäßiger Gegenstand späterer Betrachtungen.
Phase 4: Implementierung
Wenn der Entwurf erfolgreich beendet ist, dann kann die Anwendung programmiert
werden. Diesen Vorgang nennt man Implementierung, da jetzt der Entwurf in codierter
Form auf den Computer gebracht wird. Je nach Anwendungsgebiet kann es sich dabei
um die berühmte „Knochenarbeit“ handeln, wenn z. B. ein Echtzeitsystem in C++ ge-
schrieben werden muss. Es kann aber auch sein, dass gar nicht viel im herkömmlichen
Sinne programmiert werden muss, sondern dass durch den geschickten Einsatz von
Werkzeugen vieles ohne Programmierarbeit erledigt werden kann. Gerade bei Daten-
bankanwendungen z. B. für betriebliche Informationssysteme sind sehr mächtige Tools
vorhanden. Im Idealfall kann der Programmcode sogar automatisch generiert werden.
Dazu ist es natürlich erforderlich, dass der DV-Entwurf hinreichend detailliert und for-
malisiert ist. Werkzeuge, die (fast) keine Programmierung mehr erforderlich machen,
sind ein hochaktuelles Forschungsgebiet des Software Engineering.
Die Vorgehensweise für die Implementierung ist im Allgemeinen „top-down“, d. h., es
werden zuerst die einzelnen Module geplant und diese dann jeweils verfeinert und im-
plementiert. Das Ergebnis der Phase Implementierung ist also ein ausführbares Pro-
gramm mit einer eventuell physikalisch implementierten Datenbasis. Auch diese Phase
wird erst später wieder genauer betrachtet.
16 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Phase 5: Test
Der Implementierung des Programms folgt ein ausführlicher Test. Dabei sind zwei Test-
verfahren zu unterscheiden:
1) Programmtest
Hierunter versteht man den Test der einzelnen Programmmodule auf logische Kon-
sistenz und Übereinstimmung mit dem DV-Design. Dieser Test wird i. d. R. vom Pro-
grammierer selbst durchgeführt. Normalerweise beginnen diese Tests nicht erst am
Ende der Implementierungsphase, sondern bereits während der Codierung, d. h.,
während an den entsprechenden Modulen programmiert wird, werden auch gleich
die dazugehörigen Tests gemacht. Selbstverständlich wird nach Abschluss der Im-
plementierung noch einmal ein größerer Gesamttest durchgeführt, um das Zusam-
menspiel der einzelnen Module zu überprüfen und die Konsistenz mit dem DV-De-
sign festzustellen. Für diese Art des Testens gibt es kaum formale Methoden, da die
verschiedenen Möglichkeiten des Programmierstils einfach zu vielfältig sind. Als
Faustregel kann man allenfalls sagen, dass die Module jeweils für sich so weit wie
möglich ausgetestet werden sollten. Ist dies erfolgt, sollten einige zusammengehöri-
ge Module gemeinsam, d. h. deren Zusammenspiel, getestet werden. Man testet also
Gruppen von Modulen, bis schließlich das ganze Programm ausgetestet ist. Handelt
es sich z. B. um wissenschaftliche Anwendungen, so sollte eine verifizierbare Metho-
de im Voraus festgelegt werden. Soll ein Programm z. B. die numerische Lösung ei-
nes Differenzialgleichungssystems ermöglichen und sind dafür numerische Stütz-
stellen für eine Interpolations- oder Approximationsfunktion vorgesehen, so kann
ein sehr effektiver Test dadurch erfolgen, dass man einige der Stützstellen innerhalb
ihres Konvergenzintervalls leicht verschiebt. Damit hat man numerisch gesehen völ-
lig andere Zahlen als Input, aber das Ergebnis der Lösung müsste davon unabhängig
sein. Auch hilft es häufig, Extremfälle zu betrachten, deren Ergebnis im Voraus be-
kannt ist (z. B. triviale Fälle, wo alle Eingaben null sind).
2) Benutzertest
Ist das Programm durch den Programmtest als logisch richtig und fehlerfrei verifi-
ziert, muss ein Test unter Produktionsbedingungen erfolgen. Dieser Test kann zwar
auch vom Programmierer durchgeführt werden, es ist aber besser, wenn hier ausge-
wählte Benutzer testen. Wichtig ist dabei, dass keinesfalls schon echte Produktions-
prozesse gefahren werden, sondern es soll ja nur getestet werden in einer produkti-
onsidentischen Umgebung, aber nicht in der Produktion selbst. Es kann nämlich
hier noch zu folgenreichen Fehlern im Programm kommen, und das darf natürlich
nicht den Produktionsprozess behindern. Auch müssen die Benutzer, die diese Tests
durchführen, wissen, dass es sich hier um einen Test handelt, der u. U. zu unerwar-
teten Ergebnissen führen kann. Die Planung der Art und Weise dieser Tests sowie
der beteiligten Personen sollte bereits im Fachkonzept festgelegt werden.
Phase 6: Installation
Nach Abschluss der Testphase erfolgt die Installation der Anwendung in der Produkti-
onsumgebung. Es sollte dafür ein Zeitpunkt gewählt werden, der unkritisch für die lau-
fende Produktion ist. Das heißt, dass gleichzeitig keine kritischen Anwendungen dabei
laufen dürfen. Auch muss eine Datensicherung des Zielrechners vorhanden sein, sodass
ein Recovery möglich ist.
SEI11 17
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Phase 7: Wartung
Normalerweise ist die Wartungsphase – jedenfalls bei hinreichend getesteten Program-
men – wenig arbeitsintensiv. Manchmal werden Schulungsmaßnahmen (Anwender-
schulungen etc.) vorgenommen, die aber gewöhnlich zeitlich begrenzt sind. Läuft das
Programm erst einmal richtig, so bleibt hier nicht mehr viel zu tun. Manchmal kann es
vorkommen, dass kleinere Fehler erst nach längerer Zeit entdeckt werden oder dass Pro-
grammerweiterungen gewünscht werden. Solche Fälle können als Teil eines Wartungs-
vertrages im Voraus abgedeckt werden. Erfolgt dies nicht, dann muss in diesen Fällen
ein neues Angebot erstellt und das Ganze wie ein neues Projekt behandelt werden.
Rückkopplungen im Wasserfallmodell
Angenommen, beim Benutzertest stellt sich heraus, dass eine Ausgabe nicht das ge-
wünschte Ergebnis liefert. Der Benutzer geht damit dann zum Programmierer. Wenn es
sich nun um keinen Programmierfehler handelt, so wird der Programmierer den „Pla-
ner“, d. h. denjenigen, der den DV-Entwurf erarbeitet hatte, ansprechen. Hat auch dieser
keinen Fehler bei der Planung gemacht, so wird er schließlich im Fachkonzept nach-
schauen und dort den Fehler lokalisieren können. Das Fachkonzept aber wurde zusam-
men mit dem Auftraggeber nach Erstellung „abgesegnet“, d. h. beide, Entwickler und
Auftraggeber, hatten das Fachkonzept als verbindlich abgezeichnet. Jetzt muss also das
Fachkonzept korrigiert werden. Daraufhin müssen die Phasen DV-Entwurf, Implemen-
tierung und Test erneut durchlaufen werden. Dieser Sachverhalt stellt die unerwünsch-
ten Rückkopplungen dar.
Wenn man Pech hat, kann dies alles sogar mehrmals geschehen. Es existiert die Mei-
nung, dass ein Fachkonzept mit mehr als 20 Seiten immer an irgendeiner Stelle wider-
sprüchlich ist. Aus diesem Grund ist es gerade bei umfangreicheren Projekten sinnvoll,
schon frühzeitig computergestützte Werkzeuge einzusetzen. Es gibt bereits hilfreiche
Tools für die Erstellung von Fachkonzepten, die gewisse Inkonsistenzen frühzeitig ent-
decken können. Solche Tools setzen allerdings voraus, dass die Spezifikation sehr genau
durchgeführt wird. Es ist klar, dass bedingt durch den linearen Verlauf der Phasen im
Wasserfallmodell Fehler erst sehr spät entdeckt werden. Zudem bekommt ein Anwen-
der erst zu einem sehr späten Zeitpunkt das erste Mal etwas von dem Anwendungspro-
gramm zu sehen, nämlich frühestens beim Benutzertest. Bis dahin ist aber schon der
Hauptanteil der Entwicklungszeit verbraucht. Bei größeren Entwicklungsprojekten
kann es durchaus sein, dass ein Anwender erst ein Jahr nach Auftragserteilung zum ers-
ten Mal etwas auf seinem Computer zu sehen bekommt. Das ideale Wasserfallmodell
(ohne Rückkopplungen) setzt also etwas voraus, was es eigentlich nicht gibt: ideale Auf-
traggeber, die zum Zeitpunkt der Auftragsvergabe haargenau wissen, was sie wollen
und was das Programm später können soll, und ideale DV-Entwickler, die völlig fehler-
frei arbeiten und ebenfalls alle möglichen Probleme vorausgedacht haben. So etwas gibt
18 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
es natürlich nicht. Deswegen hat sich nach und nach eine weitere Planungsmethode eta-
bliert, in der die Not des realen (rückgekoppelten) Wasserfallmodells (nämlich dessen re-
ale Nichtlinearität) zur Tugend gemacht wurde: das Spiralmodell. Diese Vorgehensweise
sei als Nächstes näher betrachtet.
2.2 Spiralmodell
Die unerwünschten Rückkopplungen im Wasserfallmodell und die mittlerweile weitver-
breiteten Werkzeuge zur relativ schnellen Gestaltung von Programmoberflächen haben
dazu geführt, dass sich neben dem klassischen Wasserfallmodell noch ein weiteres eta-
bliert hat: das Spiralmodell. Der Grundgedanke ist, dass nicht die einzelnen Phasen je-
weils beendet sein müssen, bevor die nächste Phase begonnen wird, sondern dass die
Phasen in kleinere Teilphasen zerlegt werden und diese zunächst begonnen werden. Es
werden also die großen Phasen immer nur teilweise bearbeitet und danach wird gleich
zur nächsten Phase übergegangen. Das Ergebnis ist ein mehrmaliges, stückweises
Durchlaufen aller Phasen, bis ein gewisser Reifegrad erreicht ist. Betrachtet man z. B.
nur die vier Hauptphasen Analyse, Entwurf, Implementierung und Test, so könnte eine
einfache Version des Spiralmodells wie in Abb. 2.2 skizziert aussehen.
Analyse Entwurf
Test Implementierung
Boehm hat diese Idee seiner verfeinerten Version des Spiralmodells zugrunde gelegt. Der
Vorteil einer solchen Vorgehensweise liegt auf der Hand: Durch die sukzessive Entwick-
lung des Systems bekommt der Anwender schon relativ früh Teile des zukünftigen Pro-
gramms zu sehen und es können so Fehler auch früher erkannt und damit rechtzeitig
korrigiert werden. Die ersten Programmteile, die durch die ersten, inneren Spiralläufe
im Quadranten der Implementierung repräsentiert werden, nennt man auch Prototy-
pen. Sie stellen eine Art Programmhülle dar, wobei hier hauptsächlich nur die Benutzer-
oberflächen realisiert sind ohne wirkliche Anwendungen oder Funktionen hinter den
Schaltflächen.
SEI11 19
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
kumulierte
Bestimmung von Zielen, Kosten Bewertung von Alternativen
Alternativen, Restriktionen Identifizierung und
Beseitigung von Risiken
Fortschritte Risikoanalyse
Risikoanalyse
Zustimmung
Risikoanalyse betriebs-
fähiger
Prototyp Prototyp
Risikoanalyse
P3
P2
P1
Start
Modul-
codierung
Entwicklungs- Anforderungs-
plan prüfung
Modultest
Integration und Entwicklungs-
Testplan prüfung
Integration
und Test
Verbesserungs- Abnahmetest
plan und Einführung
Planung der nächsten Phase Entwicklung/Prüfung der
nächsten Produktstufe
Eine alte Faustregel besagt, dass man für die endgültige Programmierung des Systems
alle Prototypen am besten wegwirft und den Zielzustand neu programmiert. Diese et-
was radikale Methode muss aber nicht immer angewendet werden, denn häufig liefert
das Prototyping recht brauchbare Programmmodule, die zumindest teilweise wieder-
verwendet werden können.
Das Spiralmodell birgt allerdings auch Nachteile. Insbesondere kann es dazu führen,
dass der Gesamtaufwand des Entwicklungsprojekts stark in die Höhe geht. Festpreise
sind bei so einer Vorgehensweise daher sehr gefährlich für den Entwickler. Leicht kann
es z. B. dazu kommen, dass der Anwender, wenn er die ersten Versionen der Prototypen
zu sehen bekommt, in Euphorie ausbricht und etliche Zusatzwünsche äußert, die so zu-
nächst im Budget gar nicht vorgesehen waren. Die Entwicklungsspirale kann so ins Un-
ermessliche wachsen, das Programm kommt nicht in der geplanten Zeit zum Abschluss.
In der Praxis erweist es sich daher häufig als sinnvoll, eine Kombination des klassischen
Wasserfallmodells mit dem Spiralmodell zu praktizieren. Die groben Planungen und das
Gerüst der Entwicklung sollten mit dem Wasserfallmodell gemacht werden, während
die Details dann nach dem Spiralmodell realisiert werden können.
20 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
2.3 V-Modell
Das V-Modell weist gewisse Ähnlichkeiten mit dem Wasserfallmodell auf. Es besteht
ebenfalls aus 7 Phasen, die linear angeordnet sind. Allerdings sind die Phasen anders ge-
gliedert und statt von oben nach unten sind sie v-förmig angeordnet
(vgl. Abb. 2.4).
Spezifikation Validierung
Systementwurf Integrationstest
Komponentenentwurf Komponententest
Implementierung
Der Entwicklungsverlauf beginnt links oben, geht dann nach unten und dann wieder
hoch und endet schließlich rechts oben. Dabei befinden sich auf dem absteigenden Ast
konstruktive Tätigkeiten, während auf dem aufsteigenden Ast Arbeitsschritte zur Qua-
litätssicherung vorhanden sind. Wie man sieht, wird zwischen dem Systementwurf und
dem Komponentenentwurf unterschieden. Diese Unterscheidung ist nicht ganz unprob-
lematisch, denn häufig sind die zu entwerfenden Komponenten bereits Teil des Systems.
Die Grenzen können hier also verwischen. Tendenziell kann man aber sagen, dass z. B.
UML-Diagramme eher in den Systementwurf und Funktionsablaufpläne eher in den
Komponentenentwurf gehören.
Es gibt mittlerweile eine Erweiterung des V-Modells, genannt V-XT. Gemäß der offizi-
ellen Dokumentation der Bundesrepublik Deutschland aus dem Jahr 20041 ist das V-Mo-
dell als Leitfaden zum Planen und Durchführen von Entwicklungsprojekten unter Be-
rücksichtigung des gesamten Systemlebenszyklus konzipiert. Die in einem Projekt zu
erstellenden Ergebnisse werden definiert und die konkreten Vorgehensweisen beschrie-
ben, mit denen diese Ergebnisse erarbeitet werden. Zusätzlich legt das V-Modell XT die
Verantwortlichkeiten jedes Projektbeteiligten fest. Es wird daher genau festgesetzt, wer
wann was in einem Projekt zu bewerkstelligen hat. Andere Richtlinien wie ISO-Stan-
dards sind zurzeit in Gebrauch, aber im Vergleich zum V-Modell XT weniger konkret,
da sie beispielsweise keine Produktvorlagen vorgeben.
Mit dem V-XT werden Projekte besser plan- und nachvollziehbar und erzielen zuverläs-
sige Ergebnisse von relativ hoher Qualität.
1. Vgl. https://www.cio.bund.de/Web/DE/Architekturen-und-Standards/V-Modell-XT/vmodell_xt_node.ht-
ml
SEI11 21
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Selbst kleine und mittelständische Unternehmen profitieren vom V-Modell. Es bietet ih-
nen die Möglichkeit, auf standardisierte und erprobte Vorgaben für Entwicklungs- und
Managementprozesse zurückzugreifen. So können auch kleinere Unternehmen mit
überschaubarem Aufwand ihre eigenen Vorgehensweisen systematisieren und dadurch
zuverlässig hochwertige Entwicklungsergebnisse erzielen. Das V-Modell dient somit als
Vertragsgrundlage, Arbeitsanleitung und Kommunikationsbasis.
Das V-Modell XT besteht vereinfacht betrachtet aus einem Kern und einem Mantel, der
auf den Kern zurückgreift. Im Kern sind die grundlegenden Prinzipien des Modells ent-
halten. Der Mantel umfasst beispielsweise die Vorgehensbausteine, Durchführungsstra-
tegien und Entscheidungspunkte.
Das V-Modell XT beschreibt drei Projekttypen:
• Systementwicklungsprojekt eines Auftraggebers: Es wird im Projektverlauf eine
Ausschreibung erstellt und dann ein Auftragnehmer anhand der Angebote ausge-
wählt. Der Auftragnehmer entwickelt und liefert ein System. Der Auftraggeber muss
das Ganze am Ende abnehmen.
• Systementwicklungsprojekt eines Auftragnehmers: Hier wird im Projektverlauf
ein Angebot erstellt und dann (im Fall eines Vertragsabschlusses) das angebotene
System entwickelt.
• Einführung und Pflege eines organisationsspezifischen Vorgehensmodells: Die-
ser Projekttyp liefert den Rahmen für ein organisationsweites Qualitätsmanagement
von Entwicklungsprojekten. Zuvor müssen ggf. die aktuellen Vorgehensweisen un-
tersucht werden.
Bei jeder Systementwicklung gibt es eigentlich zwei Projekte: eines auf der Auftragge-
berseite, das man als „Systementwicklungsprojekt eines Auftraggebers“ bezeichnen
könnte, und eines auf der Seite des Auftragnehmers mit dem Projekttyp „Systement-
wicklungsprojekt eines Auftragnehmers“. Diese beiden Projekte sind nicht voneinander
losgelöst, sondern über die Auftraggeber-Auftragnehmer-Schnittstelle miteinander ver-
knüpft.
Die Begriffe im V-Modell XT werden durch Konventionsabbildungen mit Begriffen in
(Quasi-)Standards, Normen und Vorschriften in Beziehung gesetzt. Das V-Modell bein-
haltet unter anderem Abbildungen auf die Standards CMMI und ISO 15288.
Der Einsatz des V-Modells XT in Projekten bietet folgende Vorteile:
• Minimierung der Projektrisiken
• Verbesserung und Gewährleistung der Qualität der Entwicklungsergebnisse
• transparente Beherrschung der Gesamtkosten über den gesamten Systemlebenszyk-
lus
• Verbesserung der Kommunikation für alle Beteiligten
22 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
SEI11 23
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
In Abb. 2.5 ist das V-Modell XT so dargestellt, dass im oberen Bildteil die jeweiligen
Phasen des „Auftraggeber-Projekts“ und im unteren Bildteil die des Auftragnehmer-Pro-
jekts“ sichtbar sind. Im mittleren Teil ist die Schnittstelle zwischen diesen beiden Projek-
ten zu sehen mit ihren Aktivitäten und Dokumenten.
Das V-Modell XT wird überwiegend (wie schon das V-Modell) im öffentlichen Dienst
eingesetzt.
2.4 Diamantmodell
Multimediaanwendungen stellen eine besondere Herausforderung für Entwicklungspla-
ner dar, denn hier sind teilweise künstlerische Elemente mit programmiertechnischen
verbunden. So findet die Integration von Sound (Sprache und Musik) und Bild (im Sinne
von Fotografien, Videoclips und Animationen) zunehmend statt. Die Verbreitung des In-
ternets trägt hier wesentlich bei. Eine Webseite, die nur aus reinem Text besteht, ist so
gut wie gar nicht zu finden. Die Vorteile solcher multimedialen (MME) Anwendungen
liegen auf der Hand: Der Mensch, der bekanntlich mehr Sinne besitzt als nur den Ge-
sichtssinn zum Lesen geschriebener Information, kann so pro Zeiteinheit gleichzeitig
mehr Information aufnehmen, wenn Bilder und Sprache mitgeliefert werden. Gerade im
pädagogischen Bereich (Lernprogramme etc.) ist dies eine große Hilfe.
Unabhängig von Ursachen und Zweck solcher multimedialer Anwendungen sind diese
jedenfalls stark verbreitet und die Tendenz ist rasant steigend. Die Verbreitung solcher
Applikationen geschieht schneller, als Ansätze und Konzepte für die Planung solcher
Systeme hinterherkommen. Dies führt zu ähnlicher Unsicherheit für die Entwickler wie
in den 60er-Jahren: Eine methodische Planung, Aufwandsschätzung und Durchführung
solcher Projekte ist oft nicht gegeben, was vor allem zu einer Kostenunsicherheit seitens
der potenziellen Auftraggeber solcher Multimediasysteme führt. Die Entwickler multi-
medialer Software sind somit an Entwicklungskonzepten interessiert, die in der Praxis
unkompliziert und effizient einsetzbar sind. Es sind Vorgehenskonzepte gewünscht, ähn-
lich wie z. B. beim Wasserfallmodell, die neben den Vorgehensabschnitten auch realisti-
sche Aufwands- und Ressourcenabschätzungen ermöglichen, und das möglichst ohne
das Studium dicker Bücher und das Verständnis darin beschriebener komplizierter Mo-
dellansätze.
Es ergibt sich also die Notwendigkeit, ein möglichst einfaches, in der Praxis einsetzbares
Phasenmodell zu haben, das die Planung des zeitlichen Aufwands, der technischen Res-
sourcen, des für die Entwicklung benötigten Personalaufwands und der Vorgehensweise
bei der Projektentwicklung und Durchführung liefert.
Für multimediale Systeme gibt es hier wenig Ansätze, jedenfalls kaum welche, die für
die industrielle Praxis schnell und leicht umsetzbar wären. Das liegt u. a. daran, dass
multimediale Systeme einfach viel mehr Komponenten besitzen als konventionelle be-
triebliche Informationssysteme. Neben einer Datenbank kommen eben noch die opti-
schen und akustischen Komponenten hinzu, deren Planung nicht so leicht formalisier-
bar ist.
Daher ist das Ziel hier eine Erweiterung bestehender Planungsmodelle um gerade die im
Multimediabereich hinzukommenden Komponenten. Es wird nachfolgend eine Refe-
renzliste erstellt, die dem Planer helfen soll, an alles zu denken und die Aufwände und
Ressourcen hierfür zu planen.
24 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Zeit
techn. Ressourcen
Personal
Personal-Zeit-Ebene
Nachfolgend werden Projektionen auf zwei Ebenen betrachtet. Es sei dabei ausdrücklich
darauf hingewiesen, dass die Relationen der Komponenten- und Phasengrößen nachfol-
gend nicht in der richtigen Proportion wiedergegeben sind. Die tatsächlichen Größen-
verhältnisse sind natürlich von Art und Umfang des Gesamtprojekts bzw. der einzelnen
Komponenten und Phasen abhängig und variieren daher von Projekt zu Projekt. Die
Darstellung ist also rein qualitativ, nicht quantitativ zu interpretieren.
SEI11 25
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Zeit
Test &
Handing Over
Composing Components
Screen Layouts
Databases
Animation
Graphics
Photos
Videos
Sound
… …
Init
Personal
Die Zeitachse repräsentiert die Projektphasen, die Personalachse die von den beteilig-
ten Entwicklern zu erstellenden Projektkomponenten. Dabei ist bezüglich der Perso-
nalachse die Breite des Diamanten im Allgemeinen ein Maß für die Menge an Perso-
naleinsatz (z. B. in Personentagen). Grundsätzlich können natürlich auch alle
Komponenten und Phasen von einer einzigen Person durchgeführt werden, was die Pro-
jektdauer entsprechend verlängert. Der Umstand, dass der Diamant „in die Breite“ geht,
soll andeuten, dass in einer breiteren Phase mehr Entwicklungsaufwand nötig ist, der
größtenteils unabhängig (und daher von mehreren Personen gleichzeitig) durchgeführt
werden kann.
Ressourcen-Zeit-Ebene
Für die Entwicklung der jeweiligen MME-Komponenten sind gewisse technische Res-
sourcen erforderlich, die nachfolgend näher beschrieben werden (vgl. Abb. 2.8). Der
Einsatz dieser Ressourcen sei so verstanden, dass als Ergebnis der Verwendung der Res-
sourcen-Komponenten immer eine Datei herauskommt, also ein Datenfile, das in dem
vom MME Composing Tool entsprechend verarbeitbaren Datenformat vorliegt.
26 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Zeit
Composing Tool
Database Developer
Screen Design Tool
Animation Tool
Sound Studio
Photo Studio
Video Studio
… Graphic Tool …
Design Tool
techn. Resourcen
Eine ausführlichere Beschreibung des Diamantmodells bietet auch die Möglichkeit einer
konkreten Aufwandsabschätzung2.
SEI11 27
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
28 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Die Disziplinen (auch als Workflows bezeichnet) orientieren sich an speziellen Rollen
innerhalb des Entwicklungsteams, die von bestimmten Personen oder Gruppen wahrge-
nommen werden. Diese Disziplinen sind im Einzelnen:
• Geschäftsprozessmodellierung
• Anforderungen
• Analyse und Design
• Implementierung
• Test
• Überführung in die Einsatzumgebung
• Konfigurations- und Änderungsmanagement
• Projektmanagement
• Projektumfeld
So simpel und einleuchtend die Ausführungen bisher auch sind, einen ersten Einblick in
die Komplexität des RUP gibt Abb. 2.10, wo das Zusammenspiel zwischen Rollen, Akti-
vitäten und Artefakten dargestellt wird.
SEI11 29
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Der RUP versucht für jedes mögliche Projekt eine Vorgehensweise bereitzuhalten und
dies mit der in Abb. 2.10 dargestellten Notation zu beschreiben. Damit werden sehr viele
Abhängigkeiten zwischen den Elementen des RUP erzeugt, die zu der hohen Komplexi-
tät führen. Daher ist auch eine Anpassung („Tailoring“) des RUP an das jeweilige Projekt
unerlässlich.
Agile Softwareentwicklung
Agile Softwareentwicklung ist der Oberbegriff für den Einsatz von Agilität (lat. agilis:
flink; beweglich) in der Softwareentwicklung. Der Begriff unterscheidet – je nach Teil-
bereich der Softwareentwicklung – Agile Modeling, Agile Processing oder Agile Pro-
gramming. Agile Softwareentwicklung versucht mit geringem bürokratischem Auf-
wand und wenigen Regeln auszukommen.
In Abb. 2.11 sehen Sie das mittlerweile „berühmte“ Agility-Poster, das die wichtigsten
Grundsätze dieser Methode zeigt.
Zweck agiler Softwareentwicklung ist es, den Softwareentwicklungsprozess flexibel und
schlank zu machen. Der Fokus liegt dabei mehr auf den zu erreichenden Zielen sowie
auf technischen und sozialen Problemen bei der Softwareentwicklung. Die agile Soft-
wareentwicklung kann als eine Gegenbewegung zu den oft als schwergewichtig und bü-
rokratisch angesehenen traditionellen Softwareentwicklungsprozessen wie dem Ratio-
nal Unified Process oder dem V-Modell angesehen werden.
Erste Ansätze zu agiler Softwareentwicklung finden sich schon Anfang der 1990er- Jah-
re; wirkliche Popularität erreichte die agile Softwareentwicklung erstmals 1999, als Kent
Beck das erste Buch zu Extreme Programming veröffentlichte. Das Interesse an diesem
Thema ebnete den Weg auch für andere agile Prozesse und Methoden. Die Bezeichnung
„agil“ für diese Art der Softwareentwicklung tauchte erstmals im Februar 2001 bei ei-
nem Treffen in Utah auf, als Ersatz für das bis dahin gebräuchliche Wort „leichtgewich-
tig“ (engl. lightweight). Bei diesem Treffen wurde auch das sogenannte Agile Manifest
formuliert. Dieses lautet:
1) Individuen und Interaktionen sind wichtiger als Prozesse und Werkzeuge. – Zwar
sind wohldefinierte Entwicklungsprozesse und Entwicklungswerkzeuge wichtig,
wesentlicher sind jedoch die Qualifikation der Mitarbeitenden und eine effiziente
Kommunikation zwischen ihnen.
2) Funktionierende Programme sind wichtiger als ausführliche Dokumentation. – Gut
geschriebene und ausführliche Dokumentation kann zwar hilfreich sein, das eigent-
liche Ziel der Entwicklung ist jedoch die fertige Software.
3) Die stetige Abstimmung mit dem Kunden ist wichtiger als die ursprüngliche Leis-
tungsbeschreibung in Verträgen. – Statt sich an ursprünglich formulierten und mitt-
lerweile veralteten Leistungsbeschreibungen in Verträgen festzuhalten, steht viel-
mehr die fortwährende konstruktive und vertrauensvolle Abstimmung mit dem
Kunden im Mittelpunkt.
4) Der Mut und die Offenheit für Änderungen stehen über dem Befolgen eines festge-
legten Plans. – Im Verlauf eines Entwicklungsprojektes ändern sich viele Anforde-
rungen und Randbedingungen ebenso wie das Verständnis des Pro-blemfeldes. Das
Team muss darauf schnell reagieren können.
30 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Das Agile Manifest weist 17 Autoren und Erstunterzeichner aus, die auf unterschiedli-
chen Gebieten der agilen Softwareentwicklung tätig sind.
Agiles Prinzip
Ein agiles Prinzip ist ein Leitsatz für die agile Arbeit.
Manchmal werden agile Prinzipien auch als Methode bezeichnet.
Beispiele für agile Prinzipien:
• vorhandene Ressourcen mehrfach verwenden
• einfach (KISS-Prinzip)
• zweckmäßig
• kundennah
• gemeinsamer Code-Besitz (Collective Code Ownership)
• Der Übergang zwischen Prinzipien und Methoden ist fließend.
3. Quelle: Wikipedia.de
SEI11 31
© HfB, 02.12.20, Berk, Eric (904709)
2 Phasenmodelle
Agile Methode
Streng genommen bezeichnet der Begriff agile Methode eine an Agilität ausgerichtete
Methode zur Softwareentwicklung. Ein Kennzeichen agiler Methoden ist, dass sie in ei-
nem Prozess dazu dienen können, die Aufwandskurve möglichst gering zu halten. Als
Leitsatz gilt: Je mehr man nach Plan arbeitet, umso mehr bekommt man das, was man
geplant hat, aber nicht das, was man wirklich braucht. Daraus resultieren einige Prinzi-
pien des Agile Modelling und des Extreme Programming. Agile Methoden lassen sich in
zwei Gruppen unterteilen: die tatsächlichen Methoden und die den Methoden zugrunde
liegenden Prinzipien.
Beispiele für agile Methoden:
• Paarprogrammierung
• testgetriebene Entwicklung
• ständige Refaktorisierungen
• Story-Cards
• schnelle Codereviews
Agiler Prozess
Ziel der Vorgehensweise ist es, den Softwareentwicklungsprozess durch Abbau der Bü-
rokratisierung und durch die stärkere Berücksichtigung der menschlichen Aspekte ef-
fektiver zu gestalten. Den meisten agilen Prozessen liegt zugrunde, dass sie versuchen,
die reine Entwurfsphase auf ein Mindestmaß zu reduzieren und im Entwicklungsprozess
so früh wie möglich zu ausführbarer Software zu gelangen, die dann in regelmäßigen,
kurzen Abständen dem Kunden zur gemeinsamen Abstimmung vorgelegt werden kann.
Auf diese Weise soll es jederzeit möglich sein, flexibel auf Kundenwünsche einzugehen,
um so die Kundenzufriedenheit insgesamt zu erhöhen. Sie stellen damit einen Gegensatz
zu den klassischen Vorgehensmodellen wie dem V-Modell oder dem Rational Unified
Process (RUP) dar.
Allen agilen Prozessen ist gemeinsam, dass sie sich zahlreicher Methoden bedienen, die
Aufwandskurve möglichst flach zu halten. Inzwischen gibt es eine Vielzahl von agilen
Prozessen.
Beispiele für agile Prozesse:
• Adaptive Software Development (ASD)
Dies ist ein Softwareentwicklungsprozess, der auf das Rapid Application Develop-
ment zurückgeht; ASD ist eine Umsetzung des Prinzips der kontinuierlichen Anpas-
sung an immer neue Anforderungen (eher der Normalzustand) und ersetzt damit das
verbreitete Wasserfallmodell durch Zyklen von „Spekulieren“, „Zusammenarbeiten“
und „Lernen“.
• Crystal
Dabei handelt es sich um eine Familie von Softwareentwicklungsmethoden, die zu
den agilen Methoden der Softwareentwicklung gerechnet wird. Die Mitglieder die-
ser Familie sind in der Regel mit Farben bezeichnet. Die einfachste Variante heißt
hingegen Crystal Clear („glasklar“).
32 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Phasenmodelle 2
Zusammenfassung
Es wurden die zwei wichtigsten Phasenmodelle, das Wasserfallmodell sowie das Spiral-
modell, ausführlich besprochen. Zusätzlich wurden noch das V-Modell sowie das Dia-
mantmodell erläutert. Außerdem wurden die inhaltlichen Komponenten des Pflichten-
hefts besprochen; dazu gehört insbesondere das semantische Datenmodell als integraler
Bestandteil. Schließlich wurde noch auf neuere Methoden, wie RUP und agile Soft-
wareentwicklung, eingegangen.
SEI11 33
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
In diesem Kapitel besprechen wir die Grundlagen und Anwendungen von Soft-
wareergonomie. Sie sollen nach Durcharbeiten dieses Kapitels in der Lage sein,
• den Zweck von Softwareergonomie zu nennen,
• die Aufgaben eines Usability Engineers zu kennen,
• softwareergonomische Dialoggestaltung zu produzieren.
4. Wir folgen in diesem Abschnitt u. a. den Ausführungen des Deutschen Delegationsleiters im internationalen
Normenausschuss für Softwareergonomie, Wolfgang Redtenbacher (vgl. www.redtenbacher.de) sowie dem
„Handbuch Softwareergonomie“ der Unfallkasse Post und Telekom.
5. www.datech.de
34 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
SEI11 35
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
Antworten oder wenn man die „Fachseite“ fragt, bekommt man unterschiedliche Aus-
sagen aus unterschiedlichen Abteilungen. Dies ist der Punkt, an dem sich herausstellt,
dass die Anforderungen nicht ausreichend mit Benutzern abgestimmt sind. Benutzer
können zwar sagen, was sie benötigen, aber nicht, wie sie es technisch realisiert haben
wollen. Dafür ist eine koordinierte Zusammenarbeit der Beteiligten erforderlich.
In Abb. 3.2 sehen Sie den vom DIN geforderten Übergang vom Prozess zum Produkt.
Wie man erkennen kann, greifen die angesprochenen Punkte ineinander und besitzen
gewisse Schnittstellen, die vom Usability Engineer erkannt und berücksichtigt werden
müssen.
Zur „Gebrauchstauglichkeit“ einer Software stellt der deutsche Delegationsleiter im in-
ternationalen Normenausschuss für Softwareergonomie, Wolfgang Redtenbacher, fest6:
Die Gebrauchstauglichkeit einer Software wird dadurch bestimmt, in welchem Maße sie
es ermöglicht, Arbeitsziele in einem Nutzungskontext effektiv, effizient und zufrieden-
stellend zu erreichen.
Effektivität bedeutet dabei, ob der Benutzer die vorgesehenen Aufgaben mit der Soft-
ware erledigen kann und die benötigten Ergebnisse korrekt erreicht werden (z. B. dass
ein Textverarbeitungsprogramm einen geschriebenen Brief richtig ausdruckt oder ein
Übersetzungsprogramm den gewünschten Begriff richtig übersetzt).
Die Effizienz der Software wird durch den Aufwand bestimmt, den der Benutzer zur Er-
reichung der Arbeitsziele mit der Software treiben muss. Nützliche Software soll zu ei-
ner Arbeitserleichterung führen, d. h. zu einer Senkung des notwendigen Bedienauf-
wands und ggf. der aufzuwendenden Denkarbeit.
6. Quelle: www.redtenbacher.de
36 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
Unter der Zufriedenstellung der Benutzer versteht man schließlich das Ausmaß, wie
gern die Benutzer die Arbeitsaufgabe mit der Software als Werkzeug erledigen. Zufrie-
denstellung umfasst den subjektiven Eindruck der Benutzer von der Effizienz und der
Beeinträchtigungsfreiheit (z. B. durch verringerten Stress) bei ihrer Arbeit.
Eine Software ist also dann ergonomisch (gebrauchstauglich), wenn sie für die auszu-
führenden Aufgaben des Benutzers geeignet ist und dabei insbesondere die Hauptaufga-
ben und Benutzereigenschaften unterstützt. Wenn der Benutzer durch fehlende Funkti-
onalität, durch Mehraufwand oder durch fehlerhafte Ergebnisse wesentlich
beeinträchtigt wird, dann ist die Software nicht ergonomisch im Sinne der Bildschirm-
arbeitsverordnung.
Salopp übersetzt ist also „Usability“ (Gebrauchstauglichkeit) die Summe von Effektivi-
tät, Effizienz und Zufriedenstellung.
7. www.redtenbacher.de
SEI11 37
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
4. Lernförderlichkeit: Ein Dialog ist lernförderlich, wenn er den Benutzer in den Lern-
phasen unterstützt.
Beispiel:
Wenn eine Software aufgabenangemessen und selbstbeschreibungsfähig ist und sich
erwartungskonform verhält, ist bereits viel für leichte Erlernbarkeit getan. Eine lern-
förderliche Software würde z. B. zusätzlich ein Learning by Doing ermöglichen, d. h.
das Ausprobieren neuer Funktionen erlauben, ohne den Benutzer für Fehler gleich
durch Datenverlust o. Ä. zu „bestrafen“ und dadurch jede Experimentierfreude im
Keim zu ersticken.
5. Steuerbarkeit: Ein Dialog ist steuerbar, wenn der Benutzer in der Lage ist, den Dia-
logablauf zu starten sowie seine Richtung und Geschwindigkeit zu beeinflussen, bis
das Ziel erreicht ist.
Beispiel:
Wenn eine Software zur Verwaltung von Mietwohnungen auch an Arbeitsplätzen
eingesetzt wird, an denen telefonische Auskünfte erteilt werden, so sollte es dort
möglich sein, ein erst teilweise ausgefülltes Eingabeformular ohne Datenverlust zu
unterbrechen, um die Daten des Anrufers schnell auf den Bildschirm holen zu kön-
nen.
6. Fehlertoleranz: Ein Dialog ist fehlertolerant, wenn das beabsichtigte Arbeitsergeb-
nis trotz erkennbar fehlerhafter Eingaben entweder mit keinem oder mit minimalem
Korrekturaufwand durch den Benutzer erreicht werden kann.
Beispiel:
Wenn in einem Eingabeformular z. B. im PLZ-Feld eine ungültige Eingabe steht, so
würde dies bei einer fehlertoleranten Software nicht zu fehlerhafter Verarbeitung
führen, sondern das Programm würde den Fehler erkennen und den Cursor gleich
zur Korrektur in das betreffende Feld stellen.
7. Individualisierbarkeit: Ein Dialog ist individualisierbar, wenn er an persönliche
Anforderungen und Fähigkeiten des Benutzers angepasst werden kann.
Beispiel:
Bei einer individualisierbaren Software kann z. B. die Schriftgröße für sehbehinderte
Benutzer vergrößert werden oder die Zuordnung der Maustasten kann für Linkshän-
der angepasst werden.
Ähnlich wie es für die ergonomische Gestaltung der dynamischen Dialogabläufe einer
Software die 7 „Grundsätze der Dialoggestaltung“ aus der Norm ISO 9241-10 gibt, so
existieren auch für die statische Informationsdarstellung 7 Grundsätze, die in der inter-
nationalen Norm ISO 9241–12 „Informationsdarstellung“ beschrieben sind:
1) Erkennbarkeit (die Aufmerksamkeit des Benutzers wird zur benötigten Information
gelenkt)
2) Unterscheidbarkeit (die angezeigte Information kann genau von anderen Daten un-
terschieden werden)
3) Lesbarkeit (die Information ist leicht zu lesen)
4) Verständlichkeit (die Bedeutung ist leicht verständlich, eindeutig, vermittelbar und
erkennbar)
38 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
Vordergrund (Schrift)
Hintergrund schwarz weiß magenta blau zyan grün gelb rot
schwarz n.a. + + - + + + -
weiß + n.a. + + - - - +
magenta + + n.a. - - - - -
blau - + - n.a. + - + -
zyan + - - + n.a. - - -
grün + - - + - n.a. - -
gelb + - + + - - n.a. +
rot - + - - - - + n.a.
In Abb. 3.4 sehen wir eine kleine Liste, die diverse Probleme bezüglich der Softwareer-
gonomie darstellt:
SEI11 39
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
Wer nun glaubt, Softwareergonomie beziehe sich „nur“ auf das Layout der Bildschirme
und deren Bedienung, der irrt. Obwohl dies natürlich ein entscheidender Faktor ist, ver-
birgt sich hinter ergonomischer Software doch viel mehr, wie man schon aus Abb. 3.4
erahnen kann. In Abb. 3.5 ist dies noch deutlicher zu erkennen.
Bereits bei der Modellierung der Daten ist die softwareergonomische Auswirkung er-
heblich; die Eingabe von Daten und deren logische Zusammenhänge werden hier ja vor-
definiert. Sind hier schon Daten so modelliert, dass ein Anwender später umständlich
und evtl. sogar redundant Daten eingeben muss, dann kann die Oberfläche noch so
schön sein, dieser Mangel wird jeden Anwender stören.
40 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
SEI11 41
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
Eine softwareergonomische Version der gleichen Seite stellt sich für einen Redakteur na-
türlich gänzlich anders dar (Abb. 3.7):
Es ist durch die Verbreitung von Entwicklungswerkzeugen in neuerer Zeit glücklicher-
weise häufig so, dass diese Werkzeuge dem Entwickler bei der Entwicklung von GUIs
die wichtigsten softwareergonomischen Gesichtspunkte auf Wunsch schon vorgeben. So
bieten z. B. Entwicklungstools für Webseiten oft vorgefertigte Templates, die sich soft-
wareergonomisch bewährt haben. Der Entwickler ist gut beraten, sich an diesen Design-
mustern zu orientieren.
42 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
In Abb. 3.7 sieht man, dass hier eine eher datenbezogene, etwas „sterilere“ Darstellung
gewählt wurde, die aber für den Redakteur zweckmäßiger ist. Beim Anklicken des Links
„Ändern“ erscheint dann eine Art „Zoom“ in den betreffenden Datensatz, der schließlich
die redaktionellen Einträge und Anpassungen ermöglicht (Abb. 3.8).
Es gibt natürlich auch Content-Management-Systeme (CMS), die das Layout für den Re-
dakteur so nachbilden, wie es der Surfer sieht. Doch das ist in vielen Fällen eher un-
zweckmäßig, da, wie gesagt, die Zielgruppen völlig verschieden sind.
SEI11 43
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
Was die reine Oberflächengestaltung betrifft, so ist dies oft eine Frage des Geschmacks.
Verschiedene Menschen empfinden die gleiche Benutzeroberfläche manchmal als sehr
angenehm, während andere wiederum damit nicht zurechtkommen. Dies sollte beim Er-
stellen eines Pflichtenhefts berücksichtigt werden. So helfen bereits in dieser Phase Skiz-
zen von zukünftigen Bildschirmmasken, die mit den Benutzern abgesprochen werden
sollen. Existiert bereits Software, die von den späteren Benutzern bereits benutzt wird
44 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Softwareergonomie 3
und gut ankommt, dann sollte man sich an diesem Bildschirmlayout orientieren, damit
die Einarbeitung und Benutzung der neu zu schreibenden Anwendung möglichst unpro-
blematisch wird.
Zusammenfassung
3.1 Für die Wandlung von Video- und Audioformaten gibt es diverse Freeware. Wie
beurteilen Sie folgende beide Benutzeroberflächen unter dem Gesichtspunkt der
Softwareergonomie?
SEI11 45
© HfB, 02.12.20, Berk, Eric (904709)
3 Softwareergonomie
46 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Projektbeschreibung (Beispiel)
Es ist eine Software zu entwickeln, die die Abrechnung von Tonträgerherstellern (nach-
folgend „Lizenznehmer“ genannt) mit ihren Lizenzgebern (Künstler oder Produzenten)
ermöglicht. Der Arbeitsablauf gestaltet sich wie folgt:
1) Der Tonträgerhersteller bekommt von einem Künstler oder Produzenten
(Lizenzgeber) ein Tonband oder einen sonstigen Träger des Mastertapes einer Mu-
sikaufnahme zur Verwertung angeboten. Entscheidet sich der Tonträgerhersteller
dazu, die Aufnahme(n) herauszubringen, so wird ein Lizenzvertrag abgeschlossen.
SEI11 47
© HfB, 02.12.20, Berk, Eric (904709)
2) Der Lizenzvertrag enthält Angaben über die Titel des Mastertapes, deren Komponis-
ten, Texter, Bearbeiter, Verwertungsgesellschaft (z. B. GEMA), Musikverlage, Spiel-
dauern, Interpreten, Lizenzgeber, Lizenznehmer, Exklusivität (ja/nein), Vertragsdau-
er, Erstauflage der VHS, ggf. geplante Folgeauflagen, Erstlabelangabe, Länder der
Verwertung, Prozentsatz der Lizenzgeberbeteiligung am Großhandelsverkaufspreis,
Abrechnungsturnus, Vorschuss, Sonstiges. Der Lizenzvertrag muss ausdruckbar
sein.
3) Alle Personen und Verlage sind in einer eigenen Tabelle (Adressverwaltung) nicht-
redundant zu verwalten.
4) Alle Tonträger sind in einer Tabelle zu verwalten, zusammen mit ihren Auflagen. Es
soll ein Zugriff auf die Verkaufszahlen (inkl. Historie) in vorgebbaren Zeiträumen
möglich sein.
5) Es soll möglich sein, Statistiken zu erstellen (auch grafisch), und zwar
• über alle verkauften Tonträger in einem angebbaren Zeitraum,
• über die Verkaufszahlen eines bestimmten Tonträgers in einem angebbaren Zeit-
raum,
• über die Verkaufszahlen aller Produkte auswählbarer Lizenznehmer in einem
angebbaren Zeitraum,
• über den Verkaufsverlauf einzelner Titel (also evtl. über mehrere Tonträger hin-
weg),
• über den Verkaufsverlauf einzelner Komponisten (also evtl. über mehrere Ton-
träger hinweg),
• über den Verkaufsverlauf der Werke einzelner Verlage (also evtl. über mehrere
Tonträger hinweg).
6) Es soll möglich sein, eine Lizenzabrechnung für die Lizenzgeber über einen festleg-
baren Zeitraum durchzuführen (inkl. Historie), die den Zahlbetrag abhängig vom Li-
zenzvertrag und den Verkaufszahlen der beteiligten Tonträger des Lizenzgebers an-
gibt. Eine Aufstellung, welche Tonträger wie oft verkauft wurden, sollte vorhanden
sein.
Diese Angaben sind allerdings noch zu lückenhaft, um daraus schon die fertige Software
entwickeln zu können. Diese Lücken müssen in einer genaueren Analyse durch weitere
intensive Kommunikation zwischen Auftraggeber und Auftragnehmer geschlossen wer-
den. Doch für eine erste grobe Kostenabschätzung sollte diese Problembeschreibung
ausreichend sein, zumindest dann, wenn der Auftragnehmer über hinreichend Erfah-
rung auf diesem Gebiet verfügt. Der nächste Schritt ist dann ein Angebot des Auftrag-
nehmers an den Auftraggeber. Oft wird dabei ein Festpreis angeboten. Es ist aber noch-
mals darauf hinzuweisen, dass so etwas nur gemacht werden sollte, wenn der
Auftragnehmer über hinlänglich Erfahrung und „Gefühl“ für die auf ihn zukommende
Arbeit verfügt.
Ist der Auftraggeber mit dem Angebot einverstanden, wird er eine Auftragsbestätigung
bzw. eine Bestellung an den Auftragnehmer schicken. Damit kommt juristisch ein Ver-
trag zustande, und beide Vertragspartner sind zur Einhaltung der vereinbarten Punkte
verpflichtet.
48 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Zusammenfassend kann man also sagen: Die erste Problemanalyse dient dem Zweck der
Auftragserstellung. Der Aufwand für diese erste Analyse sollte nicht mehr als 15 % des
voraussichtlichen Gesamtaufwands betragen, da es ja sein kann, dass der Auftraggeber
das Angebot ablehnt und die Zeit für die erste Problemanalyse in der Regel nicht vergü-
tet wird.
Naturgemäß schließt sich an diese erste Problemanalyse mit Auftragsvergabe (im Was-
serfallmodell wurde dies Initialisierung genannt) eine verfeinerte Analyse an, die zur
nächsten Phase des Wasserfallmodells führt: dem Fachkonzept.
SEI11 49
© HfB, 02.12.20, Berk, Eric (904709)
5) Unternehmensspezifische Besonderheiten
6) Bewertung des Istzustandes – Aufwand und Nutzen – Stärken und Schwächen
3. Zielsetzungen
1) Erwarteter quantifizierbarer Nutzen
2) Sonstige erwartete Vorteile
4. Anforderungen an die geplante Anwendungssoftware
1) Fachliche Anforderungen
1. Überblick und Zusammenhänge
2. Detaillierte Anforderungen an die Arbeitsgebiete 1 … n
• wesentliche Verfahren
• wesentliche Ein- und Ausgabeinformationen
• Verarbeitungsarten und -häufigkeit
• unternehmensspezifische Besonderheiten
2) Technische Anforderungen
1. Qualitätsanforderungen – Integration
• Dateiorganisation
• Zugriffsberechtigung, Datensicherheit und -rekonstruktion
• Form der Programmauslieferung
2. Dokumentation und Schulung
[Differenzierung in funktional und nichtfunktional]
5. Mengengerüst
1) Kartei/Stammdaten
2) Bestandssätze, Belege/Zahl der Bewegungen
3) sonstige Mengen- und Häufigkeitsangaben
6. Anforderungen an Hardware und Systemsoftware
7. Mitarbeiter für die Umstellung
8. Zeitlicher Rahmen
Um die Punkte eines Pflichtenhefts alle hinreichend zu erfüllen, ist es natürlich erfor-
derlich, dass gewisse Analysen beim Auftraggeber durchgeführt werden.
Es handelt sich bei dieser DIN-Beschreibung um den Maximalfall. Bei kleineren Projek-
ten sind nicht alle Punkte dieser Festlegung zu berücksichtigen.
Wir betrachten nachfolgend einige Methoden, mit denen die genannten Unterpunkte ei-
nes Pflichtenhefts teilweise erstellt werden. Es sind dabei nicht alle diese Methoden in
jedem Projekt erforderlich, das muss man jeweils konkret mit dem Auftraggeber ent-
scheiden. Auch werden, um den Rahmen dieses Studienhefts nicht zu sprengen, nach-
folgend nicht alle vorhandenen Verfahren vorgestellt; wir haben eine Auswahl getroffen,
von der wir glauben, dass sie die meisten Fälle abdecken kann.
50 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Istzustand
Außer einer verbalen Beschreibung des Istzustandes bedient man sich häufig noch zu-
sätzlich sogenannter Use-Case-Diagramme (manchmal auch nur U-Case-Diagramme
genannt). Diese bestehen lediglich aus kleinen Strichmännchen, die die beteiligten Per-
sonen kennzeichnen, sowie aus beschrifteten Ellipsen, die Vorgänge darstellen sollen. In
unserem Beispiel der Lizenzabrechnung kann ein solches Diagramm wie folgt aussehen:
Authors
Publisher/
Producer
Payoff 3 GEMA Reg.
Performer
Release Record
Record Studio
Record Company
Payoff 1
Distributor
SEI11 51
© HfB, 02.12.20, Berk, Eric (904709)
Ein U-Case-Diagramm bedarf immer auch einer verbalen Erläuterung. Diese könnte
z. B. so lauten:
Ein Autor (in Abb. 4.1 Strichmännchen oben) offeriert eine
Komposition (Song) einem Musikverleger oder Produzenten
(Publisher/Producer, Strichmännchen oben Mitte). Gefällt dem
Verleger das Lied, so wird ein Verlagsvertrag gemacht (Publishing
Contract, oben rechts) und die relevanten Daten in einer Kartei
abgelegt (Files Song in Database, oben rechts) sowie eine
Registrierung bei der GEMA durchgeführt. Jetzt beauftragt der
Verleger einen Produzenten (bzw. manchmal sind Verleger und
Produzent identisch) mit der Suche nach einem Interpreten
(Performer/Artist, Strichmännchen Mitte links). Wurde ein solcher
gefunden, so wird mit demselben ein Künstlervertrag abgeschlossen
(Artist Contract). Danach geht der Produzent mit dem Künstler in
ein Tonstudio (Record Studio, Strichmännchen links unten) und
produziert (mind.) einen Song. Das Ergebnis ist ein sogenanntes
Mastertape, also eine Aufnahme des fertig produzierten Liedes. Der
Produzent macht sich jetzt auf die Suche nach einem
Tonträgerhersteller, also der eigentlichen Plattenfirma (Record
Company, Strichmännchen Mitte unten). Hat er diese gefunden, so
wird jetzt zwischen dem Produzenten und dem Tonträgerhersteller
ein weiterer Vertrag abgeschlossen, der sogenannte Lizenzvertrag
(License Contract, Mitte rechts). Die Plattenfirma lässt daraufhin
die Tonträger mit dem Song vervielfältigen und veröffentlicht
diesen (Release Record, rechts unten), indem sie einen Distributor
(Strichmännchen rechts unten) mit dem Vertrieb der Tonträger
beauftragt (der die Tonträger schließlich den Schallplatten-
geschäften verkauft). In regelmäßigem Turnus rechnet der
Distributor mit der Plattenfirma über die verkauften Tonträger ab,
d. h., er zahlt nach Abzug seiner Provision einen bestimmten
Betrag (Payoff 1, ganz unten Mitte) an die Plattenfirma. Diese
wiederum zahlt an den Produzenten die im Lizenzvertrag vereinbarte
Provision (Payoff 2, Bildmitte). Der Produzent schließlich zahlt
daraufhin an den Künstler die im Künstlervertrag vereinbarte
Beteiligung (Payoff 3). Zu beachten ist dabei, dass zwar eine
Abrechnung nach Anzahl verkaufter Tonträger erfolgt, jedoch sind
ggf. nicht alle Songs eines Tonträgers abrechnungsfähig, sondern
nur diejenigen, die dem entsprechenden Lizenzvertrag zugrunde
liegen. Es ist also bei der Lizenzabrechnung nur der tatsächliche
Anteil an Liedern eines Tonträgers zu berücksichtigen. Der Autor
erhält seine Zahlungen entweder direkt von der GEMA (an die
Rundfunkunternehmen ihre Sendetantieme entrichten müssen und der
Tonträgerhersteller für jede gepresste Schallplatte eine
Schutzgebühr zahlen muss) oder, falls er selbst nicht GEMA-
Mitglied ist, vom Musikverlag (gemäß den Vereinbarungen im
Verlagsvertrag), der in der Regel dann GEMA-Mitglied ist und von
dort die entsprechenden Anteile erhält.
Diese Istzustandsbeschreibung ist immer noch relativ ungenau. Sie enthält beispielswei-
se keine genauen Angaben über die Attribute (z. B. welche Daten in den jeweiligen Ver-
trägen in welchem Format erfasst werden) oder in welchen Zeiträumen wie genau die
einzelnen Abrechnungen erfolgen etc.; so etwas kann entweder noch im Pflichtenheft
erfolgen, in dem z. B. ein entsprechendes Entity Relationship Model erzeugt wird (was
am besten wäre), oder dies erfolgt während der DV-Entwurfsphase (wo es spätestens
52 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
vorliegen muss). Des Weiteren wären der Istanalyse noch Beispielexemplare der betei-
ligten Verträge sowie ein Muster der Abrechnungen beizufügen. Diese sind also dem
Auftragnehmer auszuhändigen. Beispiele dafür siehe Abb. 4.2 bis Abb. 4.5.
SEI11 53
© HfB, 02.12.20, Berk, Eric (904709)
Musikverlagsvertrag
zwischen
zugleich für seine/ihre Erben und Rechtsnachfolger, im folgenden URHEBER genannt, und dem Verlag
(Musikverlag)
54 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Künstlervertrag
zwischen
SEI11 55
© HfB, 02.12.20, Berk, Eric (904709)
License Agreement
This agreement entered Into between (“LESSOR”) and (hereinafter called LICENSEE).
WHEREAS LESSOR represents and warrants that it has the exclusive rights for all the recordings
(hereinafter called “The Master Recordings”) and WHEREAS LICENSEE is an a position directly
or indirectly to provide manufacturing and marketing faculties for phonograph records in ( ) only
(hereinafter called “The Licensed Territory”), and warrants a release of such phonograph records.
Now therefore, in consideration of the foregoing and of the mutual promises hereinafter set forth,
it is agreed:
1.) LESSOR grants to LICENSEE the exclusive right and license to use THE MASTER
RECORDINGS for the purpose of manufacturing and selling phonograph records and/or
prerecorded tapes well known as MusiCasettes and/or 8-Track Cartridges therefrom in the
LICENSED TERRITORY.
a) LICENSEE is not authorized to transfer the right and license to use THE MASTER
RECORDINGS to a third party.
2.) LICENSEE agrees to release THE MASTER RECORDINGS under the label as a trademark
and in the form as described in the riders hereto only.
3.) a) In consideration of the rights herein granted, LICENSEE agrees to pay to LESSOR the
royalties stated in the rider A hereto as percentage of the retail list price (exclusive of sales
taxes) for all soundcarriers without any other deduction manufactured from THE
MASTER RECORDINGS leased hereunder and shipped In THE LICENSED
TERRITORY.
b) LICENSEE agrees to inform LESSOR of such retail list price in force in THE
LICENSED TERRITORY within 30 (thirty) days from uk date of countersigning of this
contract and will notify LESSOR of any changes thereof within 14 (fourteen) days of any
such change.
c) LICENSEE, agrees that it will not sell, directly or indirectly THE MASTER
RECORDINGS under the normal retail list price In THE LICENSED TERRITORY
without written permission by LESSOR.
4.) LICENSEE further agrees to pay to LESSOR Fifty (50) percent of all public performance and
broadcasting and tv fees, if any received in respect of all records manufactured, leased and sold
hereunder. provided, however, that whenever such fees are not computed and paid in direct
relation to the public perfomances and broadcasts and tv’s of such records, they shall be
computed for the purpose of the agreement by determining the proportion of any such fee
paid to LICENSEE as the number of records produced hereunder and sold in the area from
which the fee is derived bears to the total number of records sold in that area by LICENSEE.
5.) I. Statements in reasonable detail by LICENSEE to LESSOR of royalties and fees due,
pursuant to paragraph 3a and 4 hereof, shall be made every six month calendar period and
mailed to LESSOR within fourtyfive (45) days following the dose of such a period. Each
statement shall show at least the following;
a) catalogue-number of LICENSEE, title and artist(s) of each such sound-carrier
b) the quantity of sound-carriers distributed on behalf of LICENSEE during that
period
c) the quantity of sound-carrier manufactured from each master throughout the
applicable accounting period
d) the retail list price of each such sound-carrier
e) the royalty rate paid on each such sound-carrier
f ) the total royalties earned by each such sound-carrier
II. Together with the statements the payable royalties have to be received by LESSOR.
6.) In the event that by law any royalty-payment due to LESSOR hereunder cannot be
transmitted from the country in which such royalties are earned, LICENSEE agrees to notify
LESSOR within one week after such period as mentioned in paragraph 5 by registered written
letter and to put such payment at the disposal of LESSOR by depositing same at a bank-
account to be opened by LICENSEE at ist expense but in the name of LESSOR and of which
account LESSOR shall have the power of disposing only.
56 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Als Nächstes muss der Sollzustand beschrieben werden. Dieser soll ja später einen be-
stimmten Teil (oder alles) des beschriebenen Istzustands durch ein Anwendungspro-
gramm ersetzen. Es geht dabei gerade darum, dass durch Einsatz einer zu entwickelnden
Software gewisse Abläufe des Istzustands erleichtert bzw. automatisiert werden sollen.
Daher ist es wichtig, dass mit Bezug auf den Istzustand genau beschrieben wird, welche
Teile durch Software abgedeckt werden sollen und welche Inputs und Outputs dabei ge-
nau auftreten. Dies erfolgt durch die Beschreibung des Sollzustands.
SEI11 57
© HfB, 02.12.20, Berk, Eric (904709)
Sollzustand
Eine erste Beschreibung des Sollzustands erfolgte bereits in der ersten Problem-
spezifikation, die (in der Initialisierungsphase) der Angebotserstellung diente. Nun geht
es darum, diese Beschreibung zu verfeinern, sodass daraus (ggf. zusammen mit den
nächsten Kapiteln) später ein DV-Entwurf entwickelt werden kann.
Ist der Istzustand hinreichend beschrieben, dann geht es bei der Sollzustands-
beschreibung eigentlich nur noch darum, welche Teile der Realität wie auf dem Compu-
ter abgebildet werden sollen. Je nachdem, wie das Pflichtenheft strukturiert ist, kann
jetzt bereits eine ausführliche Beschreibung erfolgen, oder, falls dies später im Pflichten-
heft vorgenommen wird, eine grobe, mehr prinzipielle Darstellung. Dies sei in das Er-
messen bzw. die Gepflogenheiten der beteiligten Unternehmen gestellt. Für unsere Zwe-
cke wählen wir den Weg, dass zunächst eine allgemeine Sollbeschreibung vorgenommen
wird. Die nachfolgenden Abschnitte geben dann weitere, detailliertere Informationen.
Eine Sollzustandsbeschreibung bezogen auf unser Beispiel könnte demgemäß z. B. so
aussehen:
Das zu entwickelnde Softwareprogramm soll den Teil des Istzustands
implementieren, der die administrativen Arbeiten bei der
Vertragserstellung der Künstler- und Lizenzverträge abwickelt
sowie die Lizenzabrechnungen automatisch erstellt. Die Arbeiten
der Musikverlagsverwaltung werden dabei ausgekoppelt und evtl. zu
einem späteren Zeitpunkt ebenfalls automatisiert (eigenes
Projekt). Es sollen sowohl (freie) Produzenten als auch
Schallplattenfirmen mit dem Programm arbeiten können. In ersterem
Fall werden Künstlerverträge erstellt und abgerechnet und im
zweiten Fall Schallplattenverträge. Grundlage der Abrechnung ist
in ersterem Fall die Abrechnung des Produzenten mit dem Künstler
und in zweitem Fall die Lizenzabrechnung der Plattenfirma an den
Produzenten. Die elektronisch abzuwickelnden Realitätsausschnitte
sind nachfolgendem U-Case-Diagramm (Abb. 4.6) zu entnehmen. Die
Benutzer des zu entwickelnden Programms werden dort mit User_1
(für den ersten Fall) und User_2 (für den zweiten Fall)
bezeichnet.
58 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Als Input für das zu schreibende Programm dienen also zum Zweck der Vertragserstel-
lung alle Angaben aus dem Künstler-/Lizenzvertrag (insbesondere die für die spätere
Abrechnung notwendigen Daten wie Vertragspartner, Vertragsdauer, prozentuale Betei-
ligungen etc.) und für die Abrechnung dann die Daten des Distributors (Payoff 1) und/
oder der Plattenfirma (Payoff 2) (z. B. über Anzahl und Verkaufspreis der verkauften
Tonträger). Der Output des Programms liefert dann die jeweiligen Verträge bzw. Ab-
rechnungen. Damit verbunden ist die Möglichkeit gewisser statistischer Auswertungen
(welcher Künstler hat wie viele Tonträger verkauft etc., siehe Problembeschreibung).
Natürlich müssen alle bereits erfolgten Abrechnungen gespeichert und doppelte Ab-
rechnungen verhindert werden. Außerdem ist eine Adressverwaltung für alle beteiligten
Firmen und Personen zu integrieren wie auch eine Tonträgerverwaltung, da sich in der
Regel auf einem Tonträger mehrere Songs (Mastertapes) befinden, die von verschiede-
nen Interpreten sein können (d. h. sich auf verschiedene Künstler-/Lizenzverträge bezie-
hen). Einzelheiten hierzu sind z. B. dem semantischen Datenmodell (vgl.
Abschnitt 4.2.2) zu entnehmen.
SEI11 59
© HfB, 02.12.20, Berk, Eric (904709)
4.2.2.1 Entscheidungstabellen
Mithilfe dieser Technik ist es möglich, dynamische Abläufe in kompakter Form zu be-
schreiben. Eine Entscheidungstabelle enthält in den Zeilen die jeweiligen Funktionen
und in den Spalten die Erfüllungsregeln bzw. die Reaktion, wenn ein Kriterium erfüllt
ist oder nicht.
Betrachten wir zur Erläuterung den Fall eines Programms, das zum Verleih von DVDs
in einer Videothek eingesetzt werden soll.
Beispiel 4.1:
Zunächst werden die jeweiligen Regeln („Wenn-dann-Beziehungen“) entworfen:
• Wenn der Kunde im Kundenstamm erfasst und die DVD verfügbar ist, dann soll
die DVD ausgegeben werden.
• Wenn der Kunde nicht im Kundenstamm erfasst ist, dann soll die DVD nicht
ausgegeben werden.
• Wenn der Kunde bereits 5 DVDs ausgeliehen hat, dann soll die DVD nicht aus-
gegeben und der Kunde informiert werden.
• Wenn der Kunde zum aktuellen Zeitpunkt eine oder mehrere DVDs länger als 60
Tage ausgeliehen hat, dann soll bis zur Rückgabe der ausgeliehenen DVDs keine
weitere DVD mehr ausgegeben und der Kunde informiert werden.
• Wenn die DVD nicht verfügbar ist, dann wird die DVD nicht ausgegeben und der
Kunde wird informiert.
• Eine Reservierung von DVDs ist nicht zulässig und soll daher nicht berücksich-
tigt werden.
60 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Aktionen ermitteln:
A1: DVD ausgeben
A2: DVD nicht ausgeben
A3: Kunde informieren „DVD nicht verfügbar“
A4: Kunde informieren „Anzahl überschritten“
A5: Kunde informieren „Leihdauer überschritten“
Bedingungen ermitteln:
B1: Kunde im Kundenstamm
B2: DVD verfügbar
B3: Anzahl ausgeliehener DVDs nicht größer als 5 Stück
B4: Leihdauer nicht größer als 60 Tage
Ausgangssituation:
VHS ausgeben R1 R2 R3 R4 R5 R6 R7 R8 R9
B1 Kunde im Kundenstamm J J J J J J J J N
B2 DVD verfügbar J J J J N N N N –
B3 auszugebende DVDs 5 J J N N J J N N –
B4 Leihdauer 60 Tage J N J N J N J N –
A1 DVD ausgeben
A3 Kundeninfo
„DVD nicht verfügbar“
A4 Kundeninfo
„Anzahl überschritten“
A5 Kundeninfo
„Leihdauer überschritten“
Nun ist es häufig so, dass nicht von vornherein die optimale Darstellung gefunden wird.
Es können dann gewisse Optimierungen erforderlich sein. Zum Beispiel liegen bei R5/
R6 und bei R7/R8 jeweils eigentlich die gleichen Aktionen zugrunde. Dies lässt sich so
vereinfachen:
SEI11 61
© HfB, 02.12.20, Berk, Eric (904709)
1. Erste Optimierung
B1 Kunde im Kundenstamm J J J J J J N
B2 DVD verfügbar J J J J N N –
B3 auszugebende DVDs 5 J J N N J N –
B4 Leihdauer 60 Tage J N J N – – –
A1 DVD ausgeben
A3 Kundeninfo
„DVD nicht verfügbar“
A4 Kundeninfo
„Anzahl überschritten“
A5 Kundeninfo
„Leihdauer überschritten“
B1 Kunde im Kundenstamm J J J J J N
B2 DVD verfügbar J J J J N –
B3 auszugebende DVDs 5 J J N N – –
A1 DVD ausgeben
A3 Kundeninfo
„DVD nicht verfügbar“
A4 Kundeninfo
„Anzahl überschritten“
A5 Kundeninfo
„Leihdauer überschritten“
62 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
DVD ausgeben R1 R2 R3 R4 R5 R6
B1 Kunde im Kundenstamm J J J J J N
B2 DVD verfügbar J J J J N –
B3 auszugebende DVDs 5 J J N N – –
B4 Leihdauer 60 Tage J N J N – –
A1 DVD ausgeben
A3 Kundeninfo
„DVD nicht verfügbar“
A4 Kundeninfo
„Anzahl überschritten“
A5 Kundeninfo
„Leihdauer überschritten“
SEI11 63
© HfB, 02.12.20, Berk, Eric (904709)
Beispiel:
name ist eine Komponente, mittelname ist wahlfrei. Die rechts stehenden Komponenten
müssen aber auch definiert werden. Dies kann z. B. geschehen durch:
Der Familienname muss also mindestens drei Zeichen und darf höchstens 32 Zeichen
lang sein.
64 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Entity (Entität)
Attribut
Verbindungslinie
1 n
Unternehmen hat Abteilung
umfasst
Mitarbeiter
m
betreut
Projekt
Die n : m-Beziehung in Abb. 4.7 bedeutet konkret, dass an einem Projekt m Mitarbeiter
beteiligt sein können und dass jeder Mitarbeiter an n Projekten beteiligt sein kann. Zu
betonen wäre noch, dass für die Beschriftungen der Entitäten und Beziehungen der Sin-
gular verwendet wird, während in Datenflussdiagrammen üblicherweise der Plural be-
nutzt wird.
SEI11 65
© HfB, 02.12.20, Berk, Eric (904709)
Objekt:
Unter einem Objekt versteht man eine Person oder einen Gegenstand (der Realität
oder Anschauung), auf die/den das Denken und Handeln bzw. jemandes Interesse ge-
richtet ist.
Diese Definition ist noch unkonkret, doch man versteht, dass es sich bei einem Objekt
um eine „Sache“ handelt, die wahrgenommen und hinlänglich beschrieben und über die
„nachgedacht“ werden kann. Im Software Engineering interessiert in erster Linie, wie
Objekte aus dem Leben auf den Computer abgebildet werden können. Natürlich gibt es
dabei auch Objekte, die ausschließlich auf einem Computer existieren, man denke z. B.
an eine Schaltfläche oder den Mauszeiger etc.; auch das sind Objekte, denn sie besitzen
unbestreitbar zumindest eine virtuelle Realität und man kann sein Interesse auf sie rich-
ten. Möchte man Objekte auf einem Computer abbilden, so müssen diese in Form von
Daten abgelegt werden. Und Daten auf dem Computer erfordern ein Datenformat, also
eine Datenstruktur. Die Beschreibung von Datenstrukturen und ihren Beziehungen
nennt man auch Datenmodell. Das ist aber noch nicht alles. Auf Daten muss man zu-
greifen können, d. h., man benötigt Zugriffsoperationen wie Edit, Delete, Add usw.; au-
ßerdem fasst man ähnliche Daten häufig zusammen. Es muss also Kriterien geben, die
die Zugehörigkeit (auch Integrität genannt) festlegen. Solche Zugehörigkeitskriterien
nennt man auch Integritätsbedingungen. Grob kann man also sagen, was für ein Objekt
im datentechnischen Sinn gelten muss:
Dadurch, dass also auch Operationen zu einem Objekt gehören, sind die Daten eines Ob-
jekts selbst nur über diese Operationen zugänglich. Man redet in diesem Zusammen-
hang von Datenkapselung. Die Operationen werden dabei als Funktionen implementiert
und dann Methoden genannt (vgl. Abb. 4.8). Ein erstelltes Datenmodell wird manchmal
auch Schema genannt.
66 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Zugriff
Methode 1 Methode 2
Methode 3 ...
Daten
... ...
... Methode n
Der Vorteil solch einer Vorgehensweise liegt auf der Hand: Die Methoden zum Zugreifen
auf die Objektdaten basieren in der Regel auf bereits vorhandenen Standardoperationen,
die z. B. von einem Datenbankmanagementsystem zur Verfügung gestellt werden. Diese
muss man also nicht erst selbst programmieren, was die Fehleranfälligkeit eines Pro-
gramms natürlich deutlich reduziert. Natürlich ist es auch möglich, Methoden selbst zu
schreiben, doch diese benutzen dann meistens wieder bereits vorhandene Standardme-
thoden und komponieren daraus die gewünschte Funktionalität.
Objekte, die in irgendeinem Sinne zusammengehörig sind, werden in sogenannte Klas-
sen zusammengefasst. Es muss also ein Kriterium geben, mit dem die Zusammengehö-
rigkeit von Objekten bestimmbar ist. In der 5. Schulklasse einer Grundschule beispiels-
weise sitzen Kinder (Objekte), denen gemeinsam ist, dass sie alle die 4. Klasse hinter sich
gebracht haben, einer bestimmten Altersgruppe angehören, einen bestimmten (Klassen-
)Lehrer haben, in einem bestimmten (Klassen-)Zimmer sitzen, einen bestimmten Stun-
denplan haben usw.; in der Mathematik kennt man u.a. Restklassen. Darunter versteht
man die Zusammenfassung von denjenigen ganzen Zahlen, die bei Division durch eine
festgelegte Zahl den gleichen Rest besitzen. Betrachtet man z. B. die Menge {..., 7, 11, 15,
19...}, so handelt es sich hierbei um die Klasse der ganzen Zahlen, welche bei Division
durch die Zahl 4 alle den gleichen Rest, nämlich 3, besitzen. Dieser Klassenbegriff führt
zur nächsten Definition.
SEI11 67
© HfB, 02.12.20, Berk, Eric (904709)
Klasse:
Unter einer Klasse versteht man eine Menge von zusammengehörigen Objekten. Eine
atomare Klasse ist eine Klasse mit nur einfachen (also nicht zusammengesetzten) Ob-
jekten.
Instanz:
Die Objekte einer Klasse werden auch Instanzen oder Exemplare der Klassen genannt.
Instanzen oder Exemplare einer Klasse können prinzipiell selbst wieder Klassen sein. Es
sei bemerkt, dass sich die Menge der Exemplare einer Klasse mit der Zeit verändern
kann. Daher sind eine Klasse und ihre Exemplarmenge i. A. nicht dasselbe.
UML-Diagramme
In einem Pflichtenheft können bereits grafische Elemente zur Datenmodellierung einge-
setzt werden. Ein Beispiel hatten wir schon gesehen, das Entity-Relationship-Diagramm
(ERD).
Seit geraumer Zeit hat sich allerdings die sogenannte Unified Modeling Language
(UML) etabliert. Dabei handelt es sich um Sammlung von Modellierungstechniken, die
sich durch eine Vielzahl von Diagrammtypen auszeichnet. Diese werden in nachfolgen-
den Studienheften ausführlich beschrieben und sind vor allem für den DV-Entwurf ein-
gesetzt. Allerdings setzen sich mehr und mehr auch im Pflichtenheft einige rudimentäre
UML-Ansätze durch. Wichtig ist dabei immer, dass auch der Kunde des Pflichtenhefts
die Ausführungen verstehen kann. Sollte er dazu nicht in der Lage sein, sollte man diese
Techniken nicht einsetzen und stattdessen die Datenstrukturen und Funktionsabläufe
verbal so präzise wie möglich beschreiben. Allerdings sollten auch die Diagramme,
wenn sie denn verwendet werden, noch einmal durch Text erläutert werden.
Klassendiagramme:
Diese Modellierungstechnik wird zur Beschreibung statischer Sachverhalte benutzt (wie
auch schon das ERD). Im Gegensatz zum Standard-ERD sind bei UML-Klassendiagram-
men weitergehende Sachverhalte darstellbar (z. B. Vererbungen, Aggregationen, siehe
nachfolgend).
Wir hatten definiert, dass Klassen eine Zusammenfassung zusammengehöriger Objekte
darstellen8. Die Zusammengehörigkeit wird dabei über Eigenschaften, sogenannte Inte-
gritätsbedingungen, definiert. Eine Schulklasse einer Grundschule besteht beispielswei-
se aus den Objekten „Schüler“, die alle gemeinsame Eigenschaften besitzen: Sie sind
(meistens) ungefähr gleich alt, gehören zum gleichen Einzugsgebiet einer Stadt, haben
bereits die vorhergehende Klasse erfolgreich absolviert (Versetzung) und so weiter. Von
einer gewissen Abstraktionsstufe aus gesehen sind diese „Objekte“ (bezüglich ihrer In-
tegritätsbedingungen) sogar „ununterscheidbar“. Jedes Objekt stellt dabei einen Reprä-
sentanten der ganzen Klasse dar. Objekte werden auch Instanzen oder Exemplare einer
Klasse genannt.
68 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Klassen werden in UML durch Rechtecke dargestellt, die wieder in 3 Abteilungen unter-
teilt sind: Oben steht der Klassenname, in der Mitte stehen die Attribute und im unteren
Teil die Methoden, mit denen auf die Objekte der Klasse zugegriffen werden kann. Au-
ßer dem Klassennamen sind alle anderen Einträge optional. Die Beziehungen zwischen
Klassen werden durch Verbindungslinien ausgedrückt, die auch beschriftet werden kön-
nen („Rolle“ der Beziehung).
In Abb. 4.9 sehen wir ein Beispiel für ein solches Klassendiagramm und in Abb. 4.10 für
ein Objektdiagramm. Objektdiagramme sehen ähnlich wie Klassendiagramme aus, je-
doch beziehen sich die Beschriftungen innerhalb der Rechtecke und der Verbindungsli-
nien jetzt auf konkrete Objekte der Klassen (je nach Literatur wird manchmal auch im
Objektnamen zuerst das Objekt, dann der Doppelpunkt und dann der Klassenname ge-
nannt).
An den Verbindungslinien werden bei dem Beziehungstyp „Assoziation“ noch die soge-
nannten Kardinalitätsbeschränkungen mit angegeben. Dabei handelt es sich um die Ma-
ximalzahlen, mit denen ein konkretes Objekt einer Klasse mit Objekten einer weiteren
(oder sogar der gleichen) Klasse eine Beziehung (Link) eingehen kann (siehe auch weiter
unten den Begriff der Rolle). Die wichtigsten sind:
* für kein oder viele
1..* für ein oder viele (auch lesbar als: mindestens 1, maximal viele)
0..1 für kein oder ein (auch lesbar als: mindestens 0, maximal 1)
1 für genau ein (diese 1 kann auch ganz weggelassen werden)
2..4 für numerische Angaben, hier: 2, 3 oder 4.
Eine Assoziation wird als eine Gruppe von Links definiert, wobei ein Link eine Verbin-
dung zwischen einander zugeordneten Objekten bezeichnet (bei 2 an einer Assoziation
beteiligten Klassen werden dann auf Instanzenebene jeweils immer genau 2 Objekte ei-
nander zugeordnet). Auf Instanzenebene werden daher im Objektdiagramm die Kardi-
nalitäten aller Links immer genau 1 betragen, und diese 1 wird in der Regel in den Dia-
grammen weggelassen.
Man kann sich eine Klasse häufig als eine Tabelle vorstellen; die Attribute der Klassen
stellen dann die Spalten einer solchen Tabelle dar, die Zeilen der Tabelle entsprechen den
Objekten. Die Assoziationen zwischen den Klassen werden in Tabellen in der Regel so
implementiert, dass man die entsprechenden sogenannten Schlüsselattribute einer Ta-
belle (das sind diejenigen Attribute, die einen Datensatz, d. h. also eine Zeile in der Ta-
belle, eindeutig identifizieren und deren Anzahl minimal ist, z. B. einfach eine fortlau-
fende Nummer, „ID“ für „Identität“) als Spalte in der anderen Tabelle einfügt (bei Eins-
SEI11 69
© HfB, 02.12.20, Berk, Eric (904709)
70 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Der Grund dafür ist folgender: Es kann Attribute geben, wie z. B. „Tätigkeit“ in
Abb. 4.11, die weder in die Klasse „Mitarbeiter“ aufgenommen werden können (da die
Tätigkeit eines bestimmten Mitarbeiters in verschiedenen Projekten jeweils eine andere
sein kann) noch der Klasse „Projekt“ zugeordnet werden können (da im gleichen Projekt
verschiedene Mitarbeiter verschiedene Tätigkeiten ausüben können). Deswegen bezieht
sich das Attribut „Tätigkeit“ nur auf den konkreten Link. Die Klasse um diese Link-At-
tribute „herum“ ist dann die Assoziationsklasse.
Ich möchte an dieser Stelle einem weitverbreitenden Irrtum entgegentreten, und zwar in
Bezug auf die Beschriftung der Kardinalitäten in Zusammenhang mit den Rollen. Be-
trachten wir zu diesem Zweck folgendes Beispiel: Eine Schallplattenfirma schließt einen
Vertrag mit einem Künstler ab. In der Musikbranche ist es dabei üblich, dass ein Künstler
immer exklusiv an eine (Schall-) Plattenfirma gebunden ist, während eine Plattenfirma
viele Künstler unter Vertrag haben kann. In Abb. 4.12 sehen wir dazu ein mögliches
Klassendiagramm:
Sicht 1 (Rolle):
Es wird an der Klasse aufgezählt, wie oft ein Exemplar dieser Klasse an einem Link
mit einem Exemplar der gegenüberliegenden Klasse teilnehmen kann.
Nun gibt es aber verwirrenderweise eine zweite Sichtweise, die zwar denselben Sach-
verhalt zum Ausdruck bringt, aber eine andere Sicht auf die Assoziation darstellt
(Abb. 4.13):
Wie man sieht, sind jetzt die Kardinalitätsbeschränkungen vertauscht. In dieser Sicht-
weise gilt:
SEI11 71
© HfB, 02.12.20, Berk, Eric (904709)
Die Sicht der inversen Rolle hat sich interessanterweise mehr verbreitet als die der Rolle.
Um Mehrdeutigkeiten zu vermeiden, kann es nicht schaden, wenn man bei der Erstel-
lung von Klassendiagrammen z. B. mithilfe einer Legende hinschreibt, welche Sicht man
gerade einnimmt.
Während Assoziationen „hat“-Beziehungen zwischen Klassen realisieren, stellen die so-
genannten Spezialisierungen (je nach Sichtweise auch Generalisierungen oder Verer-
bungen genannt) Variationen einer Klasse im Sinne einer „ist-ein“-Beziehung dar.
Abb. 4.14 liefert dazu ein Beispiel: Es gibt eine Klasse „Person“ mit den beiden Variati-
onen „Kunde“ und „Lieferant“. Die Verbindungslinien besitzen einen holen Pfeil am
Ende der Basisklasse (auch Oberklasse genannt); die Variationen werden auch abgelei-
tete Klassen, Subklassen oder Unterklassen genannt. Letztere ererben von der Basisklas-
se eventuelle Attribute, Methoden und Assoziationen. Optional kann in der Nähe des
Pfeils noch in geschweiften Klammern ein Eintrag vorgenommen werden, der Folgendes
bedeutet:
complete heißt, die Basisklasse enthält keine eigenen Instanzen
incomplete heißt, die Basisklasse kann eigene Instanzen haben
overlapping heißt, gleiche Objekte können gleichzeitig in mehreren Unterklassen
vorkommen
disjoint heißt, ein Objekt einer Subklasse kann nicht noch in einer weiteren
Subklasse vorkommen
72 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Konkret ist es also so, dass man Personen hat, die Kunde, Lieferant, keines von beiden
oder beides sein können. Hätte man statt {incomplete, overlapping} die Vererbungstypen
{complete, overlapping}, so würde das bedeuten, dass jede Person Kunde oder Lieferant
(oder beides) ist, d. h., es gibt keine Person, die nicht mindestens eines von beidem ist. In
diesem Fall nennt man die Basisklasse auch „abstrakt“, weil sie ja keine eigenen Instan-
zen besitzt (diese finden sich erst in den Subklassen). Würde im hinteren Teil statt „over-
lapping“ jetzt „disjoint“ stehen, so würde das bedeuten, dass ein Kunde nicht gleichzeitig
Lieferant sein kann und umgekehrt.
Noch ein Wort zu den drei „fast“ synonymen Begriffen Spezialisierung, Generalisierung
und Vererbung. Das Ergebnis ist in allen drei Fällen das gleiche, z. B. das Diagramm aus
Abb. 4.14. Der Unterschied besteht nur in der Sichtweise bzw. in der praktischen Vorge-
hensweise, also wie man zu dem Diagramm kommt. Beginnt man seine Systemanalyse
damit, dass man zuerst die Klasse Person modelliert und danach feststellt, dass es davon
zwei Variationen, nämlich Mitarbeiter und Lieferanten, gibt, so „spezialisiert“ man diese
Klasse in die beiden genannten Subklassen. Fängt man dagegen so an, dass man zuerst
die Klassen Mitarbeiter und Lieferant erstellt und dann feststellt, dass diese gleichlau-
tende Attribute haben (wie z. B. Name, Ort etc.), dann „zieht“ man diese gemeinsamen
Attribute „nach oben“ in eine eigens dafür neu einzuführende Klasse, die man dann z. B.
Person nennt. Dieser Vorgang wäre die Generalisierung. Betrachtet man jetzt aber, was
welche Klasse anderen Klassen zur Verfügung stellt („vererbt“), wie z. B. die Attribute
aus der Basisklasse den Subklassen, so nennt man den Mechanismus des „Zurverfügung-
stellens“ eine „Vererbung“. Vererbt werden können neben den Attributen auch die Me-
thoden und evtl. Assoziationen an die Basisklasse.
Im Beispiel aus Abb. 4.14 könnte die Klasse Person z. B. die Attribute Name, PLZ, Ort
haben. Diese werden dann an die Subklassen vererbt und werden daher dort nicht noch
einmal genannt (obwohl es sie dort gibt!). In den Subklassen werden nur noch diejenigen
Attribute genannt, die der jeweiligen Subklasse allein zu eigen sind.
Man muss aber aufpassen, wenn man aus solchen Gebilden wie in Abb. 4.14 Tabellen
machen will. Dann macht man zwar – wie üblich – auch aus jeder Klasse zunächst eine
Tabelle, doch die Vererbung muss zuvor in eine Assoziation „umgewandelt“ werden,
was möglich ist (allerdings unter Verlust der Vererbungstypisierung wie complete und
overlapping etc.). Um Vererbungen als Assoziationen darzustellen, muss man zwischen
der Basistabelle und jeder Subtabelle eine 1-zu-0..1-Assoziation herstellen. „Philoso-
phisch“ besteht aber dann ein deutlicher Unterschied: Während z. B. eine Instanz von
„Person“ mit der Variation „Kunde“ ein einziges Objekt liefert, entstehen bei der 1-zu-
0..1-Assoziation 2 Objekte (die über einen Link in Beziehung zueinander stehen). In Ta-
bellenform gibt es dann also keine abstrakten Klassen mehr, denn die Tabelle „Person“
hat dann natürlich konkrete Einträge in ihren Zeilen stehen, auf die in den beiden Sub-
tabellen Mitarbeiter und Lieferant passend referenziert wird.
Im Rahmen der Klassendiagramme spielt noch ein Spezialfall der Assoziation eine große
Rolle: die Aggregation. Aggregationen sind Assoziationen, von denen man weiß, dass
sie zusätzlich eine Gesamtheits-Teil-Beziehung darstellen (vgl. Abb. 4.15).
SEI11 73
© HfB, 02.12.20, Berk, Eric (904709)
74 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
SEI11 75
© HfB, 02.12.20, Berk, Eric (904709)
Die Objekte werden durch Rechtecke visualisiert. Von ihnen gehen die senkrechten Le-
benslinien aus, dargestellt durch gestrichelte Linien. Die Nachrichten werden durch
waagerechte Pfeile zwischen den Objektlebenslinien beschrieben.
Auf diesen Pfeilen werden die Nachrichtennamen in der Form
nachricht(argumente)
notiert. Nachrichten, die als Antworten deklariert sind, erhalten die Form
antwort: =nachricht(...).
Den Nachrichten können Bedingungen der Form
[bedingung] nachricht(...)
zugewiesen werden.
Iterationen von Nachrichten werden durch ein Sternchen „*“ vor dem Nachrichtenna-
men beschrieben. Objekte, die gerade aktiv an Interaktionen beteiligt sind, werden
durch einen Balken auf ihrer Lebenslinie gekennzeichnet. Objekte können während des
zeitlichen Ablaufs des begrenzten Kontextes erzeugt und gelöscht werden.
Ein Objekt wird erzeugt, indem ein Pfeil mit der Aufschrift neu() auf ein neues Objekt-
symbol trifft.
76 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
Aktivitätsdiagramme:
Aktivitätsdiagramme ähneln den Zustandsdiagrammen. Der Schwerpunkt liegt jedoch
im Verhalten des Systems und beschreibt hauptsächlich die Vernetzung elementarer Ak-
tionen. Dabei sind Kontroll- und Datenflüsse ablesbar. Oft wird damit der Ablauf eines
Anwendungsfalls beschrieben (wie schon bei U-Case-Diagrammen, es können sogar die
Strichmännchen von dort zum Einsatz kommen). Ein Aktivitätsdiagramm kann aber
auch zur Modellierung aller Aktivitäten eines Systems benutzt werden.
Nun gibt es zwei Ausprägungen des Aktivitätsdiagramms: In der aktuellen, neueren
Form von UML (UML 2 genannt) wird die Darstellung sogenannter nebenläufiger Sys-
teme durch die Einbindung von asynchronen Kommunikationsmechanismen (Signal
senden und empfangen, Ausnahmebehandlung) ermöglicht. Ein Aktivitätsdiagramm
spezifiziert also die eigentlichen Aktivitäten. Im Prinzip stellt ein Aktivitätsdiagramm
die objektorientierte Adaption eines Programmablaufplans dar. Ein Beispiel sehen wir
in Abb. 4.19.
SEI11 77
© HfB, 02.12.20, Berk, Eric (904709)
In Abb. 4.19 sehen wir ein einfaches Aktivitätsdiagramm mit einem Kopf- und einem
Inhaltsbereich. Am rechten und linken Rand sieht man zwei Rechtecke. Diese bezeichnet
man als „Aktivitätsparameterknoten“. Die Aktionen sind die Rechtecke mit abgerunde-
ten Ecken. Dort sind kleine Quadrate an den Rändern zu erkennen. Sie beschreiben die
Ein- und Ausgabe-Pins und deuten an, dass hier ein Objektfluss vorliegt. Der Rest stellt
Kontrollflüsse dar. Der schwarze Kreis ist der Startknoten und gehört damit zu den Kon-
trollknoten.
Aus Gründen der Historie sei noch ein Beispiel für die ältere UML-Variante der Aktivi-
tätsdiagramme (UML 1) angegeben (Abb. 4.20).
78 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
SEI11 79
© HfB, 02.12.20, Berk, Eric (904709)
4.2.3 Projektplan
Schließlich muss im Pflichtenheft noch eine Zeitplanung enthalten sein, die eine realis-
tische Entwicklungszeit wiedergibt. Je nach Komplexität des Projekts kann die Darstel-
lung unterschiedlich sein. Reicht eine tabellenförmige Aufstellung nicht aus, können
auch computergestützte Werkzeuge wie z. B. MS Project® benutzt werden. Ein Projekt-
plan muss auf jeden Fall enthalten, welche Personen zu welchem Zeitpunkt welche Tei-
laufgabe lösen. Dabei sollten Meilensteine eingebaut werden, die durch Reviews beim
Auftraggeber mit demselben „abgenommen“ werden. Durch eine solche Bestätigung des
Erreichens von Teilzielen durch den Auftraggeber erhöht sich die Sicherheit, dass die
Programmentwicklung auf dem richtigen Weg ist.
4.1 Welches sind die wichtigsten Daten bei der Planung eines Softwareprojekts?
4.2 Welche Punkte des Pflichtenhefts nach DIN 69901 sind Ihrer Meinung nach bei
kleineren Projekten nicht so wichtig?
4.3 Entwerfen Sie ein semantisches Datenmodell für den in Abb. 4.7 gezeigten Sach-
verhalt. Erfinden Sie einige Attribute zu den Entitäten und geben Sie eine verbale
Version des semantischen Datenmodells an.
80 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
SEI11 81
© HfB, 02.12.20, Berk, Eric (904709)
2.5 Da es sich hier um eine „Kreativ-Aufgabe“ handelt, kann hier keine feste Lösung
angegeben werden. Sie können aber gern Ihre Lösung an den zuständigen Tutor
leiten, dieser wird sie dann gern kommentieren. Jede „wohlbegründete“ Lösung
ist richtig!
3.1 Wie schon erwähnt bleibt die Bedienoberfläche letztlich Geschmackssache; al-
lerdings kann man objektiv feststellen, dass im SuperConverter die wichtigsten
Parameter für eine Formatwandlung auf „einen“ Blick sichtbar sind. Im oberen
Teil wählt man Ziel-Container und Ziel-Formate aus einer vorgegebenen Liste
aus und stellt dann die Parameter per Mausklick in den darunterliegenden Ab-
schnitten ein, die je nach Formatauswahl oben bereits die möglichen Einstellun-
gen angepasst anbieten. Dies ist sehr effektiv und vermeidet vor allem, hier Pa-
rameter auf Werte einzustellen, die das Zielformat gar nicht verarbeiten kann.
Insgesamt ist diese Oberfläche allerdings etwas gewöhnungsbedürftig.
Der MediaCoder hingegen kommt fast „klassisch“ daher: Es finden sich zur Aus-
wahl der Formate die üblichen Tabs, wo man neben den Formaten auch die je-
weiligen Parameter angeben kann. Dies geht aber auf Kosten der Übersicht, wes-
wegen ein eigenes kleines Fenster „Properties“ rechts oben eingeblendet wird,
wo ansatzweise die Parameter sichtbar sind. Allerdings kommt es hier oft zu
„Fehlläufen“, denn erst beim Start wird festgestellt, ob die ausgewählten Parame-
ter zu dem gewünschten Zielformat „passen“, was sehr ärgerlich sein kann. Es
erscheint auch nur der Hinweis, dass die Parameter so nicht zusammenpassen,
es wird aber kein Hinweis gegeben, welche Werte die Parameter annehmen dür-
fen. Dieses Problem stellt sich beim SuperConverter nicht, da hier nach Format-
auswahl nur die zulässigen Möglichkeiten eingeblendet werden.
Im Ergebnis sind beide Oberflächen softwareergonomisch, doch liefert der Su-
perConverter – hat man sich einmal an diese Oberfläche gewöhnt – den schnel-
leren und auch bedienerfreundlicheren Überblick
4.1 Es muss klar sein, welche Funktionen ein Programm ausführen soll. Dazu gehört
insbesondere, dass der Realitätsausschnitt, den das Programm später abdecken
soll, z. B. mit einem U-Case-Diagramm beschrieben wird. Die Entitäten in dem
U-Case-Diagramm sollten inhaltlich über die wichtigsten Attribute beschrieben
sein. Weiterhin sollen die Input-/Output-Inhalte klar sein sowie ein grobes Lö-
sungskonzept existieren, das angibt, wie vom Input zum Output gelangt werden
kann. Eine zumindest grobe Personal-Einsatzplanung darf auch nicht fehlen
(wer macht wann was).
4.2 Die Unternehmenscharakteristik spielt i. d. R. keine besondere Rolle. Auf ein
Mengengerüst kann oft auch verzichtet werden, da heute Datenbanksysteme
oft „von Hause aus“ mit Millionen von Datensätzen problemlos umgehen kön-
nen.
82 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
4.3 Ein Unternehmen ist gekennzeichnet durch einen Namen und einen Sitz (Straße,
PLZ, Postfach, PLZ-Ort, Straßen-Ort, Telefon, Fax, E-Mail). Es sollen mehrere
Unternehmen erfasst werden können. Jedes Unternehmen hat mehrere Abtei-
lungen. Jede Abteilung hat einen Namen, ein Abteilungskürzel sowie eine Tätig-
keitsbeschreibung. Jede Abteilung umfasst mehrere Mitarbeiter. Ein Mitarbeiter
ist gekennzeichnet durch seinen Namen und eine Personalnummer, sein Ge-
burtsdatum, seinen Wohnort (Straße, PLZ, Ort, Tel.-Nummer, Fax, E-Mail) sowie
das Datum seines Eintritts in die Firma. Ein oder mehrere Mitarbeiter betreuen
ggf. ein oder mehrere Projekte. Jedes Projekt besitzt einen Namen, ein Projekt-
kürzel, eine Projektbeschreibung, ein Startdatum sowie ein geplantes Enddatum.
SEI11 83
© HfB, 02.12.20, Berk, Eric (904709)
B. Literaturverzeichnis
Allgemein:
Greenfield, J. und Short, K. (2006). Software Factories.
Heidelberg: Redline GmbH.
König, W. u. a. (1999). Taschenbuch der Wirtschaftsinformatik und Wirtschaftsmathe-
matik. Frankfurt am Main: Harri Deutsch.
Nielsen, J. (2000). Erfolg des Einfachen. Markt+Technik.
Rumbaugh, J. u. a. (1991). Object Oriented Modeling and Design.
Englewood Cliffs, Prentice Hall Inc.
Zöller-Greer, P. (2002). Softwareengineering für Ingenieure und Informatiker.
Wiesbaden: Vieweg.
Zöller-Greer, P. (2010). Multi Media Systeme.
Wächtersbach: Composia.
Zöller-Greer, P. (2010). Software Architekturen-Grundlagen und Anwendungen.
Wächtersbach: Composia.
Zum Testen:
Meyers, G. J. (1979). The Art of Software Testing.
New York: John Wiley & Sons.
Realzeitsysteme:
Gomaa, H. (1999). Software Design Methods for Concurrent and Real-Time-Systems.
Addison-Wesley.
Software-Ergonomie:
Herczeg, M. (2009). Theorien, Modelle und Kriterien für gebrauchstaugliche interaktive
Computersysteme.
Oldenbourg, München.
84 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
C. Abbildungsverzeichnis
SEI11
Sachwortverzeichnis
SEI11 85
© HfB, 02.12.20, Berk, Eric (904709)
C Abbildungsverzeichnis
86 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
D. Sachwortverzeichnis
SEI11
A F
Agile Methode ................................. 32 Fachkonzept ............................... 12, 13
Agile Modeling ................................ 30 Feinkonzept ............................... 16, 49
Agile Processing .............................. 30
Agile Programming ......................... 30 G
Agile Softwareentwicklung .............. 30 Grobkonzept .............................. 13, 49
Agiler Prozess .................................. 32
Agiles Prinzip .................................. 31 I
Anforderungsanalyse ....................... 13 Implementierung ............................. 16
Assoziationstyp ............................... 64 Initialisierung .................................... 9
Attribut ...................................... 64, 65 Ist-/Sollzustände .............................. 51
Istzustand ........................................ 51
B
Benutzertest ..................................... 17 M
Beziehung ........................................ 65 Multimediaprojekt ........................... 10
Murphys Gesetz ................................. 4
C
CASE ................................................ 6 P
CMS ................................................ 43 Pflichtenheft ................................ 3, 49
Content-Management-System .......... 43 Problemanalyse ......................... 10, 47
Programmtest .................................. 17
D Projektplan ...................................... 80
Data Dictionary ............................... 63 Prototyp .......................................... 19
Datenmodell, semantisches .............. 13 Prototyping ...................................... 20
Datenmodellierung, semantische . 13, 60
Dialoggestaltung .............................. 37 R
Domain ........................................... 64 Relation ........................................... 65
DV-Entwurf ..................................... 16 Relationship ..................................... 64
DV-Konzept ..................................... 12 Relationship set ................................ 64
RUP ................................................. 27
E
E-Commerce .................................... 10 S
Entität ............................................. 65 Software Engineering ......................... 6
Entity .............................................. 64 Softwareergonomie .......................... 34
Entity Relationship Diagram ............ 64 System mit geplanter Reaktion ........... 5
Entity set ......................................... 64 Systemanalyse ............................. 6, 13
Entwicklungszyklus ........................... 3 Systemanalytiker ............................... 3
Extreme Programming ..................... 30
SEI11 87
© HfB, 02.12.20, Berk, Eric (904709)
D Sachwortverzeichnis
T
Test .................................................. 17
U
U-Case-Diagramm ........................... 51
Unified Process ................................ 27
UP ................................................... 27
Usability Engineer ........................... 35
Use-Case-Diagramm ........................ 51
V
V-Modell XT .................................... 21
W
Wartung .......................................... 18
Wasserfallmodell ......................... 9, 18
Wertebereich ................................... 64
88 SEI11
© HfB, 02.12.20, Berk, Eric (904709)
E. Einsendeaufgabe
Phasenmodelle und Planung von Softwareprojekten Einsendeaufgabencode:
SEI11-XX2-K07
SEI11 89
© HfB, 02.12.20, Berk, Eric (904709)