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

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

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

Beitrag 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
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Krishty
Establishment
Beiträge: 8316
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

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

Beitrag von Krishty »

test4() ist in der Tat völliger Unsinn.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 5044
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

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

Beitrag 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.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Alexander Kornrumpf
Moderator
Beiträge: 2138
Registriert: 25.02.2009, 13:37

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

Beitrag 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.
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

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

Beitrag 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. :-)
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 597
Registriert: 05.07.2003, 11:17

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

Beitrag 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)
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Alexander Kornrumpf
Moderator
Beiträge: 2138
Registriert: 25.02.2009, 13:37

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

Beitrag 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.
Benutzeravatar
starcow
Establishment
Beiträge: 560
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

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

Beitrag 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.
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 597
Registriert: 05.07.2003, 11:17

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

Beitrag 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.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Antworten