In the town where I was born,
Lived a man, who played with 'C'.
And he coded his whole life
On a stack of Function Keys.
Programme arbeiten mit Ganzzahlen, Zeichen (engl. character, in C: char
),
Zeichenfolgen (engl. character strings, in C++ std::string
)
und gebrochenen Zahlen (engl. floating point numbers, in C float
).
Alles, was die Programme mit den Daten anstellen,
kann (muss) in eine (zeitliche) Abfolge von Anweisungen gebracht werden.
Anweisungen, die evtl. mehrfach ausgeführt werden müssen,
sind in Wiederholanweisungen (Schleifen while(…) {…}
) eingebunden.
Für Programmteile, die nur unter bestimmten Bedingungen abzuarbeiten sind,
gibt es Auswahlanweisungen (Entscheidungen if(…) {…}
).
Im Prinzip lässt sich jedes Programm so aufbauen,
dass ausschließlich Anweisungsfolgen, Entscheidungen und Wiederholungen benutzt werden.
Ein solches Programm ist komplett als Struktogramm darstellbar.
Das wäre alles, was man übers Programmieren wissen muss.
Der Rest ist eine Fleißaufgabe.
Schluss.
Ende.
Jetzt kommt nichts mehr.
Nicht weiterlesen!
In der Praxis sind dem oben skizzierten "strukturierten Programmieren" Grenzen gesetzt. Irgendwann reicht ein Blatt Papier, eine Tapetenrolle, eine Wand nicht mehr aus, alles zu fassen, egal wie klein man schreibt. Ärgerlich ist, dass einige Programmteile, mit kleinen Variationen, immer wieder auftauchen. Spätestens dann sollte man beginnen, diese Teile aus dem Programmblock herauszulösen. Die Stelle im Programm, an dieser Block ausgeführt werden soll, wird durch einen eindeutigen Namen markiert.
Damit ist die Idee des Unterprogramms (Prozedur, engl. subroutine) geboren. Manchmal werden Datenwerte an die Prozedur als Anfangswerte oder Eingabedaten übergeben. Andere Werte können als Ergebnis der "Nebenrechnung" ins Hauptprogramm zurückgebracht werden. In der Mathematik wird jede eindeutige Zuordnung von Eingabedaten zu bestimmten Ergebniswerten Funktion genannt. Die von C abstammenden Programmiersprachen nennen jedes Unterprogramm Funktion, auch dann, wenn es keine Daten übernimmt, keine Daten zurückgibt, sogar dann, wenn bei denselben Eingaben nicht wieder dasselbe Ergebnis entsteht.
Unterprogramme, oder Funktionen, erweitern unsere sprachlichen Möglichkeiten enorm:
Abstrahieren, Zerlegen, Modularisieren, Hierarchisieren versetzen uns schließlich in die Lage, erstaunlich komplexe Programme mit Tausenden Quelltextzeilen zu bauen, die (einigermaßen) korrekt funktionieren.
Hier geht offenbar das Programmieren erst so richtig los. Wir werden uns schrittweise steigern. Ich bleibe beim Parkplatz-Beispiel, um die neuen Techniken zu demonstrieren.
Im Parkplatz-Problem haben wir es mit zwei Größen zu tun, die von zwei anderen Größen abhängen.
Im mathematischen Sinne sind autos(fahrzeuge, reifen)
und mopeds(fahrzeuge, reifen)
Funktionen.
In C und verwandten Programmiersprachen lässt sich dieser Zusammenhang verkünden,
ohne dass zunächst erklärt werden muss, worin dieser Zusammenhang besteht.
Die Funktionen werden zunächst nur angekündigt, deklariert.
Die ganzzahligen Größen fahrzeuge
und reifen
stellen
Eingangsgrößen für die noch nicht definierte Berechnung dar.
Das Ergebnis der Berechung ist wieder ganzzahlig.
//: parkplatz5.cpp : Autos und Zweiraeder - R.Richter 2007-01-02 //////////////////////////////////////////////////////////////// #include <iostream> int autos (int fahrzeuge, int reifen); int mopeds(int fahrzeuge, int reifen);
Die Deklaration liefert dem Compiler ausreichend Information, um zunächst das Hauptprogramm zu formulieren. Wir lassen die Überprüfung der Eingabewerte zwischenzeitlich wieder weg, um besser sehen zu können, wie Funktionswerte berechnet werden. Die Funktionen werden bei ihren Namen genannt, unter Angabe passender Argumente (Eingabewerte). Dieser "Aufruf" bewirkt das Ausführen der Nebenrechnung während des Programmlaufs. Jede Funktion erzeugt ein Ergebnis. Hier ist es eine Ganzzahl, die hier sofort an die Ausgabekonsole weitergereicht wird.
int main() { int fahrzeuge, reifen; std::cout << "Fahrzeuge: "; std::cin >> fahrzeuge; std::cout << "Reifen: "; std::cin >> reifen; std::cout << "Autos: " << autos (fahrzeuge, reifen) << '\n'; std::cout << "Motorraeder: " << mopeds(fahrzeuge, reifen) << '\n'; return 0; }
Wie die Funktionen die Ergebnisse berechnen, kann an anderer Stelle im Quelltext festgelegt, definiert werden. Jedem Funktionskopf wird ein Funktionsrumpf zugeordnet, der die auszuführenden Anweisungen auflistet:
int autos (int fahrzeuge, int reifen) { return fahrzeuge - mopeds(fahrzeuge, reifen); } int mopeds(int fahrzeuge, int reifen) { return (4*fahrzeuge - reifen) / 2; }
Nachdem die Funktionen deklariert wurden,
spielt es keine Rolle mehr, welche Funktion zuerst definiert wird.
Die Aufrufreihenfolge ist durch die Anweisungen im Hauptprogramm und den anderen Funktionen festgelegt.
Innerhalb einer Nebenrechnung kann wiederum eine Nebenrechnung notwendig werden.
Ist eine Nebenrechnung beendet, wird das Ergebnis an den Aufrufer zurückgegeben
(return
-Anweisung).
Der Ergebniswert der Nebenrechnung wird vom Aufrufer an die Stelle des Funktionsaufrufs gesetzt.
Versetze dich in die Lage eines menschlichen Computers, der im Hauptprogramm zwei Zahlen entgegen nimmt. Führe die Berechnungen handschriftlich aus, wenn möglich, für jede Nebenrechnung einen neuen Schmierzettel. Ist die Nebenrechnung beendet, markiere das Ergebnis (2x unterstreichen!), übertrage das Ergebnis auf das Blatt, von dem aus die Nebenrechnung notwendig war: Welches war das? Wirf das Blatt mit der erledigten Nebenrechnung weg, ins Kaminfeuer oder zerreiße es. Wird die Nebenrechnung nochmal mit denselben Werten notwendig, ist alles nochmal zu berechnen. Das Programm hat keine Erinnerung. Jetzt solltest du eine grobe Vorstellung davon haben, wie die Maschine mit den Unterprogrammen umgeht.
Für die Funktionen wiederum ist unerheblich, wo, wie oft und mit welchen Variablennamen die Aufrufe erfolgen. Die Namen der Funktionsparameter ließen sich nach Belieben verändern. Die "sprechenden" Namen werden aber so belassen, zum Zwecke der Dokumentation.
Im nächsten Schritt werden Deklaration, Definition und Aufruf der Funktionen in drei getrennte Dateien verlagert.
Weiter: Teil 8.