Schnelleinstieg: Funktionen in C/C++
C++ Funktionen sind kleine Unterprogramme, die Teilprobleme lösen. Funktionen sind ein wichtiges Werkzeug, um den Quelltext eines Programms zu ordnen und wesentliche Algorithmen und zusammenhängende Anweisungsblöcke der main()-Hauptfunktion in einer zusammenhängenden Form auszulagern. Sie werden außerhalb der main()-Hauptfunktion definiert und vereinfachen somit das Verständnis und die Lesbarkeit des Quelltextes.
Deklaration und Definition von Funktionen
Im Grunde bedeutet die Definition einer Funktion nichts Anderes, als eine Code-Block (Anweisungsblock) mit einem Funktionsnamen zu verbinden. Die Definition einer C++ Funktion besteht aus einer Deklaration und einem Anweisungsblock.
Für die Deklaration einer C++ Funktion sind gewisse Angaben erforderlich und gewisse zusätzliche Spezifikationen möglich (z.B. ‚inline‚ oder ‚virtual‚). Um eine Funktion schließlich zu definieren, muss man zusätzlich noch die zu erledigenden Anweisungen in einem Anweisungsblock zusammenfassen. Die allgemeine Definition einer C++ Funktion ist der formalen Definition einer mathematischen Funktion nicht unähnlich und man könnte sie auch wie folgt definieren: „Eine C++ Funktion ist eine Abbildung von dem Datenraum der Argumentenliste in den Datenraum des Rückgabetyps. Die dabei benutzte Abbildungsvorschrift findet sich in ihrem Anweisungsblock. Formal besitzt sie somit die folgende Struktur:“
'Rückgabe Typ' Funktionsname ('Argumentenliste') { 'Block von Anweisungen' }
- Der Name der Funktion:
Hier sollte der Programmierer einen Namen wählen, der die im Anweisungsblock definierten Anweisungen präzise und kurz in einem Wort zusammen fasst. - Rückgabe Typ:
Welchen Datentyp gibt die Funktion an das Hauptprogramm zurück? Der „Rückgabe Typ“ steht vor dem Funktionsnamen. - Argumentenliste:
Die „Argumentenliste“ steht direkt hinter dem Funktionsnamen, im Aufrufoperator „(…)“. Sie spezifiziert die Datentypen der Variablen, die die Funktion zur Berechnung ihrer Aufgabe benötigt.
Der ‚Rückgabe Typ‘ kann hierbei einer der C++ Datentypen (z.B. int oder double) oder ein Daten-Array sein. Falls der ‚Rückgabe Typ‘ als void gekennzeichnet wird, gibt die Funktion keine Daten an das Hauptprogramm zurück, und führt nur den ‚Block von Anweisungen‘ aus. Die ‚Argumentenliste‘ setzt sich aus einer Liste von Datentypen der formalen Argumente (Parameter) der Funktion zusammen, die jeweils mit einem Komma voneinander getrennt sind.
Soviel zur Theorie – werden wir praktisch:
Funktionen erfüllen also den Zweck, wiederkehrende Aufgaben auszulagern, anstatt jedes Mal den gleichen Code nochmal einzutippen, wenn eine bestimmte Aufgabe ansteht.
Programme werden Zeile für Zeile ausgeführt, in der Reihenfolge, in der du den Quellcode aufgesetzt hast. Bei einem Funktionsaufruf verzweigt das Programm, um die Funktion auszuführen. Ist die Funktion beendet, springt die Programmausführung zurück zu der Zeile in der aufrufenden Funktion, die auf den Funktionsaufruf folgt. Funktionen dürfen andere Funktionen – auch sich selbst (Rekursion) – aufrufen.
Der Funktion main()
kommt dabei eine Sonderstellung zu. Normalerweise muss eine Funktion, um etwas leisten zu können, im Verlauf Ihres Programms durch main() oder eine andere Funktion aufgerufen werden. main()
ist die Hauptfunktion eines jeden Programms und wird beim Start des Programms vom Betriebssystem aufgerufen. Sie steuert den eigentlichen Ablauf des Programms. Die Hauptfunktion darf sich nicht selbst aufrufen.
Im realen Leben könnte das so aussehen: Stellen wir uns vor, dass du ein Bild von dir selbst zeichnest. Du zeichnest deinen Kopf, die Augen, die Nase – und plötzlich bricht dein Bleistift ab. Du »verzweigst« nun in die Funktion »Bleistift spitzen«. Das heißt, du hörst mit dem Zeichnen auf, stehst auf, gehst zur Spitzmaschine, spitzt den Stift, kehrst an deine Arbeit zurück und setzt sie dort fort, wo du aufgehört hast. Wenn ein Programm eine bestimmte Arbeit verrichtet haben möchte, kann es dafür eine Funktion aufrufen und nach Abarbeitung der Funktion genau an dem Punkt weitermachen, wo es aufgehört hat. Das Listing „demofunction.cpp“ verdeutlicht dieses Konzept:
// Listing: demofunction.cpp #include <iostream> // Namensraum für cout: using namespace std; // Funktion DemoFunction() // - gibt eine Meldung aus und gibt danach // - die Kontrolle zurück an den Aufrufer: void DemoFunction(void) { cout << "In DemoFunction\n"; } // Funktion main - gibt eine Meldung aus, ruft // dann DemoFunction auf, gibt danach // eine zweite Meldung aus: int main() { cout << "In main\n" ; DemoFunction(); // Verzweigung zur Funktion 'DemoFunction()' // Wieder in main() nach Abarbeitung der Funktion: cout << "Zurueck in main\n"; return 0; }
Hinweis: Bei der Funktion DemoFunction() handelt es sich um eine Funktion ohne Rückgabewert (Datentyp für die Rückgabe: void).
Ausgabe:
./demofunction In main In DemoFunction Zurueck in main
Programmanalyse:
- Zeile 2 inkludiert IOSTREAM. Das ist eine Header-Datei, die Teil der Standardbibliothek von C++ ist. Diese Datei enthält Definitionen für die Ein- und Ausgabestreams, wie cin, cout, cerr und clog. Diese Streams sind Instanzen von Klassen wie istream und ostream, die wiederum von der Basisklasse ios abgeleitet sind.
- Zeile 5 legt den Namensraum std fest und erspart dir damit Tipparbeit (vgl. std::cout).
- Zeile 9 ist der Funktionskopf der Funktion DemoFunction().
- Die Zeilen 10 und 12 beginnen und beenden den Anweisungsblock (Funktionsrumpf) der Funktion.
- Zeile 11 sorgt für die Ausgabe der Meldung ‚In DemoFunktion‘, wenn die Funktion aufgerufen wird. Danach wird die Funktion ohne Rückgabewert beendet und die Programmkontrolle wieder an den Aufrufer (in diesem Fall: Die main()-Funktion) zurück gegeben.
- Die Hauptfunktion main() beginnt in Zeile 17 (Funktionskopf). Ihr Anweisungsblock beginnt in Zeile 18 und endet in Zeile 26.
- Zeile 19 gibt die Meldung ‚In main‘ und einen Zeilenvorschub aus.
- Zeile 20 ruft die Funktion ‚DemoFunktion()‘ ohne Parameter auf und übergibt ihr die Programmkontrolle, bis die Funktion abgearbeitet ist.
- Zeile 23 gibt die Meldung ‚Zurueck in main‘, gefolgt von einem Zeilenvorschub aus. Zu diesem Zeitpunkt liegt die Programmkontrolle bereits wieder bei main().
- Zeile 25 gibt den Integer-Wert ‚0‘ (Null) an den Aufrufer (Das ist das Betriebssystem, bzw. die Konsole, in welcher das Programm gestartet wurde) zurück. Das Programm ist damit beendet.
Funktionen sind Arbeitstiere. Um ihren Aufgaben gerecht zu werden, können sie Daten (z. B. das Ergebnis einer Berechnung) über ihren Rückgabetyp an den Aufrufer zurückgeben. Um flexible Ergebnisse liefern zu können, verfügt eine Funktion über eine Parameterliste, mit der ihr Werte übergeben werden können. Soll eine Funktion zum Beispiel zwei Zahlen addieren, stellen die Zahlen die Parameter für die Funktion dar. Ein typischer Funktionskopf sieht folgendermaßen aus:
int summe (int a, int b)
Das Listing „addition.cpp“ zeigt eine Funktion, die zwei ganzzahlige Parameter übernimmt und einen ganzzahligen Wert zurückgibt. Kümmere dich momentan nicht um die Syntax oder die Einzelheiten, wie man mit Integer-Werten (beispielsweise int x
) arbeitet. Wir kommen darauf zurück.
// Listing: addition.cpp #include <iostream> using namespace std; // Funktion: int Add(int x, int y) // - addiert zwei an die Funktion übergebene // - Zahlen und gibt das Ergebnis an den // - Aufrufer zurück: int Add(int x, int y) { cout << "\nFunktion 'Add()', erhalten: " << x << ", " << y << "\n"; return(x + y); // Addieren und zurückgeben } // --- Haupt-Funktion -- int main(void) { int summand1, summand2, summe; cout << "Zahlen addieren:\n"; cout << "================\n"; cout << "Ich bin in 'main()'!\n"; cout << "Gib die erste Zahl ein: "; cin >> summand1; cout << "Gib die zweite Zahl ein: "; cin >> summand2; // Berechnung: // Der Variablen 'summe' wird der Rückgabewert // der Funktion 'Add()' zugewiesen: summe = Add(summand1, summand2); // Funktionsaufruf und Wertzuweisung // Ausgabe des Ergebnisses: cout << "\nZurueck in 'main()'!\n"; cout << summand1 << " + " << summand2 << " = " << summe << endl; cout << "\nProgramm beendet.\n"; return 0; }
Hinweis: Bei der Funktion ‚int Add(int x, int y)‘ handelt es sich um eine Funktion mit Rückgabewert und Parameterliste.
Compiliere das Programm (<F5> in Geany oder Konsole):
g++ -Wall -s addition.cpp -o addition
Starte es anschließend und gib nach Aufforderung zwei Ganzzahlen ein (z. B. 4700 und 11).
Ausgabe:
./addition Zahlen addieren: ================ Ich bin in 'main()'! Gib die erste Zahl ein: 4700 Gib die zweite Zahl ein: 11 Funktion 'Add()', erhalten: 4700, 11 Zurueck in 'main()'! 4700 + 11 = 4711 Programm beendet.
Programmanalyse:
- Die Zeilen 10 bis 14 definieren die Funktion ‚int Add(int x, int y)‘. Beachte: Dies geschieht vor der Definition von ‚main()‘!
- Zeile 12 sorgt dafür, dass beim Aufruf von Add() eine Meldung ausgegeben wird, die auch die beiden übergebenen Parameter ausgibt.
- Die Hauptfunktion main() erstreckt sich über die Zeilen 17 bis 40.
- In Zeile 19 deklarieren wir drei Variablen vom Typ Ganzzahl (int). Sie dienen später der Aufnahme der beiden Summanden und der berechneten Summe.
- Zeile 24 fordert zur Eingabe einer Zahl auf.
- Zeile 25 liest diese Zahl mit dem Eingabestream cin ein. Dieses Schlüsselwort steht für „Console Input“ und wird vom Eingabeoperator „>>“ (dem Gegenstück zum Ausgabeoperator „<<„) gefolgt. Er verweist auf die Variable summand1, die die eingegebenen Daten (die erste Ganzzahl) aufnehmen soll.
- In den Zeilen 26 und 27 wiederholt sich dieser Vorgang der Dateneingabe für die Variable summand2.
- Zeile 32 – Hier wird die eigentliche Arbeit geleistet: Der Variablen summe wird das Ergebnis der Berechnung, also der Rückgabewert der Funktion Add() zugewiesen. Hierzu ruft die Zuweisung die Funktion Add() mit den Parametern (summand1, summand2) auf. Überraschung! Hatten wir nicht die Parameterlist von Add() als (int x, int y) definert? Doch, hatten wir – und das ist auch korrekt. Die Parameter x und y sind lokale, nur innerhalb der Funktion selbst sichtbare Variablen. Der Aufruf von außerhalb kann mit jeder passenden Variablen, gleich welchen Namens, erfolgen. Intern arbeitet die Funktion allerdings mit den Variablen x, y. Das kann man gut an den Zeilen 12 und 13 erkennen.
- Zeile 36 gibt beide eingegebenen Zahlen und ihre Summe in der Form aus, wie du die Berechnung auch auf ein Blatt Papier schreiben würdest:
4700 + 11 = 4711 - Zeile 39 beendet das Hauptprogramm mit dem Rückgabewert Null.
Funktionsprototypen
Bisher haben wir eigene Funktionen immer vor der Hauptfunktion main() definiert. Das ist dem Umstand geschuldet, dass in C/C++ eine Variable oder Funktion immer vor ihrer ersten Verwendung deklariert sein muss. Es geht aber auch anders, wenn man dem Compiler eine Funktion durch ihren Funktionsprototypen rechtzeitig – also vor main() – bekannt macht. Dabei entspricht der Funktionsprototyp genau dem Funktionskopf, gefolgt von einem Semikolon:
// Listing: addition2.cpp #include <iostream> using namespace std; // Funktionsprototyp: int Add(int x, int y); // -- Hauptprogramm -- int main(void) { int summand1, summand2; cout << "Zahlen addieren:\n"; cout << "================\n"; cout << "Ich bin in 'main()'!\n"; cout << "Gib die erste Zahl ein: "; cin >> summand1; cout << "Gib die zweite Zahl ein: "; cin >> summand2; // Berechnung und Ausgabe: cout << summand1 << " + " << summand2 << " = " << Add(summand1, summand2) << endl; return 0; } // -- Funktion: int Add(int x, int y) -- int Add(int x, int y) { return(x + y); // Addieren und zurückgeben }
Beachte: Im Listing „addition2.cpp“ erfolgt die Definition der Funktion „Add()“ erst nach der Hauptfunktion main(). Um die Funktion trotzdem bereitstellen zu können, wurde in Zeile 7 der Funktionsprototyp entsprechend dem Funktionskopf von Add() deklariert. Auch erfolgt die Berechnung nicht mehr mit Zwischenspeicherung des Ergebnisses in der Variablen summe – diese wurde eingespart. Stattdessen übergeben wir in Zeile 23 den Rückgabewert von Add() direkt an den Ausgabestream.
Die Verwendung von Prototypen hat übrigens den Vorteil, dass der Compiler die korrekte Verwendung einer Funktion bereits frühzeitig überprüfen und auf etwaige Regelverstöße genauer reagieren kann. Gute C/C++-Programme beinhalten deshalb einen Abschnitt, in dem alle Prototypen für Funktionen vor ihrer ersten Verwendung deklariert werden.
Zusammenfassung:
Funktionen geben entweder einen Wert oder void
(das heißt: nichts) zurück. Eine Funktion zur Addition von zwei ganzen Zahlen liefert sicherlich die Summe zurück, und man definiert diesen Rückgabewert vom Typ Integer (int). Eine Funktion, die lediglich eine Meldung ausgibt, hat nichts zurückzugeben und wird daher als void
(zu deutsch: leer) deklariert.
Funktionen gliedern sich in Kopf und Rumpf. Der Kopf besteht wiederum aus dem Rückgabetyp, dem Funktionsnamen und den Parametern. Mit Parametern lassen sich Werte an eine Funktion übergeben.
Funktionen müssen (wie Variablen) vor ihrer ersten Verwendung dem Compiler bekannt gemacht werden. Aus diesem Grund definiert man sie entweder vor main(), oder aber man verwendet stattdessen einen entsprechenden Funktionsprototypen an ihrer Stelle.
[Inhaltsverzeichnis] | [zurück] | [vorwärts]