Seite 1 von 1

Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 08.09.2022, 14:44
von starcow
Ich habe hier ein Beispiel, in welchem es darum geht, wie man einen Zeiger auf einen Speicherbereich zurückgeben kann.

Entweder verstehe ich hier etwas nicht oder das Beispiel ist fehlerhaft.

Code: Alles auswählen

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Fehler Funktion gibt die Adresse
 * einer lokalen Variablen zurück. */
 
char *test1(void){
	char buffer[10];
	strcpy(buffer, "testwert");
	return buffer;
}

/* Möglichkeit1: Statische Variable */
char *test2(void){
	static char buffer[10];
	strcpy(buffer, "testwert");
	return buffer;
}

/* Möglichkeit2: Speicher vom Heap verwenden */
char *test3(void){
	char *buffer = (char *) malloc(10);
	strcpy(buffer, "testwert");
	return buffer;
}

/* Möglichkeit3: Einen Zeiger als Argument übergeben */
char *test4(char *ptr){
	char buffer[10];
	ptr = buffer;
	strcpy(buffer, "testwert");
	return ptr;
}

int main(void){
	char *ptr;

	ptr = test1();
	printf("test1: %s\n", ptr); // meistens Datenmüll
	ptr = test2();
	printf("test2: %s\n", ptr);
	ptr = test3();
	printf("test3: %s\n", ptr);
	test4(ptr);
	printf("test4: %s\n", ptr);

	return EXIT_SUCCESS;
}
Die Funktionen test1, test2, test3 sind klar. Auch, dass die Funktion test1 so nicht funktionieren kann, leuchtet ein.

Nur ergibt die Funktion test4 (Möglichkeit3) für mich aus folgenden Gründen keinen Sinn:

- Die Funktion nimmt zwar einen char pointer entgegen, nur wird der Parameter dabei bereits in der zweiten Zeile mit einer neuen Adresse überschrieben (die Adresse von buffer). Die Adresse, die bei Funktionseintritt an *ptr übergeben wurde, wird gar nie genutzt.

- Das char array buffer ist wie bei Funktion test1 nur lokal und hat damit die Lebensdauer auto. Die Speicheradresse wird ungültig, sobald die Funktion verlassen wird.

- Der Funktions-Parameter char *ptr ist komplett sinnlos. Ebenso gut hätte man buffer als pointer zurückgeben können - was ja unzulässig wäre, da die Lebensdauer des arrays buffer auto ist.

- Der zurückgegebene Pointer der Funktion test4 wird in der main Funktion überhaupt nicht "entgegen genommen".


Ich meine, man könnte natürlich einen Pointer an eine Funktion übergeben, um den entsprechenden Speicherbereich dann in der Funktion zu befüllen (dann wäre zudem der Rückgabetyp void sinnvoller). Nur müsste man dann zu Beginn auch tatsächlich Speicherplatz bereitstellen (auf dem Stack oder dem Heap). Einfach einen char Pointer in der Hauptfunktion deklarieren reicht ja nicht! Es ist ja schliesslich kein leeres char array! Gut, man könnte jetzt geltend machen, dass in der Funktion test3 auf dem Heap Speicher reserviert wurde und ptr nun auf diesen Bereich zeigt. Meiner Meinung nach suggerieren die Beispiele jedoch, dass sie für sich selbst stehen.
Unabhängig davon sehe ich aber nicht, wie die Funktion test4 so funktionieren sollte.

Übersehe ich hier etwas oder interpretiere ich etwas falsch? Oder ist vielleicht das Beispiel fehlerhaft?

Gruss, starcow

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 08.09.2022, 14:51
von Krishty
test4() ist in der Tat völliger Unsinn.

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 08.09.2022, 14:58
von Schrompf
Du hast das komplett richtig verstanden, soweit ich das überschaue. test4 macht exakt den selben Fehler wie test1, nur mit nem überflüssigen Parameter zusätzlich. Ich habe keine Ahnung, was da eigentlich die Absicht sein könnte. Vielleicht ist es als OutParam gemeint? Im Sinne von "Allokiere mir passenden Speicher und schreibe mir den Zeiger hier hin.". Dann wäre das Funktionsargument aber ein Doppelzeiger - man liest das als "Zeiger auf einen CharPointer".

