Im vorletzten Artikel wurde das allgemeine Konzept der Funktionen eingeführt, im letzten das Konzept der Variablen. Heute wollen wir beide Konzepte verbinden und uns anschauen, was unter Funktionsparametern zu verstehen ist.
Unsere bisherigen selbstgeschriebenen Funktionen haben immer nur maximal einen Wert zurückgeliefert; mehr haben sie nicht gemacht, zum Beispiel hier:
int f() { return 42; }
Die Funktion liefert den Wert 42 zurück, und zwar bei jedem Aufruf. Das ist zwar schön und gut, wenn man einen konstanten Wert berechnen und wiederverwenden möchte, aber so richtig weit kommt man damit nicht. Und wenn wir an die Mathematik denken, dann verhalten sich Funktionen dort auch meist ein klein wenig anders, indem sie nämlich ein Ergebnis abhängig von einer Eingabe berechnen. Die mathematische Funktion
sqr(x) = x * x
etwa berechnet das Quadrat der Eingabe. Um Funktionen sinnvoll nutzen zu können, benötigen wir das natürlich auch beim Programmieren – gibt es natürlich auch. Das Schlüsselwort hierfür sind die bereits erwähnten Funktionsparameter. Genutzt haben wir die auch schon indirekt, und zwar hier:
printf( "Hello World!" );
Die Funktion printf
verlangt als Funktionsargument die Zeichenkette, die sie auf dem Bildschirm ausgeben soll (und zusätzlich eventuell noch weitere Argumente, aber das ist ein anderes Thema). Wir geben der Funktion damit eine Eingabe, mit der sie etwas macht (in diesem Fall, sie auf dem Bildschirm auszugeben). Die große Frage ist nun nur noch: wie teilen wir einer Funktion mit, dass sie eine Eingabe erwartet, und noch wichtiger: was für eine Eingabe?
Nichts leichter als das. Wir müssen einfach nur die Signatur der Funktion (wir erinnern uns: das war der Kopf, etwa hier: int main()
) um die Informationen erweitern, welche Eingabe von einer Funktion erwartet wird. Dazu schreiben wir einfach in die Klammern der Signatur, welche Eingabe wir erwarten, sprich: welche Datentypen erwarten wir als Eingabe und mit welchem Namen wollen wir die Eingabe dann in der Funktion ansprechen. Das erfolgt in Form der bereits bekannten Variablendeklarationen und sieht zum Beispiel so aus:
int sqr( int x )
Hiermit definieren wir den Kopf einer Funktion, die eine einzelne ganze Zahl x
als Eingabe erwartet und eine ganze Zahl als Ergebnis zurückliefert. Die Variable x
bezeichnen wir als Funktionsparameter. Die vollständige Funktion sieht dann so aus:
int sqr( int x ) { return x * x; }
Wir sehen, dass die im Funktionskopf deklarierte Variable x
wie eine normale lokale Variable im Funktionsrumpf genutzt werden kann. Die Nutzung der so definierten Funktion erfolgt dann in der bekannten Weise:
#include <cstdio> int sqr( int x ) { return x * x; } int main() { int x = 42; printf( "%i squared is: %i", x, sqr( x ) ); }
Eine Bemerkung zu den Begrifflichkeiten: bei der Definition einer Funktion spricht man von Funktionsparametern, beim Aufruf der Funktion spricht man bei den übergebenen Werten von Funktionsargumenten. Der Parameter ist also die im Funktionskopf deklarierte Variable, das Argument ist der tatsächlich eingegebene Wert. In int f( int x )
heißt x
also Parameter, in f( x )
heißt x
Argument.
Und um an dieser Stelle gleich einen bekannten Fallstrick zu erwähnen: Funktionsparameter und -argumente sind – genauso wie gleich benannte Variablen in unterschiedlichen Scopes – vollkommen unabhängig voneinander, sprich, nur weil der Parameter einer Funktion x
heißt, muss als Argument nicht auch zwingend eine Variable mit der Bezeichnung x
eingegeben werden. Das ergibt sich schon logisch daraus, dass als Argument jeder beliebige Ausdruck eingesetzt werden kann, und nicht nur Variablen. Der folgende Codeausschnitt ist ebenso gültig (in verkürzter Formatierung der sqr
-Funktion):
int sqr( int x ) { return x * x; } int main() { int y = 42; printf( "%i squared is: %i", y, sqr( y ) ); }
Eine Funktion kann natürlich auch mehrere Eingabewerte verlangen; die werden dann einfach hintereinander geschrieben, etwa so:
int mul( int x, int y ) { return x * y; }
Der Aufruf erfolgt dann ganz simpel per
mul( 42, 2 )
Worauf man übrigens achten muss: wurde eine Variable bereits als Funktionsparameter deklariert, kann im Rumpf der Funktion keine Variable mit dem gleichen Namen deklariert werden. Der folgende Code ist also ungültig (unabhängig von seiner Sinnhaftigkeit):
int f( int x ) { int x = 1; return x * x; }
Ein letztes Konzept noch: bei manchen Funktionsaufrufen möchte man es sich manchmal ersparen, bestimmte Argumente immer mit angeben zu müssen, da sie sowieso meist (nicht immer!) den gleichen Wert haben sollen. In diesem Fall kommen dann die sogenannten Default-Argumente zum Einsatz. Bei der Deklaration eines Funktionsparameters kann ihm ein Standard- oder Default-Wert zugewiesen werden; wann immer beim Funktionsaufruf kein Argument für diesen Parameter übergeben wird, wird stattdessen automatisch der Default-Wert eingesetzt. Das sieht dann so aus:
int f( int x, int y = 1 ) { return x * y; }
Aufgerufen werden kann diese Funktion sowohl per f( 42, 2 )
als auch per f( 42 )
– im ersten Fall wäre das Ergebnis 84, im zweiten 42; natürlich kann auch mehr als ein Parameter mit Default-Argumenten versehen werden. Um bei einem Funktionsaufruf zu wissen, wann Default-Argumente eingesetzt werden müssen, dürfen diese natürlich nur am Ende der Parameterliste stehen, da sonst nicht klar wäre, wie die Funktion aufgerufen werden soll. Nehmen wir einmal den folgenden Funktionskopf (welches so ungültiger Code ist):
int f( int x = 1, int y, int z = 3 )
und dazu den folgenden Aufruf der Funktion:
f( 42, 84 )
Würde eine beliebige Anordnung der Default-Argumente erlaubt sein, wäre nun gar nicht klar, wie f
aufgerufen werden soll. Es könnte die 42
für x
und die 84
für y
oder die 42
für y
und die 84
für z
eingesetzt werden. Aus diesem Grund ist das auch gar nicht erst erlaubt und Default-Argumente müssen immer am Ende der Parameterliste stehen.
Kommentare (21)