2010-06-19 21 views
4

Ce qui suit est conçu pour prendre un caractère constant de longueur variable et l'imprimer dans un format agréable pour la journalisation. Je suis certain que les lecteurs auront des suggestions sur la façon dont cela peut être amélioré, et je m'en réjouis. Ce qui me laisse perplexe, c'est que je m'attendais à ce qu'il soit nécessaire de libérer() le caractère statique retourné chaque fois que ToHexString() est appelé. Au lieu de cela, je ne vois aucune fuite de mémoire. Même si j'utilise la fonction inline et n'attribue donc pas sa valeur de retour à une variable.Pourquoi aucune fuite de mémoire?

J'ai créé un test simple qui appelle cette fonction dans une boucle, chaque fois avec un paramètre de longueur différente cString et nMaxChars. Puis j'ai regardé le statut de VM. L'allocation de mémoire pour mon programme de test et la mémoire libre n'ont jamais changé.

Il me semble qu'il aurait dû augmenter chaque fois qu'un malloc est appelé et non libre.

static char *ToHexString(const char *cString,int nMaxChars) 
{ 
    static char *cStr; 



    /*if (80>strlen(cString)) 
     nRawChars=strlen(cString); 
    if (nMaxChars>nRawChars) 
     nRawChars=nMaxChars; 
    */ 
    if (nMaxChars==0) 
     nMaxChars=80; 

    printf("There are %i chars\n",nMaxChars); 

    char *cStr1; 
    char *cStr2; 
    char *cStr3; 
    int nLen=nMaxChars*6; 
    cStr=calloc(nLen,sizeof(char)); 

    cStr1=calloc(10,sizeof(char)); 
    cStr2=calloc(nLen,sizeof(char)); 
    cStr3=calloc(nLen,sizeof(char)); 
    cStr1[0]='\0'; 
    cStr2[0]='\0'; 
    cStr3[0]='\0'; 
    int nC1=0; 
    int nRowCnt=0; 

    for (nC1=0;nC1<nMaxChars;nC1++) 
    { 
     ++nRowCnt; 
     if (cString[nC1]==0x00) 
      snprintf(cStr1,8,"[00] "); 
     else 
      snprintf(cStr1,8,"[%02x] ",(unsigned char)cString[nC1]); 

     if ((nRowCnt%8==0)) 
     { 
      snprintf(cStr3,nLen,"%s%s\n",cStr2,cStr1); 
     } 
     else 
      snprintf(cStr3,nLen,"%s%s",cStr2,cStr1); 

     snprintf(cStr2,nLen,"%s",cStr3); 
    } 
    snprintf(cStr,nLen,"%s",cStr3); 
    free(cStr1); 
    free(cStr2); 
    free(cStr3); 
    return(cStr); 
} 

Voici le programme appelant:

for (i=0;i<100;i++) 
{ 
    memset(&cBuff, 0,255); 
    printf("Reading %s now..\n",cPort); 
    while (sleep(1)==-1); 
    nChars=read(nPort, cBuff, 255); 
    //printf("Read %i chars from %s\n",nChars,cPort); 
    if (nChars<=0) 
     printf("Read 0 chars from %s\n",cPort); 
    else 
     printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars)); 
} 
+6

Votre résolution est probablement trop petite. Votre système d'exploitation alloue probablement 4-8k pages.À moins que vous libérez cela, vous avez une fuite, vous ne pouvez probablement pas le voir parce que ces allocations sont minuscules. –

+0

@Jason - vous avez probablement raison. Je pensais qu'un pointeur statique conserverait la même adresse (il le ferait, s'il appelait calloc une seule fois, puis réallouerait par la suite). –

+1

Exécutez ce code via valgrind, vous verrez la fuite. –

Répondre

10

Ce qui suit est une fuite:

static void memeat(void) 
{ 
     static char *foo = NULL; 

     foo = malloc(1024); 

     return; 

} 

Valgrind sortie:

==16167== LEAK SUMMARY: 
==16167== definitely lost: 4,096 bytes in 4 blocks 
==16167== indirectly lost: 0 bytes in 0 blocks 
==16167==  possibly lost: 0 bytes in 0 blocks 
==16167== still reachable: 1,024 bytes in 1 blocks 
==16167==   suppressed: 0 bytes in 0 blocks 
==16167== Rerun with --leak-check=full to see details of leaked memory 

Remarque, still reachable (1024 octets) est le résultat de la dernière fois que memeat() a été saisi. Le pointeur statique contenait toujours une référence valide au dernier bloc memeat() alloué lorsque le programme est sorti. Juste pas les blocs précédents.

Voici PAS une fuite:

static void memeat(void) 
{ 
     static char *foo = NULL; 

     foo = realloc(foo, 1024); 

     return; 

} 

sortie Valgrind:

==16244== LEAK SUMMARY: 
==16244== definitely lost: 0 bytes in 0 blocks 
==16244== indirectly lost: 0 bytes in 0 blocks 
==16244==  possibly lost: 0 bytes in 0 blocks 
==16244== still reachable: 1,024 bytes in 1 blocks 
==16244==   suppressed: 0 bytes in 0 blocks 
==16244== Rerun with --leak-check=full to see details of leaked memory 

Ici, l'adresse foo pointé a été libéré, et foo pointe maintenant à l'adresse nouvellement allouée , et continuera à le faire la prochaine fois que memeat() est entré.

Explication:

