/* Möglichkeit3: Einen Zeiger als Argument übergeben */
29
char*test4(char*ptr){
30
charbuffer[10];
31
ptr=buffer;
32
strcpy(buffer,"testwert");
33
returnptr;
34
}
35
36
intmain(void){
37
char*ptr;
38
39
ptr=test1();
40
printf("test1: %s\n",ptr);// meistens Datenmüll
41
ptr=test2();
42
printf("test2: %s\n",ptr);
43
ptr=test3();
44
printf("test3: %s\n",ptr);
45
test4(ptr);
46
printf("test4: %s\n",ptr);
47
returnEXIT_SUCCESS;
48
}
In der Tat funktionieren die Beispiele so wie sie niedergeschrieben
sind. ABER:
1. Muß ich in test3() nicht mit free(buffer) den Speicher wieder
freigeben? Wenn ich das mache bekomme ich in main in der Zeile
printf("test2: %s\n", ptr); für den Zeiger nichts angezeigt.
2. test4 scheint nur zu funktionieren weil ptr vorher im Speicher
gehalten wurde. Mit:
1
char*ptr2;
2
test4(ptr2);
3
printf("test4: %s\n",ptr2);
bekomme ich:
test4: (null)
Ich verwende gcc mit Codeblocks und default-Einstellungen.
Joachim ... schrieb:> /* Möglichkeit2: Speicher vom Heap verwenden */> char *test3(void){> char *buffer = (char *) malloc(10);> strcpy(buffer, "testwert");> return buffer;> }
Nachteil: Die aufrufende Funktion muss den free() machen.
> /* Möglichkeit3: Einen Zeiger als Argument übergeben */> char *test4(char *ptr){> char buffer[10];> ptr = buffer;> strcpy(buffer, "testwert");> return ptr;> }
Fehler: buffer ist nicht static, sondern liegt auf dem Stack. Nach
Verlassen der Funktion ist buffer "zerstört", d.h. nicht mehr anwendbar.
Joachim ... schrieb:> 1. Muß ich in test3() nicht mit free(buffer) den Speicher wieder> freigeben?
Genau, und wenn du es richtig machen willst den Pointer auch noch auf
NULL prüfen, bevor du ihn verwendest.
> 2. test4 scheint nur zu funktionieren weil ptr vorher im Speicher> gehalten wurde. Mit:>> char *ptr2;> test4(ptr2);> printf("test4: %s\n", ptr2);>> bekomme ich:> test4: (null)
Test4 kann ptr2 nicht verändern, da dieser by Value und nicht bei
Reference übergeben wird.
1
char*ptr2;
2
ptr2=test4(ptr2);
3
printf("test4: %s\n",ptr2);
Wird aber auch nicht funktionieren, da der Pointer auf eine
auto-Variable im Stack zeigt und diese freigegen wird wenn test4()
verlassen wird. Wenn man prt2 sofort nach dem Funktionsaufruf verwendet
bevor etwas Neues auf den Stack gelegt wurde kann es funktionieren,
saubere Programmierung ist es aber auf keinen Fall.
Den mit malloc reservierten Speicher aus test3 sollte man wieder
freigeben, aber erst, wenn er nicht mehr benötigt wird.
test4 hängt damit zusammen, und ist für sich alleine Unsinn. Die
Funktion gibt einen pointer auf den lokal angelegten Buffer zurück, was
nicht zulässig ist. Allerdings wird der Rückkgabewert nicht benutzt. Der
Parameter ptr wird per reference übergeben, und ändert damit außerhalb
von test4 seinen Wert nicht. Das auf test4 folgende printf gibt den
String aus test3 damit nochmal aus.
Ein free auf dem Speicher aus test3 darf daher erst nach test4
passieren.
Wenn du die Strings in den Funktionen von "testwert" in "testwert1" bis
"testwert4" änderst, erkennst du deutlicher was passiert.
Oliver
/* Möglichkeit3: Einen Zeiger als Argument übergeben */
2
char*test4(char**ptr){
3
charbuffer[10];
4
*ptr=buffer;
5
strcpy(buffer,"testwert");
6
return*ptr;
7
}
und
1
test4(&ptr);
Und ein genereller Tipp: Verwende unterschiedliche Testwerte in
unterschiedlichen Testfunktionen. Dein test4 dürfte nur funktionieren
weil vorher test3 gelaufen ist.
A. H. schrieb:> Versuch es mal mit> ...
Das Problem, dass buffer[10] nach dem Verlassen der Funktion vom Stack
genommen wird bleibt aber bestehen.
Und man sollte ptr nach dem Aufruf von test4 freen, weil der Pointer
geändert wird und der in test3 angeforderte Speicher somit für immer
verloren ist.
Joachim ... schrieb:> 1. Muß ich in test3() nicht mit free(buffer) den Speicher wieder> freigeben? Wenn ich das mache bekomme ich in main in der Zeile> printf("test2: %s\n", ptr); für den Zeiger nichts angezeigt.
Wenn du das tust, hast du ungefähr den gleichen Fall wie bei test1. Der
Speicher wurde wieder freigegeben, bevor du ihn in main() benutzt. Du
hast zwar recht, daß du den Speicher mit free() freigeben musst, aber
natürlich erst, wenn du ihn wirklich nicht mehr brauchst.
Es gibt in C generell drei Arten von Speicher, nämlich statisch,
automatisch und dynamisch.
Variablen im statischen Speicher existieren über die gesamte
Programmlaufzeit hinweg. Das macht sich test2() zunutze. Zu beachten ist
hier, daß ein weiterer Aufruf von test2() die Daten vom letzten mal
überschreibt.
Automatische Variablen sind lokale Variablen, die nicht static sind.
Diese werden beim Verlassen des Blocks, in dem sie definiert sind,
automatisch wieder freigegeben. Das ist das Problem bei test1(). Der
Aufrufer darf den zurückgegebenen Zeiger nicht nutzen, weil die
Variable, auf die er zeigt, mit Verlassen der Funktion aufhört zu
existieren.
Dynamischen Speicher bekommt man von malloc() und Konsorten. Dieser
bleibt so lange verfügbar, bis man ihn explizit mit free() wieder
freigibt. Damit hat man die größte Flexibilität, was die Lebensdauer
betrifft, aber muß sich selbst drum kümmern, es wieder freizugeben, wenn
man es nicht mehr braucht.
Joachim ... schrieb:> 1. Muß ich in test3() nicht mit free(buffer) den Speicher wieder> freigeben?
Nein. Nicht in test3(), sondern nachdem Du etwas mit dem
zurückgegebenen Pointer angestellt hast, also nachdem Du in Deinem
main() die Zeile
> printf("test3: %s\n", ptr);
aufgerufen hast.
Da gehört das
free(ptr);
hin.
Joachim ... schrieb:> Danke. Das hat funktioniert. Jetzt muß ich nur noch verstehen, warum **> hier die Lösung ist...
Du solltest lieber versuchen, zu verstehen, warum das in dem Fall NICHT
die Lösung ist, auch wenn es zufällig funktioniert.
Oliver
Joachim ... schrieb:> Danke. Das hat funktioniert. Jetzt muß ich nur noch verstehen, warum **> hier die Lösung ist...
Weil du damit den Pointer by Reference, und nicht by Value übergibst und
die Zuweisung *ptr = buffer dadurch auch außerhalb der Funktion Wirkung
hat.
So hast du aber das gleiche Problem wie in test1: buffer[10] wird vom
Stack genommen wenn du test4 verlässt. Das kann wie gesagt gut gehen,
saubere Programmierung ist es aber auf keinen Fall.
Es gibt noch eine weitere Möglichkeit für die nach dem "Array return
Suchenden":
1
structtext
2
{
3
chartxt[10];
4
};
5
6
structtextstring_return()
7
{
8
structtextmy_text;
9
10
strcpy(my_text.txt,"Hallo");
11
12
returnmy_text;
13
}
Auch wenn das auf den ersten Blick genauso falsch aussieht, wie test4()
(das einen Zeiger auf eine lokale Variable zurückgibt, und - sollte das
tatsächlich aus einem C-Buch stammen - dem Autor um die Ohren gehauen
gehört), ist das korrektes und funktionierendes C (structs können als
einziger komplexer Datentyp "by value" übergeben werden).
Trotzdem sollte man das nicht unbedingt in großem Stil benutzen, weil es
natürlich ziemlich ineffizient ist, größere Datenmengen über den Stack
zu schieben.
Die Beispiele sind in Ordnung, wenn sie zum erklären der möglichen
Fehler dienen.
Werden diese Fehler im Buch nicht erklärt, schmeiß es weg.
Du lernst dadurch Müll und merkst es nicht.
Gerade dieses Buch (und auch der Autor) sind bei
https://www.c-plusplus.net/forum/ verrufen, da das nicht die einzige
Stelle ist, wo das Buch Falsches vermittelt.
Gerade das Beispiel von 12.6.1 mit strtok beweist, dass der Autor keine
Ahnung hat.
1
char*eingabe(char*str){
2
charinput[MAX];
3
4
printf("Bitte \"%s\" eingeben: ",str);
5
fgets(input,MAX,stdin);
6
returnstrtok(input,"\n");
7
}
wird als richtig erklärt, während die Funktion ohne strtok mit return
input; als falsch beschrieben wird.
Klar ist das return input falsch, aber das strtok liefert denselben Wert
zurück.
Max H. schrieb:> A. H. schrieb:>> Versuch es mal mit>> ...> Das Problem, dass buffer[10] nach dem Verlassen der Funktion vom Stack> genommen wird bleibt aber bestehen.
Das ist richtig, darum geht hier aber nicht, denn das Problem wurde
bereits in test1 diskutiert und ist hier nicht falscher als dort. Man
könnte noch viel über die 4 Funktionen und deren Sinn (oder Unsinn)
schreiben, das drängendste Problem in diesem Fall war aber die
fehlerhafte Parameterübergabe (und die Möglichkeiten, dass zu erkennen)
und auf das habe ich mich beschränkt.
Die Sau mit dem falschen Beispiel wird nun schon seit Jahren durch die
Foren getrieben. Hier Beitrag "C pointer frage" ein
Beispiel vom 27.7.2010. Seltsam, dass der Rheinwerk-Verlag das noch
nicht geändert hat.
Joachim ... schrieb:> 1. Muß ich in test3() nicht mit free(buffer) den Speicher wieder> freigeben?
Sobald er nichtmehr benötigt wird.
> Wenn ich das mache bekomme ich in main in der Zeile> printf("test2: %s\n", ptr); für den Zeiger nichts angezeigt.
Das free mus nach dem printf stehen
Joachim ... schrieb:> test4 scheint nur zu funktionieren weil ptr vorher im Speicher gehalten> wurde. Mit:
...
> bekomme ich:> test4: (null)
Richtig. So könnte test4 alles sein, müsste nicht null sein. Trotzdem
überschreibst du irgendeinen Speicherbereich. Das orriginalbeispiel geht
wegen dem vorherigen malloc. Eine bessere Variante des Beispiels wäre:
1
charptr2[256];// 256 byte grosses array
2
test4(ptr2);// addresse, pointer auf array start übergeben
3
printf("test4: %s\n",ptr2);
Dann zeigt ptr2 garantiert auf freie 256 bytes Speicher.
Daniel A. schrieb:> char ptr2[256]; // 256 byte grosses array> test4(ptr2); // addresse, pointer auf array start übergeben> printf("test4: %s\n", ptr2);> Dann zeigt ptr2 garantiert auf freie 256 bytes Speicher.
So funktioniert das aber nicht, test4 schreibt in char buffer[10];
Damit es funktioniert müsste test4 so aussehen:
Und spätestens hier sollte man ein zusätzliches Argument in Erwägung
ziehen, um einen Buffer-Overflow zu verhindern - nämlich die Angabe der
Größe des übergebenden Buffers.
Max H. schrieb:> test4 schreibt in char buffer[10];
Sorry, das hatte ich übersehen. Zusätzlich hatte mich der Kommentar über
der Funktion irregeführt.
Danke für den Hinweis. Da habe ich wohl einen Fehler gemacht.