Pourquoi pas de fuite de mémoire?

Ce qui suit est conçu pour prendre un caractère de longueur variable constante et l’imprimer dans un format agréable pour la journalisation. Je suis certain que les lecteurs auront des suggestions sur la manière d’améliorer cette situation, et j’en serais heureux.

Ce qui me laisse perplexe, c’est que je pensais qu’il serait nécessaire de libérer () le caractère statique renvoyé à chaque appel de ToHexSsortingng (). Au lieu de cela, je ne vois aucune fuite de mémoire. Même si j’utilise la fonction inline et n’assigne donc pas sa valeur de retour à une variable.

J’ai créé un test simple qui appelle cette fonction dans une boucle, chaque fois avec un paramètre de longueur différente, cSsortingng et nMaxChars. Ensuite, j’ai regardé l’état de la machine virtuelle. L’allocation de mémoire pour mon programme de test et la mémoire libre n’ont jamais changé.

Il me semble que cela aurait dû augmenter à chaque fois qu’un malloc est appelé et non gratuit.

static char *ToHexSsortingng(const char *cSsortingng,int nMaxChars) { static char *cStr; /*if (80>strlen(cSsortingng)) nRawChars=strlen(cSsortingng); 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 la routine d’appel:

 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)); } 

Ce qui suit est une fuite:

 static void memeat(void) { static char *foo = NULL; foo = malloc(1024); return; } 

Sortie Valgrind :

 ==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 où memeat() été entré. Le pointeur statique contenait toujours une référence valide au dernier bloc memeat() alloué à la sortie du programme. Juste pas les blocs précédents.

Ce qui suit n’est 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 indiquée par foo a été libérée et foo pointe maintenant sur l’adresse nouvellement allouée et continuera à le faire lors de la prochaine memeat() .

Explication:

Le type de stockage static indique que le pointeur foo pointera vers la même adresse que celle initialisée à chaque fois que la fonction est entrée. Cependant, si vous modifiez cette adresse chaque fois que la fonction est entrée via malloc() ou calloc() , vous perdez la référence aux blocs de l’allocation précédente. Par conséquent, une fuite, puisque l’un ou l’autre va renvoyer une nouvelle adresse.

‘Toujours accessible’ dans valgrind signifie que tous les blocs de tas alloués ont toujours un pointeur valide pour les accéder / les manipuler / les libérer à la sortie. Cela revient à allouer de la mémoire dans main() et à ne pas la libérer, il suffit de s’appuyer sur le système d’exploitation pour récupérer de 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, sauf si vous ajoutez un autre argument à votre fonction qui indique ToHexSsortingng à ToHexSsortingng d’appeler free le pointeur statique, que vous pouvez utiliser lors de la fermeture.

Similaire à ceci: (programme de test complet)

 #include  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 le résultat final :

Oui, 6144 octets ont été effectivement alloués en fonction de ce que malloc() renvoyé pendant l' memeat() du programme, mais cela signifie simplement que le pointeur statique a été libéré, puis réaffecté en fonction du nombre de fois où memeat() été entré. L’utilisation réelle du programme à un moment donné n’était en réalité que de 2 * 1024, 1k pour allouer le nouveau pointeur alors que l’ancien existait toujours et attendait d’être copié dans le nouveau.

Encore une fois, il ne devrait pas être trop difficile d’ajuster votre code, mais j’ignore pourquoi vous utilisez d’abord le stockage statique.

C’est une fuite de mémoire. Si vous appelez cette fonction de manière persistante, la mémoire utilisée par le programme augmente. Par exemple:

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

Si vous exécutez cette opération et observez le processus avec un outil de surveillance du système, vous constaterez que la mémoire utilisée augmente constamment.

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

J’aimerais souligner deux choses qui me traversent l’esprit lors de l’inspection du code:

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

Pourquoi n’avez-vous pas fait d’erreur en vérifiant ceci …. comme je peux voir dans le code, il y a un contrôle de zéro sur la mémoire de prise en charge sera toujours disponible … dangereux …. TOUJOURS vérifier le pointeur NULL au retour de un appel de fonction d’allocation de mémoire tel que calloc , malloc et realloc … il incombera TOUJOURS au programmeur de gérer la calloc des pointeurs pour les renvoyer dans le tas.

De plus, comme cStr déclaré comme un pointeur statique sur char * , vous ne le libérez pas du tout. En fait, cette ligne le prouve:

 printf ("Lire les caractères% i dans% s \ n% s \ n", nChars, cPort, ToHexSsortingng (cBuff, nChars));
                                                   ^^^^^^^^^^^

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

 char * hexedSsortingng;
 hexedSsortingng = ToHexSsortingng (cBuff, nChars);
 ....
 printf ("Lire les% i caractères de% s \ n% s \ n", nChars, cPort, hexedSsortingng);
 ....
 libre (hexedSsortingng);

Vous retournez 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. Ainsi, dans l’appelant, vous devriez stocker le résultat de votre routine dans un fichier temporaire, en faire ce que vous voulez, puis le libérer () à la fin.