Verständnisschwierigkeiten, Beispiel mit Zeiger und Arrays
-
Hallo,
bin neu hier im Forum und bringe mir gerade C bei. Auf die Empfehlung eines Bekannten hin, habe ich bisher die online Version von "C von A bis Z" von Jürgen Wolf durchgearbeitet, bis zum Kapitel Zeiger. Jetzt tauchten da aber zwei Beispiele auf, die mich stutzig gemacht haben und bei denen ich nachhaken wollte. Das erste was ich dann hier im Forum finde, ist, dass die Werke von besagtem Autor anscheinend ziemlich umstritten sind (wohlwollend ausgedrückt). Aber jetzt mal zum Thema. Im Endeffekt würde ich gerne wissen, ob meine Annahmen, die jeweils unter dem Beispiel stehen richtig sind.1. Beispiel: http://openbook.galileocomputing.de/c_von_a_bis_z/012_c_zeiger_006.htm#mj1d0c139ec6125f198a762880b480283f
char *test4(char *ptr){ char buffer[10]; ptr = buffer; strcpy(buffer, "testwert"); return ptr; } int main(void) { char *ptr; ... test4(ptr); printf("test4: %s\n", ptr); return EXIT_SUCCESS; }
Ist es hier nicht unsicher, den array "buffer" lokal zu deklarieren? Die Funktion "test4" gibt ja nen Zeiger auf das "t" vom String "testwert" zurück. Oder? Dass printf dann den ganzen String ausgibt muss aber nicht immer so sein, weil "buffer" mit verlassen von "test4" nicht mehr existiert und der entsprechende Speicher neu beschrieben werden könnte.
2. Beispiel: http://openbook.galileocomputing.de/c_von_a_bis_z/012_c_zeiger_008.htm#mj13c471f1707e00fcd1343a6b758e2093
void funktion(char *str) { printf("%s\n",str); } int main(void) { char *string = "Hallo Welt"; funktion(string); printf("Anfangsadresse auf die *string zeigt = %p\n",*string); printf("Der Inhalt dieser Anfangsadresse = %c\n",*string); return EXIT_SUCCESS; }
Ähnliche Frage: Das printf in "function" gibt zwar "Hallo Welt" komplett aus, aber richtig gespeichert bzw. reserviert ist der String doch nirgends, oder?
Letzte Frage. Ich bin jetzt ein bisschen verunsichert, ob ich mit "C von A bis Z" weiterarbeiten soll. Als Alternative hätte ich http://de.wikibooks.org/wiki/C-Programmierung gefunden, ist das eher zu empfehlen?
Ich sage schonmal recht herzlich danke.
-
1. Gut erkannt, so ist es.
2. string ist ein Stringliteral. Der ist schon (irgendwo) im Program abgelegt.
Nur ändern kannst du ihn nicht. Das Programm gibt einen Laufzeitfehler.
Darum teilst du das dem Compiler schon vorher mit, damit der Meckern kann.const char *string = "Hallo Welt";
-
Es ist wirklich erschütternd, was dieser Autor für einen Müll zusammenschreibt. Der Text ist sogar richtig, er sagt, man könne "einen beim Aufruf der Funktion als Argument übergebenen Puffer" verwenden. Das Beispiel
text4
macht aber was ganz anderes, und das ist schon kein Fehler mehr, das ist grober Unfug.Richtig müsste es so aussehen:
/* Möglichkeit3: Einen Zeiger als Argument übergeben */ char *test4(char *ptr){ strcpy(ptr, "testwert"); return ptr; }
Da ist dann der Aufrufer dafür zuständig, den Speicher im voraus zu besorgen und auch wieder abzuräumen.
-
Bashar schrieb:
Es ist wirklich erschütternd, was dieser Autor für einen Müll zusammenschreibt. Der Text ist sogar richtig, er sagt, man könne "einen beim Aufruf der Funktion als Argument übergebenen Puffer" verwenden. Das Beispiel
text4
macht aber was ganz anderes, und das ist schon kein Fehler mehr, das ist grober Unfug.Richtig müsste es so aussehen:
/* Möglichkeit3: Einen Zeiger als Argument übergeben */ char *test4(char *ptr){ strcpy(ptr, "testwert"); return ptr; }
Da ist dann der Aufrufer dafür zuständig, den Speicher im voraus zu besorgen und auch wieder abzuräumen.
Und selbst das ist im seinen Kontext falsch, weil er in der main-Funktion die ptr Variable nicht initialisiert.
@dephyyar: wenn du nicht willst, dass dein Gehirn mit falscher Information zugemüllt wird, dann lies dieses Buch nicht weiter. Ich kenne die Print-Ausgabe nicht, aber die Online-Ausgabe ist zum Heulen.
-
Bashar schrieb:
Richtig müsste es so aussehen:
/* Möglichkeit3: Einen Zeiger als Argument übergeben */ char *test4(char *ptr){ strcpy(ptr, "testwert"); return ptr; }
Wennschon, dann so:
char *test4(char *ptr){ return strcpy(ptr, "testwert"); }
-
Danke für die Antworten.
@DirkB
Dein 2. verstehe ich nicht ganz. Bei mir gibt es auch gar keine Fehlermeldung bzw nur eine Warnung, aber laufen tuts.@Bashar und Wutz
Ich verstehe nicht den Unterschied zwischen euren Lösungen. Aber mit eurem Code ist die Ausgabe von "testwert" auch nicht sicher, oder? Der pointer verweist zwar auf den entsprechenden Speicher, aber der ist ja nicht für ne Variable oder so reserviert. Das heißt der könnte in einem umfangreicheren Programm also neu beschrieben werden, und dann wär der "testwert" string weg, oder?
Wieso kann ich denn überhauüt mit strcpy einen string auf einen pointer kopieren?Nochmal zu meiner dritten Frage, hat jemand Erfahrung mit dem wikibook? Ist das zu empfehlen.
-
Man kopiert keine Strings in einen Pointer. C kennt keine Strings. Da passiert Folgendes:
char* strcpy(char* str1, const char* str2) { char* rval = str1; while (*str2) *str1++ = *str2++; *str1 = '\0'; return rval; }
Ich glaube das Wikibooks Ding ist ganz gut..
-
dephyyar schrieb:
Aber mit eurem Code ist die Ausgabe von "testwert" auch nicht sicher, oder? Der pointer verweist zwar auf den entsprechenden Speicher, aber der ist ja nicht für ne Variable oder so reserviert.
Der Speicher muss vom Aufrufer bereitgestellt werden, z.B. so:
int main() { char speicher[10]; char *ptr = test4(speicher); }
Oder per malloc() oder sonst wie. Man muss für die Funktion dokumentieren, wieviel Speicher sie belegt, oder jedenfalls irgendeine Möglichkeit anbieten, dass der Aufrufer die korrekte Menge Speicher bereitstellen kann.
Das heißt der könnte in einem umfangreicheren Programm also neu beschrieben werden, und dann wär der "testwert" string weg, oder?
Nö, wie denn?
-
dephyyar schrieb:
@Bashar und Wutz
Ich verstehe nicht den Unterschied zwischen euren Lösungen. Aber mit eurem Code ist die Ausgabe von "testwert" auch nicht sicher, oder? Der pointer verweist zwar auf den entsprechenden Speicher, aber der ist ja nicht für ne Variable oder so reserviert. Das heißt der könnte in einem umfangreicheren Programm also neu beschrieben werden, und dann wär der "testwert" string weg, oder?Nein, kann er nicht. Sring-Literale werden vom Compiler so untergebracht, daß du sie nicht ändern kannst (wenn doch, endet das idR mit einer Zugriffsverletzung). Deshalb warnt dich ja auch der Compiler, mit einem (nicht-konstanten) char* auf diesen Bereich zu verweisen.
PS:
printf("Anfangsadresse auf die *string zeigt = %p\n",*string);
Das gibt zwar etas aus, aber ich glaube nicht, daß diese Ausgabe etwas mit der Anfangsadresse zu tun hat (du übergibst das erste Zeichen des Srings an printf() und versuchst es als Pointer zu interpretieren).