Le type de stockage static dit que le pointeur foo pointera à la même adresse que initialisé chaque fois que la fonction est entrée. Toutefois, si vous modifiez cette adresse à chaque fois que la fonction est entrée via malloc() ou calloc(), vous avez perdu la référence aux blocs de l'allocation précédente. Par conséquent, une fuite, puisque l'un ou l'autre va retourner une nouvelle adresse. 'Still Reachable' dans valgrind signifie que tous les blocs segmentés alloués ont toujours un pointeur valide pour accéder/manipuler/les libérer à la sortie. Ceci est similaire à l'allocation de mémoire dans main() et ne libère pas, tout en s'appuyant sur le système d'exploitation pour récupérer la mémoire.

En bref, oui - vous avez une fuite. Cependant, vous pouvez le réparer assez facilement.Notez simplement que vous comptez sur votre système d'exploitation pour récupérer la mémoire à moins que vous ajoutiez un autre argument à votre fonction qui indique simplement ToHexString d'appeler gratuitement sur le pointeur statique, que vous pourriez utiliser en quittant.

similaires à ceci: (programme de test complet)

#include <stdlib.h> 

static void memeat(unsigned int dofree) 
{ 
     static char *foo = NULL; 

     if (dofree == 1 && foo != NULL) { 
       free(foo); 
       return; 
     } 

     foo = realloc(foo, 1024); 

     return; 

} 


int main(void) 
{ 
     unsigned int i; 

     for (i = 0; i < 5; i ++) 
       memeat(0); 

     memeat(1); 
     return 0; 
} 

sortie Valgrind:

==16285== HEAP SUMMARY: 
==16285==  in use at exit: 0 bytes in 0 blocks 
==16285== total heap usage: 6 allocs, 6 frees, 6,144 bytes allocated 
==16285== 
==16285== All heap blocks were freed -- no leaks are possible 

Note sur la sortie finale:

Oui, 6144 octets ont été effectivement attribués selon à ce malloc() retourné pendant que le programme a couru, mais cela signifie simplement que le pointeur statique a été libéré, puis réalloué en fonction du nombre de fois memeat() a été entré. L'utilisation réelle du tas à un moment donné était en réalité juste 2 * 1024, 1k pour allouer le nouveau pointeur alors que l'ancien existait toujours en attente d'être copié sur le nouveau.

Encore une fois, il ne devrait pas être trop difficile d'ajuster votre code, mais il n'est pas clair pour moi pourquoi vous utilisez le stockage statique pour commencer.

+0

Tim, Est-ce que realloc() est nécessaire alors si je fais la technique libre que vous suggérez (memeat (1))? J'utilise le statique car j'ai eu des expériences où le char * var ordinaire est clobé. –

+0

@ k1mgy - oui, ou vous devez libérer alors calloc() le pointeur statique si ce n'est pas null. Sinon, vous perdez la référence à ce qui a été attribué la dernière fois que la fonction a été entrée. Bien que vous n'ayez pas besoin de libérer explicitement les blocs qui peuvent encore être atteints à la sortie, il est toujours bon de le faire, ce qui signifie que certains moyens de libérer le pointeur statique avant de quitter seraient idéaux. –

+0

@ k1mgy - De même, si vous passez la route "libre sinon null" lorsque la fonction est entrée, veillez à initialiser le pointeur statique une fois libéré à chaque fois, sinon vous risquez toujours de finir par fuir à cause d'un pointeur sauvage . –

0

Vous renvoyez le résultat de votre routine dans un nouveau tableau. Avec votre modèle, la responsabilité de libérer ce tableau avec le résultat est dans l'appelant. Donc, là, dans l'appelant, vous devriez stocker le résultat de votre routine dans un temporaire, faire ce que vous voulez avec, puis libre() à la fin.

1

Il s'agit d'une fuite de mémoire. Si vous appelez constamment la fonction, la mémoire utilisée par le programme augmente. Par exemple:

int main() { 
    while (1) { 
     ToHexString("testing the function", 20); 
    } 
} 

Si vous exécutez cela et regarder le processus avec un outil de surveillance du système, vous verrez que la mémoire utilisée est en constante augmentation.

La fuite n'est probablement pas évidente dans votre programme car la fonction ne fuit que quelques octets à chaque appel et n'est pas appelée très souvent. Ainsi, l'augmentation de l'utilisation de la mémoire n'est pas très perceptible.

1

Je voudrais souligner deux choses qui me traverse l'esprit lors de l'inspection du code:

 
cStr=calloc(nLen,sizeof(char)); 

Pourquoi vérifier avez-vous pas faire erreur sur cette .... que je peux voir à partir du code, il est égal à zéro contrôle sur la mémoire prise en charge sera toujours disponible .... dangereux .... tOUJOURS chèque NULL pointeur sur le retour d'un appel de fonction d'allocation de mémoire tels que calloc, malloc et realloc ... il va TOUJOURS être le fardeau sur le programmeur pour gérer le free 'des pointeurs pour les renvoyer au tas.

Aussi, parce que vous avez cStr déclaré comme pointeur statique char *, vous n'êtes pas affranchissant du tout, en fait, cette ligne le prouve:

 
printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars)); 
                ^^^^^^^^^^^ 

Vous seriez mieux de le faire de cette façon :

 
char *hexedString; 
hexedString = ToHexString(cBuff, nChars); 
.... 
printf("Read %i chars from %s\n%s\n",nChars,cPort,hexedString); 
.... 
free(hexedString); 
+0

Merci tommieb75. J'ai été plutôt cavalier avec malloc et calloc. Pas plus :) –