argv und Strings kopieren
-
Hallo, ich habe mal wieder ein Problem:
Da ich öfter mal Kommandozeilen-Parameter nutze, wollte ich eine Art Parser dafür basteln. Momentan jedoch klappt das nicht, laut Debugger erfolgt der Absturz in meiner String-Kopier-Funktion:void _string_copy (char * Target , char * Source) { unsigned long i = 0; /*printf("%s %s\n" , Target, Source); printf("%c\n" , Source[0]);*/ //diente zur Kontrolle, ob man auf die Elemente zugreifen kann - und das geht while(Source[i] != '\0') { Target[i] = Source[i]; i++; } Target[i] = '\0'; }Als Target ist hier ein String angegeben, der (aktuell) folgendermaßen aussieht:
{'0' , '\0' , 'T' , 'E'} - das T und E habe ich wohl im Speicher gefunden
Source sieht so aus:
{'l' , '1' , '0' , '\0'}
Das sind - wie gesagt - die Debuggerinformationen, die mir in dieser Funktion vorliegen. Bei Source handelt es sich um ein char-Array aus dem argv-Feld. Ich bin davon ausgegangen, dass ich argv als 2-dimensionales Char-Array ansehen kann (an eine weitere Funktion zum Parsen wurde char ** argv übergeben, von dort dann char * argv[i] an diese Funktion) und habe auch als Ziel ein solches Array gewählt.In genau folgender Zeile bekomme ich dann jedes Mal einen Segmentation Fault:
Target[i] = Source[i];Kann mir jemand sagen, woran das liegen könnte? Darf ich argv vielleicht nicht als völlig dynamisches Array behandeln oder hat es was mit dem Wetter zu tun?

Übrigens leistet mir die String-Kopier-Funktion schon seit einiger Zeit treue Dienste.
EDIT: Nach weiterem Probieren scheint sich das Problem darauf zu reduzieren, dass ich nicht in das Target-Array schreiben kann.
EDIT2: Ich habe gerade einen Weg gefunden, den Fehler zu umgehen - allerdings verstehe ich nicht, warum es nötig sein sollte, diesen Weg zu gehen. Mein Kopie-Feld wurde folgendermaßen initialisiert:
char ** Puffer; Puffer = malloc(_feld_laenge * sizeof(char*)); //_feld_laenge ist gleich der Anzahl der möglichen Parameter for(i = 0 ; i < _feld_laenge ; i++) //diesen Teil verstehe ich nicht { Puffer[i] = malloc(2 * sizeof(char)); Puffer[i] = "0"; if(Puffer[i] == NULL) return(NULL); }Einige Sicherheitsabfragen habe ich der Übersichtlichkeit halber mal entfernt.
Etwas später wird noch eine Funktion aufgerufen, die mein Array an genau der richtigen Stelle auf die richtige Größe anwachsen lässt. Warum muss ich also vorher schon jeden String mit eigenem Speicher versehen und kann nicht einfach einen Teil des Feldes "unerstellt" lassen?
-
Ich habe weiterhin Probleme:
for(i = 0 ; i < argc ; i++) //Sortierung nach erstem Zeichen { slaenge = 1 + _string_length(argv[i + 1]); Puffer[ argv[i + 1][0] - 97 ] = realloc(Puffer[ argv[i + 1][0] - 97] , slaenge * sizeof(char)); if(Puffer[argv[i + 1][0] - 97] == NULL) return(NULL); if(Puffer[argv[i + 1][0] - 97][0] == 48) _string_copy( Puffer[argv[i + 1][0] - 97] , argv[i + 1] ); }Hier soll mein Array sortiert werden, so dass ich später ganz leicht sehe, welche Parameter übergeben wurden. Schwierigkeiten machen hier realloc und die letzte if-Abfrage. Beide sorgen für einen Programmabsturz.
Ersetze ich das realloc durch ein malloc, ist das erste Problem beseitigt, allerdings müsste ich dann wieder alle Zellen mit einer "0" beschreiben.Der Compiler beschwert sich an keiner dieser Stellen, aber wenn ich das Programm in der Konsole ausführe, bekomme ich folgende Fehlermeldung:
*** glibc detected *** ./program: realloc(): invalid pointer: 0x0000000000401142 ***Danach folgt noch etwas, aber damit kann ich gar nichts anfangen.
Weiß vielleicht jemand Rat? Ich wollte diese Funktion eigentlich ganz schnell schreiben und hänge jetzt an unsichtbaren Hürden fest :(.
------------------
EDIT: Ich habe noch ein wenig drüber nachgedacht und die schädlichen Code-Zeilen entfernt. Falls aber jemand weiß, wo meine Probleme herkamen, würde ich mich über Aufklärung freuen :).
for(i = 0 ; i < argc ; i++) //Sortierung nach erstem Zeichen { slaenge = 1 + _string_length(argv[i + 1]); Puffer[ argv[i + 1][0] - 97 ] = malloc(slaenge * sizeof(char)); if(Puffer[argv[i + 1][0] - 97] == NULL) return(NULL); _string_copy( Puffer[argv[i + 1][0] - 97] , argv[i + 1] ); }So sieht es aktuell aus, ich hoffe, dass ich da nichts vergessen habe.
-
char ** Puffer; Puffer = malloc(_feld_laenge * sizeof(char*)); //_feld_laenge ist gleich der Anzahl der möglichen Parameter for(i = 0 ; i < _feld_laenge ; i++) //diesen Teil verstehe ich nicht { Puffer[i] = malloc(2 * sizeof(char)); Puffer[i][0] = '0'; Puffer[i][0] = '\0'; /* oder strcpy (Puffer[i], "0") */ if(Puffer[i] == NULL) return(NULL); }In Deiner Version allokierst den Speicher undlegst dann den Zeiger, der auf diesen Speicher verweist auf einen fixen String "0".
-
Hey.
Was hast du denn da vor, du machst dir das Leben viel zu kompliziert, es gibt
für sowas doch strdup().Und wenn du das schon selber machen möchtest, und strdup() ignorieren wolltest:
char* strdup_(const char* dest, const char* src) { size_t size = 0, i = 0; size = strlen(src); dest = calloc(size, 1); // sizeof(char) wird immer 1 bleiben. for(i; i<size; i++) dest[i] = src[i]; return dest; } int main( int argc, char** argv ) { unsigned int i = 0, j = 0; char** puffer; puffer = calloc(argc-1, sizeof(char*)); for(i=1; i<argc; i++, j++) puffer[j] = strdup_(puffer[j], argv[i]); // etwas damit tun for(i=0; i<=j; i++) free(puffer[i]); free(puffer); }Aber vergess das ganze hinterher nicht zu free'n!
-
Hi!
Was soll das ganze dublizieren.
Tuh doch einfach argv und argc speichern.typedef struct { int argc; char** argv; }cmdline_param;em ef ge
d.
-
@ hartmut1164:
Dein Vorschlag sieht eigentlich genauso aus wie meiner - ein String ist doch ein Array, oder übersehe ich da jetzt ein paar Details?
Die Null soll wirklich als String vorhanden sein, da das praktisch mein Zeichen ist, dass ein Parameter nicht übergeben wurde. Ich könnte es wohl auch um einen Byte verkürzen, vielleicht tue ich das ja noch.@ feraL:
Ich weiß, dass es diverse Funktionen gibt - aber was ich selbst schreibe, verstehe ich besser - und außerdem kann ich (falls nötig) die Funktion an das aktuelle Programm anpassen. Ansonsten scheint dein strdup die gleiche Arbeit zu leisten, wie mein _string_copy :).Das ganze soll erstmal alle möglichen Kommandozeilen-Parameter übernehmen, sortieren und (im geänderten Array) leicht zu Abfragen zur Verfügung stellen. Deshalb ist mein Array wohl auch etwas überlang (26 Strings momentan - für jeden Buchstaben eine Möglichkeit). So kann ich innerhalb des Programms leicht mit einer if-Abfrage auf das "i." Feld des Arrays zugreifen und weiß sofort, ob der Parameter i übergeben wurde (z.B. i50) oder nicht ('0').
Falls es da wesentlich leichtere (und elegantere) Möglichkeiten gibt, die trotzdem die volle Flexibilität bieten, würde es mich freuen, wenn sie mir jemand mitteilen könnte.@ dublikat0r:
Das bringt mich nicht ans Ziel, meine Parameter sollen bei der Eingabe keiner festen Reihenfolge unterliegen, beliebig kombiniert werden usw. Bisher habe ich immer auf argv[argc] o.ä. zugegriffen, musste jedoch einige Proben durchführen, um herauszufinden, ob ein Parameter übergeben wurde.Was mich noch immer wurmt:
Warum gehen diese Code-Zeilen nicht?Puffer[ argv[i + 1][0] - 97 ] = realloc(Puffer[ argv[i + 1][0] - 97] , slaenge * sizeof(char)); if(Puffer[argv[i + 1][0] - 97][0] == 48) _string_copy( Puffer[argv[i + 1][0] - 97] , argv[i + 1] );Die letzte ist ziemlich unnötig (fiel mir ja nach einer Weile auf
), aber ich erkenne bei keiner der Zeilen einen Fehler.
-
Stiefel2000 schrieb:
@ dublikat0r:
Das bringt mich nicht ans Ziel, meine Parameter sollen bei der Eingabe keiner festen Reihenfolge unterliegen, beliebig kombiniert werden usw. Bisher habe ich immer auf argv[argc] o.ä. zugegriffen, musste jedoch einige Proben durchführen, um herauszufinden, ob ein Parameter übergeben wurde.mit der struktur ist keine reihenfolge festgelegt, es wird lediglich argv und argc in einer struktur verpackt und kann einfach als parameter für weitere funktionen zur verfügung stehen, ohne das die argumente unnötig verdoppelt werden.
-
Stiefel2000 schrieb:
@ hartmut1164:
Dein Vorschlag sieht eigentlich genauso aus wie meiner - ein String ist doch ein Array, oder übersehe ich da jetzt ein paar Details?
Die Null soll wirklich als String vorhanden sein, da das praktisch mein Zeichen ist, dass ein Parameter nicht übergeben wurde. Ich könnte es wohl auch um einen Byte verkürzen, vielleicht tue ich das ja noch.Es geht um folgenden Sequenz (bei Dir Zeile 4 und 5):
Puffer[i] = malloc(2 * sizeof(char)); Puffer[i] = "0";Im ersten Schritt weisst dem Pointer Puffer[i] Arbeitsspeicher zu (2 * char). Dann nimmst Du aber den Pointer unverbiegst ihn auf eine Stelle im Arbeitsspeicher in der der String konstant "'0','/0'" steht. Dabei bleibt die allocierte Stelle im Arbeitsspeicher unbeschrieben, aber nur der Pointer verbogen.
Puffer [i] ist kein String (C kennt diesen Datentyp nicht), sondern nur die Representation der ersten Position eines Strings.
-
Stiefel2000 schrieb:
ein String ist doch ein Array, oder übersehe ich da jetzt ein paar Details?
weder ein Array, noch ein Pointer. Ein String ist eine mit \0 abgeschlossene Folge von Zeichen, das ist ein gewaltiger Unterschied. Man kann diese Liste in ein Array speichern, und darauf mit einem Zeiger zugreifen. Du musst nur die C Grundlagen über Arrays und Zeiger nochmal lernen, denn wer
Puffer[i] = malloc(2 * sizeof(char)); Puffer[i] = "0";schreibt, hat es eindeutig nicht verstanden.
-
Sag mir mal, ab wo es falsch wird:
char * test = "test";char * test; test = "test";char * test[2] = {"test1" , "test2"};char * test[2]; test[0] = "test1"; test[1] = "test2";char ** test = malloc(2*sizeof(char*)); test[0] = "test1"; test[1] = "test2";char ** test = malloc(2*sizeof(char*)); test[0] = malloc(6*sizeof(char)); test[0] = "test1"; test[1] = "test2";Das alles erzeugt bei mir gültigen C-Code, sollte es meinem Verständnis nach auch. Es wäre nett, wenn du mir mal ganz langsam auf die Sprünge helfen könntest - allzu kompliziert kann das ganze ja nicht sein.
-
Stiefel2000 schrieb:
char ** test = malloc(2*sizeof(char*)); test[0] = malloc(6*sizeof(char)); test[0] = "test1"; test[1] = "test2";das ist falsch, hartmut hat es dir bereits erklärt. Wenn du aber hartmuts Erklärung nicht verstehst, dann solltest du lieber sofort dein C-Grundlagen Buch und die Kapitel Pointer, Malloc und Strings *nochmal* durcharbeiten.
Stiefel2000 schrieb:
Das alles erzeugt bei mir gültigen C-Code, sollte es meinem Verständnis nach auch.
kompilierbarer Code != fehlerfreier Code, sonst gäbe es keine Software-Bugs
-
Heißt "falsch" einfach, dass ich mir die malloc-Zeile sparen soll (das wäre dann nicht wirklich falsch, sondern überflüssig)?
test[0] = "test1";Auf der rechten Seite erstelle ich ein char-Array, das einen Pointer an die linke Seite übergibt - zufällig darf ich dort auch Pointer speichern. Daran stört mich also nichts. Eventuell habe ich vorher mit malloc einen anderen Speicherbereich reserviert, in der Erwartung, dass ich diesen Speicher anschließend beschreibe.
Übrigens habe ich kein C-Grundlagenbuch und da ich noch immer vermute, dass es hier um ein winziges Verständnisproblem geht, wäre es wesentlich nützlicher, wenn mir jemand in zwei Sätzen genau erklärt, was falsch ist.
Ich habe auch mal den Inhalt der Variablen des letzten Feldes ausgeben lassen, das sieht so aus:
003F2690 //Adresse von test[0] unmittelbar nach dem malloc 003F2610 //Adresse von test (**) 00403004 + 0040300A //Adressen von test[0] und test[1] nach dem Beschreiben test1 + test2 //Inhalt der beiden Arrays
-
Stiefel2000 schrieb:
Übrigens habe ich kein C-Grundlagenbuch und da ich noch immer vermute, dass es hier um ein winziges Verständnisproblem geht, wäre es wesentlich nützlicher, wenn mir jemand in zwei Sätzen genau erklärt, was falsch ist.
Es geht leider nicht um ein "winziges Verständnisproblem", sondern um ein sehr grundsaetzliches Problem. Wenn Du leidlich Englisch kannst, dann empfehle ich das hier als C-Buch (einer der wenigen Faelle, wo sogar empfehlen wuerde die rund 80 Seiten auzudrucken und sich als Ringhefter hinzustellen):
http://www.oucs.ox.ac.uk/documentation/userguides/c/l922.pdf
-
supertux schrieb:
char ** test = malloc(2*sizeof(char*)); test[0] = malloc(6*sizeof(char)); test[0] = "test1"; test[1] = "test2";das ist falsch, hartmut hat es dir bereits erklärt. Wenn du aber hartmuts Erklärung nicht verstehst, dann solltest du lieber sofort dein C-Grundlagen Buch und die Kapitel Pointer, Malloc und Strings *nochmal* durcharbeiten.
Nein, falsch ist das nicht. Es ist Schwach-/Unsinnig, weil Speicherplatzverschwendung.
-
Stiefel2000 schrieb:
char * test = "test";char * test; test = "test";char * test[2] = {"test1" , "test2"};char * test[2]; test[0] = "test1"; test[1] = "test2";char ** test = malloc(2*sizeof(char*)); test[0] = "test1"; test[1] = "test2";char ** test = malloc(2*sizeof(char*)); test[0] = malloc(6*sizeof(char)); test[0] = "test1"; test[1] = "test2";^^
diese ganzen zuweisungen arbeiten mit adressen. z.b:char * test[2]; // ein array, das 2 adressen aufnehmen kann test[0] = "test1"; // erstes element bekommt die adresse von "test1" test[1] = "test2"; // zweites element bekommt die adress von "test2"dabei sind "test1" und "test2" zwei strings, die in einem (von deinem programm aus) nicht beschreibbaren speicher liegen. test[0] und test[1] zeigen nach den zuweisungen auf diese strings. ein kopieren der strings findet nicht statt.
was anderes wäre das:char test[] = "test1";^^dabei wird das array test[] mit den zeichen t,e,s,t,1,\0 initialisiert, also es wird wirklich was rüberkopiert.

-
blubb schrieb:
supertux schrieb:
char ** test = malloc(2*sizeof(char*)); test[0] = malloc(6*sizeof(char)); test[0] = "test1"; test[1] = "test2";das ist falsch, hartmut hat es dir bereits erklärt. Wenn du aber hartmuts Erklärung nicht verstehst, dann solltest du lieber sofort dein C-Grundlagen Buch und die Kapitel Pointer, Malloc und Strings *nochmal* durcharbeiten.
Nein, falsch ist das nicht. Es ist Schwach-/Unsinnig, weil Speicherplatzverschwendung.
Nicht nur das: Die Sache mal aus der Sicht des Betriebsystems. Der Prozess hat Speicher angefordert (eben im malloc) und wird ordnungsgemaess beendet. Weil aber die Referenz zum angeforderten Speicher nicht zu finden ist, diese deshalb auch nicht mit free () freigeben werden kann, stellt das Betriebssystem fest, dass dort noch Speicher existiert, der vom Prozess selber nichaufgeraeumt wurde und mossert.
-
Das Problem muss ja hochkomplex sein, wenn keiner von euch in der Lage ist, es mir kurz und knapp zu erklären
.@ +fricky: Das mit den Zeigern war mir natürlich klar, aber der Hinweis von dir, dass bei der einen Schreibweise was kopiert wird, war noch ganz nützlich (das wusste ich nämlich nicht).
@ hartmut1164: Vielen Dank für den Link, ich werde mir das Werk ansehen, sobald ich Zeit habe.
Die Essenz eurer zahlreichen Beschwerden an mich ist also, dass ich an der Stelle, an der ich eigentlich einen Speicherbereich beschreiben wollte,
char ** test = malloc(1*sizeof(char*)); /*test[0] = malloc(6*sizeof(char));*/ test[0] = "test1"; //diese Zeile meine icheine Zuweisung zu einem anderen Speicherbereich durchgeführt habe. Darf ich das so stehen lassen und als "wieder was gelernt" verbuchen?
Falls ja: Wie würde ich denn meinem 6-Byte langen dynamischen Char-Array einen String zuweisen können? Nur über den Zugriff auf die einzelnen Elemente (also zeichenweise)?
-
Stiefel2000 schrieb:
...aber der Hinweis von dir, dass bei der einen Schreibweise was kopiert wird, war noch ganz nützlich (das wusste ich nämlich nicht).
das geht aber nur so, wenn die variable frisch angelegt wird. später im code dann nicht mehr.
Stiefel2000 schrieb:
Falls ja: Wie würde ich denn meinem 6-Byte langen dynamischen Char-Array einen String zuweisen können? Nur über den Zugriff auf die einzelnen Elemente (also zeichenweise)?
etwa so:
char *str = malloc(6); // speicher für 6 zeichen holen ... strcpy (str, "hallo"); // füllen mit h,a,l,l,o,\0 achtung: nur sizeof(str)-1 zeichen (ausser der 0) sind möglich. ... free(str); // <-- wenn der speicher nicht mehr gebraucht wird
-
blubb schrieb:
Nein, falsch ist das nicht. Es ist Schwach-/Unsinnig, weil Speicherplatzverschwendung.
und damit falsch

Stiefel2000 schrieb:
Das Problem muss ja hochkomplex sein, wenn keiner von euch in der Lage ist, es mir kurz und knapp zu erklären
.Im Gegenteil, aber hier wird dir keiner etwas erklären, was du nicht selbst mit einem Grundlagen Buch lernen kannst. Viele deiner Fragen kommen durch das Fehler von gewissen Grundlagenwissen wie "was ist ein String", "was ist ein konstantes Literal", usw.
-
supertux schrieb:
...aber hier wird dir keiner etwas erklären, was du nicht selbst mit einem Grundlagen Buch lernen kannst...
manche lernen leichter wenn sie jemanden fragen, anstatt aus büchern. wie auch immer, programmieren lernt man sowieso nur durch übung.

-
@ +fricky: Das mit strcpy hätte ist mal eine Idee, darauf wäre ich niee gekommen ;). Aber ich habe wirklich nicht daran gedacht, obwohl ich in meinem Quelltext oben genau das mache...
Eine Antwort auf meine Frage im letzten Post wäre ganz nett, ich hoffe noch immer, dass die Unklarheiten damit beseitigt wären.
Die Essenz eurer zahlreichen Beschwerden an mich ist also, dass ich an der Stelle, an der ich eigentlich einen Speicherbereich beschreiben wollte, eine Zuweisung zu einem anderen Speicherbereich durchgeführt habe. Darf ich das so stehen lassen und als "wieder was gelernt" verbuchen?