10.5 Zeiger als Rückgabewert
Ein Zeiger kann auch als Rückgabetyp einer Funktion deklariert werden – dann gibt er aber logischerweise auch nur die Anfangsadresse des Rückgabewertes zurück. Die Syntax dazu sieht folgendermaßen aus:
Typ* Funktionsname( formale Parameter )
Das Verfahren mit Zeigern als Rückgabewert von Funktionen wird bei Arrays, Strings oder Strukturen verwendet und ist eine effiziente Methode, um umfangreiche Datenobjekte aus einer Funktion zurückzugeben. Natürlich werden hier nicht ganze Datenobjekte, sondern nur die Anfangsadressen darauf an den Aufrufer zurückgegeben.
Wenn Sie sich noch an Abschnitt 7.6, »Exkurs: Funktionen bei der Ausführung«, erinnern, wissen Sie, dass beim Aufruf einer Funktion der Stack verwendet wird. Auf diesem werden alle benötigten Daten einer Funktion (die Parameter, die lokalen Variablen und die Rücksprungadresse) abgelegt. Die Rede ist vom Stack-Frame. Dieser bleibt so lange bestehen, bis sich die Funktion wieder beendet. Geben Sie eine Adresse auf einen solchen Speicherbereich (lokalen Speicher) zurück, der sich ebenfalls auf dem Stack-Frame befand und somit bei Beendigung der Funktion nicht mehr vorhanden ist, wird ein undefinierter Speicherbereich zurückgegeben.
Die letzte Aussage mag für Sie ein wenig verwirrend klingen (vor allem, wenn Sie nicht, wie wir, Experten im Bereich der Prozessorarchitekturen sind); deshalb erklären wir die letzten Sätze noch einmal genauer.
Wollen Sie einen Zeiger auf einen gültigen Speicherbereich zurückgeben, haben Sie folgende Möglichkeiten. Sie verwenden
-
einen statischen (static) oder einen globalen Speicher,
-
einen beim Aufruf der Funktion als Argument übergebenen Speicher (bzw. eine Adresse) oder
-
einen mittels malloc() zur Laufzeit reservierten Speicher (siehe Kapitel 11, »Dynamische Speicherverwaltung«).
In der Praxis würden wir zwar die dynamische Reservierung von Speicher zur Laufzeit mit malloc() bevorzugen, aber um nicht auf dieses Thema vorzugreifen, soll hier ein Beispiel mit einem statischen Speicherbereich demonstriert werden:
00 // Kapitel10/statischer_puffer.c
01 #include <stdio.h>
02 #include <stdlib.h>
03 #define MAX 5
04 int* ifget( void ) {
05 static int puffer[MAX];
06 for(int i=0; i<MAX; i++) {
07 printf("Wert %d : ", i+1);
08 if( scanf("%d", &puffer[i] ) != 1 ) {
09 printf("Fehler bei der Eingabe\n");
10 return EXIT_SUCCESS;
11 }
12 }
13 return puffer;
14 }
15 int main(void) {
16 int* iptr = ifget( );
17 printf("Folgende Werte wurden eingelesen\n");
18 for(int i=0; i < MAX; i++ ) {
19 printf("%d : %d\n", i+1, *(iptr+i));
20 }
21 return EXIT_SUCCESS;
22 }
In Zeile (16) wird die Funktion ifget() aufgerufen. Der Rückgabewert wird an den int-Zeiger iptr zugewiesen. Entsprechend muss natürlich auch der Funktionskopf der Zeile (04) aufgebaut sein, die einen Zeiger vom Typ int* zurückgibt. Damit die Funktion einen gültigen Speicherbereich zurückgibt, wurde in Zeile (05) mit dem Schlüsselwort static ein statischer Speicher, nämlich ein Array mit MAX int-Werten definiert. Diesem Array übergeben Sie in der for-Schleife (Zeile (06) bis (12)) insgesamt MAX int-Werte. In Zeile (13) geben Sie die Anfangsadresse des statischen Speicherbereichs an den Aufrufer der Zeile (16) zurück. In der main()-Funktion werden die in der Funktion ifget() eingegebenen int-Werte nochmals in einer for-Schleife in den Zeilen (18) bis (20) ausgegeben.
Würden Sie das Schlüsselwort static in Zeile (05) entfernen, würden in der main()-Funktion undefinierte Werte ausgegeben, weil ein lokaler Speicher nach dem Ende der Funktion, der nicht mehr vorhanden ist, an den Aufrufer zurückgegeben und verwendet würde. Abhängig von Compiler und Warnstufe sollten Sie allerdings auch eine entsprechende Warnung erhalten.
Das Programm gibt bei der Ausführung Folgendes aus:
Wert 1 : 12
Wert 2 : 34
Wert 3 : 56
Wert 4 : 78
Wert 5 : 90
Folgende Werte wurden eingelesen
1 : 12
2 : 34
3 : 56
4 : 78
5 : 90