Längere Programme enthalten in der Regel Programmteile, die sich mehr oder weniger häufig wiederholen. Diese Teile will man gern an anderer Stelle ausführlich definieren, und im Programm nur durch einen ``Platzhalter'', also einen Funktionsaufruf ersetzen. Ein Beispiel ist das Einholen einer Bestätigung durch den Benutzer, ob eine bestimmte numerische Aufgabe das Löschen oder das Umsortieren von Daten ausgeführt werden soll. Weiterhin möchte man häufig aus Gründen der Übersicht bestimmte Programmteile gruppieren und wenn möglich, die dazugehörigen Anweisungen nur noch durch einen Namen kennzeichnen, der ihre ``Funktion'' charakterisiert.
Das zu diesem Zweck existierende Sprachmittel sind die Funktionen, die in anderen Sprachen auch Prozeduren oder Unterprogramme genannt werden. Eine Funktion ist gekennzeichnet durch Parameter, die eine Art ``Eingabe'' in den Funktionsblock darstellen und solche, die eine ``Ausgabe'' darstellen. Im einfachsten Falle erfolgt die Übergabe der Eingabeparameter als Argument an die Funktion und die ``Ausgabe'' durch einen entsprechenden Rückgabewert.
Außer in wenigen Ausnahmefällen erzwingt C++ die Spezifikation
von Argument- und Rückgabetypen von Funktionen. Hier definiert power
eine Funktion, die aus einer Eingabe eines
doppeltgenauen Wertes base
und einer ganzen Zahl
exponent
einen neuen ``double
''-Wert berechnet und
zurückgibt. Der Name deutet an, welcher Algorithmus sich dahinter
versteckt.
// raise 'base' to the power 'exponent', with exponent being integer, // base being double; accept also negative exponents double power(double base, int exponent){ double result = 1.; // will multiply this by base 'exponent' times bool neg_exp = false; if (exponent < 0 ) { // for negative exponents we use a single // division at the end of the function neg_exp = true; exponent = -exponent; } for (int i=0; i < exponent; i++ ) result *= base; // successive multiplication if ( neg_exp ) // have to take the inverse result = 1./result; return result; // return the result to the calling program }Die Namen der Argumente sind wie Variablennamen frei wählbar; unter diesem Namen können sie dann im Funktionskörper angesprochen werden. Der Anweisungsblock des Funktionskörpers wird wie bei Schleifen nicht durch Semikolon abgeschlossen.
Das Schlüsselwort return
bricht das Unterprogramm an der
spezifizierten Stelle ab, legt den Wert des dahinter angegebenen
Ausdrucks auf dem Stack ab und kehrt ins Hauptprogramm zurück. Im
obigen Fall besteht dieser Ausdruck nur aus der Variable
result
. Runde Klammern werden an dieser Stelle nicht
benötigt. Ein Unterprogramm kann mehrere return
Anweisungen
enthalten. Dies kann ein nützliches Mittel sein, um z.B. bei Angabe
unpassender Argumenttypen oder bei Problemen mit der Berechnung die
Bearbeitung frühzeitig abzubrechen.
Eine Funktion muß definiert oder deklariert sein, bevor sie im Programm verwendet werden kann. Eine Deklaration ist im wesentlichen die erste Zeile der Funktionsdefinition, ohne ihren Körper. Sie teilt dem Compiler den Namen der Funktion und die Typen der Argumente und des Rückgabewertes mit.
double power(double base, int exponent);Anstelle des Anweisungsblocks tritt ein einzelnes Semikolon. Während die Angabe des Argumenttyps zwingend ist, ist die eines Namens für die Argumentvariable dem Benutzer freigestellt. Es ist jedoch guter Stil, hier Namen anzugeben, um die Bedeutung der Argumente klarzustellen.
Deklarationen können im Prinzip beliebig häufig im Programm wiederholt werden, sie sind sozusagen ein Versprechen an den Compiler, daß diese Funktion später oder in einem anderen Programmteil definiert wird. Funktionen sind immer globale Objekte innerhalb eines Namesraumes. Man kann Funktionen nicht wie Variablen innerhalb lokaler Bezugsrahmen definieren, z.B. innerhalb eines geschweiften Klammer-Paares (oder eines begin/end-Blockes wie in PASCAL).
Ein Programm, das von der Tastatur eine Zahl liest, dann power
benutzt und schließlich das Ergebnis ausgibt, könnte so
aussehen:
#include <iostream> using std::cout; using std::cin; double power(double base, int exponent); // declaration of 'power' int main(){ // definition of main double in; cin >> in; // read value from keyboard cout << "-3rd to 3rd power of " << in << ": "; for( int i= -3; i <= 3; i++ ){ cout << power(in, i) << " "; // call power and use return value } cout << "\n"; return 0; // return to operating system } double power(double base, int exponent){ // definition of power double result = 1.; // ... program text as above return result; }
Die Definition einer Funktion ist die Festlegung des Programmtextes, der bei Aufruf der Funktion ausgeführt werden soll. Geht die Definition einer Funktion ihrer Benutzung voraus, so kann sie eine Deklaration ersetzen. Die Spezifikation des Programmsegmentes für die Funktion erfolgt innerhalb eines geschweiften Klammerpaares, das nicht durch ein Semikolon abgeschlossen wird.
Die Übergabe von Argumenten an eine Funktion erfolgt ``by value,''
d.h., daß in C vor dem Aufruf der Funktion Kopien der Argumente
gemacht werden, die üblicherweise auf dem Stapel (Stack) abgelegt werden.
Dann erfolgt der Aufruf der Funktion, die dann nur auf den Kopien
dieser Variablen auf dem Stapel operiert. Es können so niemals die
Werte der Variablen im aufrufenden Programm verändert werden.
Möchte man dies erzielen, z.B. in Funktionen, die
Werte aus einer Datei oder Benutzereingaben einlesen, so benutzt man
Zeiger- oder Referenztypen als Argumente, die wir später in
Abschnitt besprechen werden.