2009-10-17 10 views
0

Y at-il quelque chose que je devrais savoir sur l'utilisation de strtok sur une chaîne malloced?Utilisation de strtok() sur une chaîne allouée?

Dans mon code, j'ai (en termes généraux)

char* line=getline(); 
Parse(dest,line); 
free(line); 

getline() est une fonction qui retourne un char * à une mémoire malloced. et Parse(dest, line) est une fonction qui effectue l'analyse en ligne, en stockant les résultats dans dest, (qui a été partiellement rempli plus tôt, à partir d'autres informations).

Parse() appels strtok() un nombre variable de fois sur la ligne, et fait une certaine validation. Chaque jeton (un pointeur vers ce qui est retourné par strtok()) est placé dans une file d'attente jusqu'à ce que je sache combien j'en ai.

Ils sont ensuite copiés sur un caractère malloc'd ** dans dest.

maintenant free(line) et une fonction libre de chaque partie du char * [] dans dest, les deux viennent sur valgrind que:

« Adresse 0x5179450 est de 8 octets dans un bloc de taille 38 libre » d "

ou quelque chose de similaire.

Je considère refactorisation mon code ne pas stocker directement les jetons sur le char **, mais la stocker au lieu une copie d'entre eux (par l'espace == pour allouer de strlen (jeton) +1, puis en utilisant strcpy()).

+0

Je crois que: http://stackoverflow.com/questions/1495368/strtok-and-memory-leaks, va un peu whay pour répondre à ma question. –

+0

strtok modifie de façon appressive les chaînes dansplace, en ajoutant des valeurs nulles là où se trouvent les suppressions. et renvoyant des pointeurs vers les départs de ces sections –

Répondre

2

Vous demandez:

Est-ce que je devrais savoir sur en utilisant strtok sur une chaîne malloced?

Il y a un certain nombre de choses à connaître. Tout d'abord, strtok() modifie la chaîne lors de son traitement, en insérant des valeurs nulles ('\0') où les délimiteurs sont trouvés. Ce n'est pas un problème avec la mémoire allouée (c'est modifiable!); c'est un problème si vous essayez de passer une chaîne constante à strtok().

Deuxièmement, vous devez avoir autant d'appels à free() comme vous le faites à malloc() et calloc() (mais realloc() peut gâcher avec le comptage).

Dans mon code, j'ai (en termes généraux)

char* line=getline(); 
    Parse(dest,line); 
    free(line); 

À moins Parse() alloue l'espace il garde, vous ne pouvez pas utiliser la structure dest (ou, plus précisément, les pointeurs dans la ligne dans la structure dest) après l'appel à free(). Le free() libère l'espace alloué par getline() et toute utilisation des pointeurs après cela génère un comportement indéfini. Notez que le comportement indéfini inclut l'option de 'semblant fonctionner, mais seulement par coïncidence'.

où getline() est une fonction qui renvoie un char * dans une certaine malloced mémoire, et Parse (dest, ligne) est une fonction qui ne l'analyse en ligne, stocker les résultats dans dest (qui a été partiellement rempli plus tôt, d'autres informations). Parse() appelle strtok() a une variable nombre de fois en ligne, et effectue quelques validations . Chaque jeton (un pointeur vers ce qui est retourné par strtok()) est mis en file d'attente jusqu'à ce que je sache combien j'ai .

Notez que les pointeurs retournés par strtok() sont tous les pointeurs dans la seule partie de l'espace alloué par getline(). Vous n'avez pas décrit d'allocation de mémoire supplémentaire.

Ils sont ensuite copiés sur un malloc 0d ** dans dest.

Cela sonne comme si vous copiez les pointeurs de strtok() dans un tableau de pointeurs, mais vous ne vous présentez pas à copier les données que ces pointeurs pointent à.

maintenant libre (ligne) et une fonction qui libre de chaque partie du char * [] dans dest, viennent tous deux sur valgrind comme:

"Address 0x5179450 is 8 bytes inside a block of size 38 free'd" 

ou quelque chose de similaire.

