2010-09-20 18 views
1

Je suis curieux de savoir quelle est la bonne façon d'ajuster une chaîne pour éviter toute fuite de mémoire. Je suppose que cela peut vraiment être une question basée sur exactement comment fonctionne free(). J'ai inclus le code pour ma fonction trim(). Voir ci-dessous.Le rognage de chaînes provoque une fuite de mémoire?

int main() 
{ 
    char* testStr1 = strdup("some string"); 
    char* testStr2 = strdup(" some string"); 
    char* testStr3 = strdup("some string  "); 

    trim(&testStr1); 
    trim(&testStr2); 
    trim(&testStr3); 

    free(testStr1); // no memory leak 
    free(testStr2); // possible memory leak? 
    free(testStr3); // possible memory leak? 

    return 0; 
} 

int trim(char** pStr) 
{ 
if(pStr == NULL || *pStr == NULL) 
    return FAILURE; 
char* str = *pStr; 
while(isspace(*str)) { 
    (*pStr)++; 
    str++; 
} 

if(*str == 0) { 
    *pStr = str; 
    return SUCCESS; 
} 

char *end = str + strlen(str) - 1; 
while(end > str && isspace(*end)) 
    end--; 
*(end+1) = 0; 

*pStr = str; 
return SUCCESS; 
} 

Répondre

5

Oui, cela causera une fuite de mémoire, mais pire, cela provoque un comportement indéfini. Puisque trim modifie les variables de pointeur, main passe un pointeur vers free qui n'a pas été retourné par malloc. C'est un comportement non défini, et il va corrompre le tas sur de nombreuses implémentations.

Il y a au moins trois façons correctes de gérer cela.

1.   et dotés d'une garniture allouent retourner une nouvelle chaîne, et faire l'appelant responsable de la libération du nouveau, ainsi que l'ancien (si nécessaire):

char *trim(char *orig); 
// ... 
char *trimmed1 = trim(testStr1); 
free(testStr1); 
// ... 
free(trimmed1); 

2.   Laissez l'appelant allouer une nouvelle chaîne de même longueur (comme conservateur), et de transmettre les deux pointeurs dans

int trim(char *orig, char *new); 
// ... 
char *trimmed1 = malloc(strlen(testStr1) + 1); 
trim(testStr1, trimmed1); 
free(testStr1); 
// ... 
free(trimmed1); 

3.   Couper la corde en place, le déplaçant à gauche.

| | |t|r|im| | |\0|-> 
|t|r|i|m|\0| 

int *trim(char *orig); 
trim(testStr1); 
// ... 
free(testStr1); 
+0

+1 pour # 3. La fonction spécifiée gère le problème pour le code client, au lieu d'introduire de nouvelles acrobaties. – grossvogel

+2

Il est intéressant de noter que 'memmove()' est utile pour implémenter l'option # 3. – caf

14

Le pointeur que vous passez à free a besoin d'être exactement le même pointeur que vous avez reçu de malloc (ou calloc ou realloc), non seulement un pointeur dans la région de mémoire malloc retourné. En tant que tel, votre deuxième chaîne est celle qui cause un problème. Votre premier et troisième sont bien, car le pointeur que vous passez à free correspond à celui que vous avez reçu de malloc (via strdup). Ce que vous obtenez dans ce cas, cependant, n'est pas vraiment une fuite de mémoire - c'est un comportement indéfini.

+0

+1. Habituellement, vous obtenez la corruption de tas qui rend toutes les inquiétudes au sujet des fuites de mémoire légèrement exagérées. Une question C++ liée: http://stackoverflow.com/questions/1913343/how-could-pairing-new-with-delete-possibly-lead-to-memory-leak-only – sharptooth

0

Ce n'est pas vraiment une réponse à la façon dont fonctionne libre, mais je voudrais faire quelque chose le long de ces lignes:

char * trim_realloc (char * str) { char * p = str; char * e; char * ne; // nouvelle fin char * r; size_t len;

// Since you put this level of error testing in your program 
if (!str) { 
    return str; // str is NULL 
} 

while (*p || isspace(*p)) { 
    p++; 
} 

len = strlen(p); 
e = p + len; 

ne = e; 

while (ne > p) { 
    if (isspace(*ne)) { 
     *ne = 0; 
     ne--; 
    } else { 
     break; 
    } 
} 


if (p == str) { 
    if (e != ne) { 
     return realloc(str, len+1); // only tail trim -- you could just return str here 
    } else { 
     return str; // no actual trim 
    } 
} else { 
    r = strdup(p); 
    free(str); // str is the head of the string, so that's what we have to free 
    return r; 
} 

}

Il est à noter mon commentaire sur la ligne avec realloc Depuis que je remis à zéro l'espace arrière de toute façon (et depuis de nombreuses implémentations de realloc ne vous inquiétez pas à propos de « est-il assez grand », et non « est là aussi beaucoup d'espace supplémentaire ") vous pourriez avoir juste laisser le tampon que votre chaîne a vécu prendre trop de place à la fin. Il est toujours terminé au bon endroit (à moins qu'il y ait des bogues dans mon code non testé, ce qui pourrait être le cas).

D'autres choses que vous pourriez faire serait de déplacer simplement la chaîne au début du tampon, puis couper la queue, de sorte que:

" cat " 

est passé par les étapes:

« c chat " "cat ca" "catcat" "chat à" "chat t" "chat"

avant de commencer à couper la queue.

Maintenant, revenons à la façon dont free works - free doit être passé à la valeur NULL ou à la valeur que l'une des fonctions d'allocation de tas vous est passée. Certaines bibliothèques d'allocation de tas sont implémentées de sorte que lorsque malloc alloue des données, la taille de ce bloc de données est stockée dans les octets juste avant l'adresse que retourne malloc, et lorsque vous appelez gratuitement les octets juste en face de ce pointeur sont utilisés pour déterminer la taille de ce morceau de mémoire est réellement. Si vous passez le quelque chose qui n'a pas été retourné par malloc (ou calloc, ou realloc, ou similaire) alors libre peut regarder au mauvais endroit et utiliser tout ce qu'il trouve là comme la taille du morceau que vous libérez - et rien de bon vient de cela.

0

Vous n'avez pas besoin malloc supplémentaire/realloc/... dans l'assiette, comme:

char *trim(char *s) 
{ 
    while(isspace(*s)) 
    memmove(s, s+1, strlen(s)); 
    while(*s && isspace(s[strlen(s)-1])) 
    s[strlen(s)-1] = 0; 
    return s; 
} 

Pas rapide mais sûr, sans ne manque jamais pour vos exemples, parce que s pas changé. Seul le contenu peut changer.