Problem mit Funktion

Tommy57

Erfahrenes Mitglied
Hallo,

ich habe hier eine Funktion geschrieben, mit der ich einen Text in Array umwandel. Leider funktioniert das nicht richtig. Scheinbar existiert der Rückgabewert nur so lange, wie result existiert, oder so ähnlich. Kann mir jemand helfen?

Code:
static void explode(char *delimiter, char *string) {
    char* result[18];
    int i = 0;
    
    while((string = strtok(string, delimiter)) != NULL) {
        result[i] = string;
        string = NULL;
        i++;
    }
    
    return result;
}
 

cwriter

Erfahrenes Mitglied
wie result existiert, oder so ähnlich. Kann mir jemand helfen?
Du versuchst zwar 'result' zurückzugeben, aber Deine Funktion hat den Rückgabewert VOID !!!
Das wäre das erste. Die anderen Probleme:
Du gibst result zurück, was auf dem Stack der Funktion liegt. Wenn die Funktion endet, wird der Stackframe aber invalidiert, und damit auch das Resultat.
Was passiert, wenn mehr als 18 Teilstücke existieren?
strtok() hat viele Probleme und sollte eigentlich vermieden werden. Alternativen sind strchr() oder einfach selbst durch die Characters durchgehen.

Lösungen für dein Problem: Erstelle die Speicherplatz für das Resultat nicht in der Funktion, sondern ausserhalb, gibt der Funktion also einen Pointer mit, wohin sie das Resultat speichern soll. Empfehlenswerterweise auch mit einem size_t, das die maximale Anzahl Elemente angibt. Alternativ erstellst du den Speicherplatz mit malloc()/calloc() auf dem Heap, wobei du dich dann aber wieder ums Aufräumen kümmern musst.

Gruss
cwriter
 

Tommy57

Erfahrenes Mitglied
Danke für die Antworten.

Das void war nur ein Kopierfehler. Hab sehr viel Zeit mit der Funktion verbracht. Nur jedes Mal ohne Erfolg.

cwriter, ich glaube, du hast mein Problem auf den Punkt gebracht. Weil im Rückgabewert index 0 existiert und bei index 1 kriege ich eine Fehlermeldung, obwohl da in der Funktion noch was drinnen ist.

Könntest du mir kurz zeigen, was ich wo ändern muss, bzw das mit den 1-3 Sternchen verstehe ich auch noch nicht so ganz, wodurch ich da meist mit try und error mich durchquäle.

Gruß, Tommy
 

cwriter

Erfahrenes Mitglied
Könntest du mir kurz zeigen, was ich wo ändern muss, bzw das mit den 1-3 Sternchen verstehe ich auch noch nicht so ganz, wodurch ich da meist mit try und error mich durchquäle.
Naja, die allgemein beste Lösung wäre:
C:
static char** explode(char *delimiter, char *string, char** out, size_t outsize) {
    int i = 0;
   
    while((string = strtok(string, delimiter)) != NULL) {
        if(i >= outsize)
            return NULL;

        out[i] = string;
        string = NULL;
        i++;
    }
   
    return out;
}

// Aufruf:
char* result[18];
explode(delim, string, result, 18);
Halt immer noch mit strtok().

Ansonsten:
C:
static char** explode(char *delimiter, char *string) {
    char** result = (char**)malloc(sizeof(char*) * 18);
    if(result == NULL) return NULL;
    int i = 0;
   
    while((string = strtok(string, delimiter)) != NULL) {
        result[i] = string;
        string = NULL;
        i++;
    }
   
    return result;
}
free() aber nicht vergessen. Da das oft vergessen geht, ist der 1. Weg vorzuziehen.

Gruss
cwriter
 

Bratkartoffel

gebratene Kartoffel
Premium-User
Hi,

@cwriter Wenn du eh schon mit malloc() arbeitest, wäre dann ein realloc() nicht besser? Ansonsten bekommst du da einen segfault wenn du über 18 Einträge hast.

Eine "vollständige" Implementierung würde mMn so aussehen:
C:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static char** explode(const char *delimiter, char *string) {
    int pos = 0, len = 8;
    // das array initial auf "len" elemente setzen
    // durch calloc() werden die einzelnen elemente mit NULL initialisiert
    char** result = calloc(sizeof(void*), len);
    if(result == NULL) {
        fprintf(stderr, "malloc failed\n");
        return NULL;
    }
    // den eingabestring bis zum ende zerlegen
    while((string = strtok(string, delimiter)) != NULL) {
        // die elemente sind ja von 0 - (len-1) indiziert
        // -2 deswegen, weil das letzte element NULL sein MUSS
        if(pos >= len - 2) {
            // wir verdoppeln mit jeder vergrösserung das array
            // 8, 16, 32, 64 ...
            len *= 2;
            char** newResult = realloc(result, sizeof(char*) * len);
            if (newResult == NULL) {
                fprintf(stderr, "realloc failed\n");
                free(result);
                return NULL;
            }
            result = newResult;
            // die neu verfügbaren elemente müssen mit NULL initialisiert werden
            // somit stellen wir sicher, dass das zurückgegebene array mit
            // mindestens einem NULL endet
            for(int i = pos; i < len; i++) result[i] = NULL;
        }
        // den aktuellen teil speichern
        result[pos++] = string;
        // string für strtok() auf NULL setzen
        string = NULL;
    }
    return result;
}

int main(int argc, const char** argv) {
    const char* delimiter = " ";
    char* string = strdup("dies ist ein einfacher test mit realloc ob alles sauber funktioniert. das ergebnis ist dann 64 elemente gross, wobei halt nur 46 belegt sind. dies ist aber kein problem, da die aufrufene funktion ja bis zum ersten NULL läuft. man beachte auch das free am ende");
    char** temp = explode(delimiter, string);

    if(temp == NULL) {
        fprintf(stderr, "explode failed\n");
        return 1;
    }

    // die einzelnen elemente ausgeben
    for(int i = 0; temp[i] != NULL; i++)
        fprintf(stdout, "Found %d: %s\n", i, temp[i]);

    free(string); // wegen strdup
    free(temp);
    return 0;
}

Grüsse,
BK
 
Zuletzt bearbeitet:

cwriter

Erfahrenes Mitglied
Wenn du eh schon mit malloc() arbeitest, wäre dann ein realloc() nicht besser? Ansonsten bekommst du da einen segfault wenn du über 18 Einträge hast.

Eine "vollständige" Implementierung würde mMn so aussehen:
Du hast natürlich völlig Recht - allerdings ist zweifelsfrei auch die Komplexität stark gestiegen, und das Problem der Ownership besteht. Da ich ohnehin die erste Variante favorisiere, wollte ich nicht viel Energie in die 2. stecken.

Gruss
cwriter
 

Tommy57

Erfahrenes Mitglied
Wooow Leute. Das ist ja Wahnsinn. Werde die gleich mal testen. Echt klasse. Vielen Dank für die Hilfe.
 

Technipion

Erfahrenes Mitglied
Möchte hier nur kurz anmerken, dass man im Zweifelsfall auch schnell im Voraus ermitteln kann, wie groß das Array sein muss.
Sind im String insgesamt n delimiter enthalten, wird er von der Funktion in n+1 Substrings gesplittet.

Gruß Technipion