La première free() de la partie « char *[] » de dest a probablement un pointeur vers line et libère donc le bloc entier de la mémoire. Tous les libres suivants sur les parties de dest essaient de libérer une adresse non retournée par malloc(), et valgrind essaie de vous le dire. L'opération free(line) échoue ensuite car le premier free() des pointeurs dans dest a déjà libéré cet espace.

Je songe à refactoriser mon code [à] en stocker une copie [...].

Le refactoring proposé est probablement sensible; la fonction strdup() déjà mentionnée par d'autres fera le travail proprement et de manière fiable.

Notez qu'après le refactoring, vous devrez toujours libérer la ligne, mais vous ne pourrez pas libérer les pointeurs renvoyés par strtok(). Ils sont juste des pointeurs dans l'espace géré par (identifié par) line et seront tous libérés lorsque vous publiez line.

Notez que vous devrez libérer chacune des chaînes, ainsi que le tableau de pointeurs de caractères qui sont attribués séparément (accès strdup() « d) par l'intermédiaire dest.

Sinon, ne libérez pas la ligne immédiatement après avoir appelé Parse(). Avoir dest enregistrer le pointeur alloué (line) et libérer cela quand il libère le tableau de pointeurs. Cependant, vous ne libérez toujours pas les pointeurs renvoyés par strtok().

2

ils sont ensuite copiés sur un caractère malloc'd ** dans dest.

Les chaînes sont copiées ou les pointeurs sont copiés? La fonction strtok modifie la chaîne que vous lui donnez afin qu'elle puisse vous donner des pointeurs dans cette même chaîne sans rien copier. Quand vous obtenez des jetons, vous devez les copier. Soit cela ou garder la chaîne d'entrée aussi longtemps que l'un des pointeurs de jetons sont utilisés.

Beaucoup de gens vous recommandent d'éviter strtok tout à fait parce qu'il est sujette aux erreurs. En outre, si vous utilisez des threads et que le CRT n'est pas sensible au thread, strtok risque de bloquer votre application.

+1

+1 pour éviter strtok() tout à fait. –

2

Il y a une fonction strdup qui alloue de la mémoire puis copie une autre chaîne en elle.

+0

Je suis maintenant strdup'ing chaque jeton, avant de stocker. Je n'ai pas d'erreurs de libération de mémoire maintenant, et la vie est bonne. –

+0

Si cela répond à votre question, veuillez cliquer sur le bouton "Accepter". Ce serait bien de le faire pour les autres questions que vous avez posées ici. Vous pouvez l'annuler plus tard si une meilleure réponse se présente. –

+1

Pas une fonction C standard cependant. – AnT

0

1 dans votre parse(), strtok() écrit que '\ 0' à chaque position correspondant. En fait, cette étape n'a rien de spécial. L'utilisation de strtok() est facile. Bien sûr, il ne peut pas être utilisé sur le tampon de mémoire en lecture seule.

2 pour chaque sous-chaîne de caractères obtenu en parse(), le copier dans une mémoire tampon de ed malloc() en conséquence.si je donne un exemple simple pour stocker les sous-chaînes, il semble que le code ci-dessous, par exemple sur le plan conceptuel, mais il pourrait ne pas être exactement le même que votre code réel:

 
    char **dest; 
    dest = (char**)malloc(N * sizeof(char*)); 
    for (i: 0..N-1) { 
     dest[i] = (char*)malloc(LEN); 
     strcpy(dest[i], sub_strings[i]); 
    NOTE: above 2 lines could be just one line as below 
     dest[i] = strdup(sub_string[i]); 
    } 

3 dest libre, sur le plan conceptuel nouveau:

 
    for (i: 0..N-1) { 
     free(dest[i]); 
    } 
    free(dest); 

4 sans appel (ligne) est rien de spécial, et il ne touche pas votre « dest » même un peu. "Dest" et "ligne" utilisent différents tampons de mémoire, de sorte que vous pouvez effectuer l'étape 4 avant l'étape 3 si vous préférez. Si vous avez suivi les étapes ci-dessus, aucune erreur ne se produirait. semble que vous avez fait des fautes à l'étape 2 de votre code.

Questions connexes