strtok und char**: Warum fehlt ein Eintrag im char**?



  • Hallo, ich bin's mal wieder mit meiner frühen Pseudo-Shell. (siehe http://www.c-plusplus.net/forum/viewtopic-var-t-is-145301.html)

    Diesmal habe ich ein Problem mit einem dynamischen char**.
    Ich bin gerade dabei, die per readline übergebene Zeile per strtok aufzuteilen und in ein char** zu speichern.
    Dieses char** (sagen wir mal params) sollte dann eigentlich in der Form
    {"COMMAND", "PARAM1", "PARAM2", ..., NULL } vorliegen.
    Damit könnte ich doch wunderbar execv füttern:

    execv(params[0], params);
    

    Während dem "Tokenizing" passen alle Eintrage in params aber wenn ich danach noch einmal über das Array iteriere, fehlt der erste Eintrag.
    Und ich bekomme nicht herraus, warum dieser fehlt.

    Anscheinend hat strtok noch mehr Seiteneffekte die ich im Moment nicht in den Griff bekomme.

    count_delim ist einfach nur dazu da um die Anzahl der Parameter herrauszufinden.
    Damit möchte ich vermeiden, dass ich mich auf eine bestimmte Anzahl an Parametern festlege.

    Nach dem Code kommt die Ausgabe des Programmes.

    /* Muschel.c
     * 
     * This work is licensed under a
     * Creative Commons Attribution-ShareAlike 2.0 Germany License
     * http://creativecommons.org/licenses/by-sa/2.0/de/
     * 
     * Johannes Held - sijoheld - 21197451
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <readline/readline.h>
    #include <readline/history.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <string.h>
    
    const char* prompt = "?: ";
    const char* delim = " ";
    
    /* int count_delim(char* src, const char* delim)
     * 	char* src: char* to parse
     *  char* delim: the delimiter to count
     * 
     *  AFTER this function, src is not altered.
     *  count_delim works on a copy of str.
     */
    int count_delim(char* src, const char* delim) {
    	char* wako = 0;
    	char* token = 0;
    	int count = 0;
    
    	wako = strdup(src);
    	token = strtok(wako, delim);
    	while(token != NULL) {
    		++count;
    		token = strtok(NULL, delim);
    	}
    	free(wako);
    	return count;
    }
    
    int main() {
    	char* user_input;
    	char* token = NULL;
    	char** params = NULL;
    	int paramcount = 0;
    	int count = 0;
    
    	/* I user readline for userinput.
    	 * If the user hits ENTER, simply prompt him again! */
    	do {
    		/* Don't forget to free char* got from readline! */
    		user_input = readline(prompt);
    	} while(strlen(user_input) == 0); 
    
    	/* - Get the count of parameters.
    	 * - create an new array of size parameters+1 to hold trailing NULL 
    	 * - tokenize through user_input and copy every token into array. */ 
    	paramcount = count_delim(user_input, delim) +1;
    	params = (char**)malloc((paramcount) * sizeof(char));
    
    	count = 0;
    	token = strtok(user_input, delim);
    	while(token != NULL) {
    		params[count] = (char*)malloc(strlen(token) +1);
    		memset(params[count], 0, strlen(token) +1);
    		strncpy(params[count], token, strlen(token));
    		token = strtok(NULL, delim);
    		printf("[%d] %s\n", count, params[count]);
    		++count;
    	}
    	params[count] = NULL;
    	printf("[%d] %s\n", count, params[count]);
    	/* now params should be something like that:
    	 * {"COMMAND", "PARAM1", "PARAM2", ..., NULL } */
    
    	for(count = 0; count < paramcount; ++count) {
    		printf("[%d] %s\n", count, params[count]);
    	}
    
    	free(user_input); 
    
    	return 0;
    }
    
    11:13:47 [~/codes/c/Muschel]
    ./Muschel 
    ?: Hallo ich bin Johannes
    [0] Hallo
    [1] ich
    [2] bin
    [3] Johannes
    [4] (null)
    [0] 
    [1] ich
    [2] bin
    [3] Johannes
    [4] (null)
    11:13:58 [~/codes/c/Muschel]
    

    Weiterhin ist mir schleierhaft, dass ich wenn ich mir die einzelnen Tokens mit strtok hole beim erstellen des neuen char* mit malloc, 1 dazuzählen muss.
    Denn lasse ich diese 1 weg, dann wird nicht korrekt kopiert.
    Obwohl mit man strtok sagt das jeder Aufruf von strtok einen \0-terminierten char* zurückgibt.
    Wenn ich die gleiche Schleife mit Texten aus einem ensprechenden char** mache {"Hallo", "ich", "bin", "Johannes"}, dann brauche ich diese zusätzliche 1 nicht.

    Ich würde mich freuen, wenn ihr Licht in meine Fragen bringen würdet.
    Danke.



  • Also auf Anhieb kann ich nicht erkennen, wer dir deinen String wieder löscht - starte das Programm mal im Debugger und beobachte genau, wo etwas verändert wird.

    Weiterhin ist mir schleierhaft, dass ich wenn ich mir die einzelnen Tokens mit strtok hole beim erstellen des neuen char* mit malloc, 1 dazuzählen muss.
    Denn lasse ich diese 1 weg, dann wird nicht korrekt kopiert.
    Obwohl mit man strtok sagt das jeder Aufruf von strtok einen \0-terminierten char* zurückgibt.

    Ja, der Rückgabestring von strtok ist 0-teminiert - aber strlen zählt diese '\0' nicht mit, wenn es die Stringlänge berechnet.



  • CStoll schrieb:

    Also auf Anhieb kann ich nicht erkennen, wer dir deinen String wieder löscht - starte das Programm mal im Debugger und beobachte genau, wo etwas verändert wird.

    Dazu muss ich hier erst mal mit dem gdb (bzw dem gdb mit Eclipse) firm werden.

    CStoll schrieb:

    hehejo schrieb:

    Weiterhin ist mir schleierhaft, dass ich wenn ich mir die einzelnen Tokens mit strtok hole beim erstellen des neuen char* mit malloc, 1 dazuzählen muss.
    Denn lasse ich diese 1 weg, dann wird nicht korrekt kopiert.
    Obwohl mit man strtok sagt das jeder Aufruf von strtok einen \0-terminierten char* zurückgibt.

    Ja, der Rückgabestring von strtok ist 0-teminiert - aber strlen zählt diese '\0' nicht mit, wenn es die Stringlänge berechnet.

    Und warum gibt es dann hierbei keine Fehler?

    char* text = "Hallo";
    char* neu = (char*)malloc(strlen(text));
    memset(neu, 0, strlen(text));
    strncpy(neu, text, strlen(text));
    printf("%d %s\n", strlen(text), text);
    printf("%d %s\n", strlen(neu), neu);
    free(neu);
    

    Hier habe ich jetzt doch auch nicht auf \0 geachtet.



  • Fehler gibt es dort keinen, weil strncpy verhindert dass das Nullzeichen hinter den allokierten String geschrieben wird. Wenn dort (in fremdem Speicher) jetzt noch ein Nullbyte steht (was garnicht mal so unwahrscheinlich ist), sieht die Ausgabe sogar identisch aus, aber das ist Zufall (undefiniertes Verhalten).



  • Ok, dann ist das kleine Programm oben, also falsch und ich weiß, wie ich es in Zukunft machen muss.

    Aber vll. eine Ahnung, warum paramams[0] leer wird?
    Ich kopiere es doch korrekt, oder nicht?

    Also langsam wird das aber echt seltsam.
    Jetzt habe ich nach dem ersten Aufruf von strtok, den bekommenen token per strdup dupliziert.

    token = strtok(woka, " ");
    char* command = strdup(token);
    

    Jetzt steht auch wieder ein Text in params[0]. Aber command ist leer.

    Was läuft denn da bitte für black magic???



  • params = (char**)malloc((paramcount) * sizeof(char));
    

    müsste

    params = (char**)malloc((paramcount) * sizeof(char *));
    

    sein
    und mit

    params[count] = NULL;
    

    schreibst du über die arraygrenzen hinaus.
    also an besten

    params = (char**)malloc((paramcount + 1) * sizeof(char *));
    

    Kurt



  • ZuK schrieb:

    und mit

    params[count] = NULL;
    

    schreibst du über die arraygrenzen hinaus.
    also an besten

    params = (char**)malloc((paramcount + 1) * sizeof(char *));
    

    Kurt

    Danke. Das mit dem char statt char* war dumm von mir.

    Über die Arraygrenzen schreibe ich imho nicht herraus, da ich mir ein Feld mehr als Tokens mache.
    Zumindest in meinem aktuellen Code.
    dummy_fucker brauche ich damit params[0] nicht gelöscht wird.

    /* user_input kommt von readline */
    int count = 0;
    char* token = 0;
    char* dummy_fucker = 0;
    int paramcount = 0;
    /* - Get the count of parameters.
     * - create an new array of size parameters+1 to hold trailing NULL  
     * - tokenize throug user_input and copy every token into array. */ 
    paramcount = count_delim(user_input, delim) +1;
    params = (char**)malloc((paramcount) * sizeof(char*));
    
    count  = 0;
    token = strtok(user_input, delim);
    dummy_fucker = strdup(token);
    while(token != NULL) {
    	params[count] = (char*)malloc(strlen(token) +1);
    	memset(params[count], 0, strlen(token) +1);
    	strncpy(params[count], token, strlen(token));
    	token = strtok(NULL, delim);
    	++count;
    }
    params[count] = NULL;
    

    Also falls ich jetzt dennoch etwas falsch programmiert habe, dann bitte ich um Abhilfe.



  • hehejo schrieb:

    dummy_fucker brauche ich damit params[0] nicht gelöscht wird.

    ???
    Glaubst du wirklich an schwarze Magie ?
    Wenn du wirklich einen dummy_fucker brauchst dann ist da noch immer irgendwo der Hund drin.
    Kurt



  • ZuK schrieb:

    hehejo schrieb:

    dummy_fucker brauche ich damit params[0] nicht gelöscht wird.

    ???
    Glaubst du wirklich an schwarze Magie ?
    Wenn du wirklich einen dummy_fucker brauchst dann ist da noch immer irgendwo der Hund drin.
    Kurt

    Ja eben, das ist ja grad mein Problem.
    Benutze ich diesen "dummy_fucker" nicht, dann ist params[0] leer.
    Da steht nix mehr drin und das obwohl ich ja den token immer komplett kopiere.

    Darum möcht ich ja gerne wissen, was da falsch läuft.



  • Es gibt keine schwarze Magie.
    Wenn du den Fehler mit dem sizeof(char) ausbesserst dann laufts einwandfrei (auch ohne dummy_fucker).
    Kurt



  • Juhu, vielen Dank.
    Etz bin ich wieder glücklich.

    Ich hatte das zwar schon auf char* umgestellt, aber dann eben nicht mehr getestet ob's ohne den dummy_fucker auch geht.
    DANKE.


Anmelden zum Antworten