Das alles soll sicherlich reines C bleiben, aber ich finde, es ist ein Prachtbeispiel für modernes C++. Einfach einen std::string zurückgeben, C++ Copy Elison vermeidet automatisch das eventuelle Umallokieren und für alle kurzen Strings wird gar nix allokiert. C++ wäre hier sowohl bequemer als auch schneller.

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 08.09.2022, 15:00
von Alexander Kornrumpf
Ich denke mal das Programm gibt "test4: testwert" aus, aber das ist noch der Testwert aus test3 wie man leicht überprüfen könnte, würde man verschiedene Testwerte in den verschiedenen Funktionen benutzen.

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 09.09.2022, 21:21
von starcow
Danke fürs Aufklären.
Ich habs jetzt trotzdem mal abgetippt und compiliert. Wie zu erwarten - und von Kornrumpf angemerkt, gibt das letzte printf einfach nochmals den selben String aus wie zuvor.
Jedenfalls hat es dieser Fehler bis in die vierte Auflage geschafft. :-)

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 11.09.2022, 17:18
von Lord Delvin
Das funktioniert halt, solange dir kein folgender Funktionsaufruf oder Interrupt den Speicher überschreibt, weil er in aller Regel von niemandem sonst überschrieben werden wird. "Es geht, ich habe es ausprobiert" ist halt nicht genug :)

Mach' mal die Optimierungen raus und rufe nochwas auf bevor du den Wert verwendest. (mit Optimierungen ist das verhalten nicht so klar, weil das ziemlich sicher als undefiniert definiert ist)

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 11.09.2022, 18:08
von Alexander Kornrumpf
Lord Delvin hat geschrieben: 11.09.2022, 17:18 Das funktioniert halt, solange dir kein folgender Funktionsaufruf oder Interrupt den Speicher überschreibt, weil er in aller Regel von niemandem sonst überschrieben werden wird. "Es geht, ich habe es ausprobiert" ist halt nicht genug :)

Mach' mal die Optimierungen raus und rufe nochwas auf bevor du den Wert verwendest. (mit Optimierungen ist das verhalten nicht so klar, weil das ziemlich sicher als undefiniert definiert ist)
Lies den code nochmal. Der ungültige Zeiger, den test4 zurückgibt, wird nirgendwo verwendet. Es funktioniert nicht nur zufällig sondern schon "by design". Es ist aber völlig unklar was das "design" eigentlich demonstrieren soll.

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 11.09.2022, 23:58
von starcow
Lord Delvin hat geschrieben: 11.09.2022, 17:18 Das funktioniert halt, solange dir kein folgender Funktionsaufruf oder Interrupt den Speicher überschreibt, weil er in aller Regel von niemandem sonst überschrieben werden wird. "Es geht, ich habe es ausprobiert" ist halt nicht genug :)

Mach' mal die Optimierungen raus und rufe nochwas auf bevor du den Wert verwendest. (mit Optimierungen ist das verhalten nicht so klar, weil das ziemlich sicher als undefiniert definiert ist)
Sorry, Missverständnis. Ich war nie der Meinung, dass das irgendwie geht. Ganz im Gegenteil! :-) Das Abtippen hatte alleine den Zweck, nochmals zu zeigen, dass das so nie gehen kann. Das letzte printft gibt einfach nochmals den String aus, wie er in Funktion test3 gesetzt wurde.

Re: Zeiger zurückgeben - Verständnisproblem oder fehlerhaftes Beispiel?

Verfasst: 13.09.2022, 19:43
von Lord Delvin
Alexander Kornrumpf hat geschrieben: 11.09.2022, 18:08
Lord Delvin hat geschrieben: 11.09.2022, 17:18 Das funktioniert halt, solange dir kein folgender Funktionsaufruf oder Interrupt den Speicher überschreibt, weil er in aller Regel von niemandem sonst überschrieben werden wird. "Es geht, ich habe es ausprobiert" ist halt nicht genug :)

Mach' mal die Optimierungen raus und rufe nochwas auf bevor du den Wert verwendest. (mit Optimierungen ist das verhalten nicht so klar, weil das ziemlich sicher als undefiniert definiert ist)
Lies den code nochmal.
Ja richtig. In meinem Kopf stand da das:

Code: Alles auswählen

/* Möglichkeit3: Einen Zeiger als Argument übergeben */
char *test4(char **ptr){
	char buffer[10];
	*ptr = buffer;
	strcpy(buffer, "testwer4");
	return *ptr;
}

int main(void){
	char *ptr;

	ptr = test1();
	printf("test1: %s\n", ptr); // meistens Datenmüll
	ptr = test2();
	printf("test2: %s\n", ptr);
	ptr = test3();
	printf("test3: %s\n", ptr);
	test4(&ptr);
	printf("test4: %s\n", ptr);

	return EXIT_SUCCESS;
}
Mit einem Stern weniger ist es natürlich kompletter Mist. Genau wie ihr sagt.