2016-07-13 13 views
4

Ich versuche, all den allozierten Speicher von malloc() freizugeben, realloc() aber valgrind sagt, dass das ein Speicherleck ist.Geben Sie den gesamten von malloc() zugewiesenen Speicher frei. Realloc() in C

Der Code:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 


int main(int argc, char *argv[]) { 

    int lines_allocated = 128; 
    int max_line_len = 50; 

    FILE *fp; 

    /* File allocate lines of text */ 
    char **array = (char **)malloc(sizeof(char*)*lines_allocated); 
    if (array==NULL) { 
     fprintf(stderr,"Out of memory (1).\n"); 
     exit(1); 
    } 

    FILE *file = fopen("file.txt", "r"); 
    if (file == NULL) { 
     fprintf(stderr,"Error opening file.\n"); 
     exit(2); 
    } 

    int il; 
    for (il = 0; 1; il++) { 
     int j; 

     /* Have we gone over our line allocation? */ 
     if (il >= lines_allocated) { 
      int new_size; 

      /* Double our allocation and re-allocate */ 
      new_size = lines_allocated*2; 
      array = (char **)realloc(array,sizeof(char*)*new_size); 

      if (array==NULL) { 
       fprintf(stderr,"Out of memory.\n"); 
       exit(3); 
      } 

      lines_allocated = new_size; 
     } 

     /* Allocate space for the next line */ 
     array[il] = malloc(max_line_len); 
     if (array[il]==NULL) { 
       fprintf(stderr,"Out of memory (3).\n"); 
       exit(4); 
      } 
     if (fgets(array[il], max_line_len-1, file)==NULL) 
      break; 

     /* Get rid of CR or LF at end of line */ 
     for (j=strlen(array[il])-1;j>=0 && (array[il][j]=='\n' || array[il][j]=='\r');j--) 
      ; 

     array[il][j+1]='\0'; 
    } 

    /* Close file */ 
    fclose(file); 

    /* Print and free the every element of the array */ 
    int cc; 
    for (cc = 0; cc < il; cc++) { 
     printf("%s\n", array[cc]); 

     /* Free the every element of the array */ 
     free(array[cc]); 
    } 

    /* Free hole array */ 
    free(array); 

    return 0; 
} 

valgrind ./main

valgrind --leak-check=full --show-reachable=yes ./main 
==4806== Memcheck, a memory error detector 
==4806== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. 
==4806== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info 
==4806== Command: ./main 
==4806== 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
==4806== 
==4806== HEAP SUMMARY: 
==4806==  in use at exit: 50 bytes in 1 blocks 
==4806== total heap usage: 14 allocs, 13 frees, 2,192 bytes allocated 
==4806== 
==4806== 50 bytes in 1 blocks are definitely lost in loss record 1 of 1 
==4806== at 0x4C2AC3D: malloc (vg_replace_malloc.c:299) 
==4806== by 0x40092E: main (in /var/www/mem/main) 
==4806== 
==4806== LEAK SUMMARY: 
==4806== definitely lost: 50 bytes in 1 blocks 
==4806== indirectly lost: 0 bytes in 0 blocks 
==4806==  possibly lost: 0 bytes in 0 blocks 
==4806== still reachable: 0 bytes in 0 blocks 
==4806==   suppressed: 0 bytes in 0 blocks 
==4806== 
==4806== For counts of detected and suppressed errors, rerun with: -v 
==4806== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 

Wie die Speicher korrekt zu befreien? Es besagt, dass es einen weiteren Speicherblock geben sollte, aber wo ist er?

+4

[Bitte sehen Sie diese Diskussion darüber, warum nicht den Rückgabewert von 'malloc()' und Familie in 'C' zu werfen. ] (http://stackoverflow.com/q/605845/2173917). –

Antwort

10
for (cc = 0; cc < il; cc++) { 

Wenn il ein gültiger Index von array ist (und es ist), sollte der Vergleich in der Schleife sein:

for (cc = 0; cc <= il; cc++) { 

, um die allerletzten Element array (und frei zu triggern ihre Erinnerung).

5

Ersetzen Sie einfach

for (cc = 0; cc < il; cc++) 

mit

for (cc = 0; cc <= il; cc++) 

, um das herauszufinden, vorstellen, was Schleife for (il = 0; 1; il++) iteriert, wenn die Zuweisung nur einmal passiert. In diesem Fall erreicht die Steuerung il++ nicht, so dass il Null bleibt und for (cc = 0; cc < il; cc++) nullmal iteriert. Im allgemeinen Fall macht die Freigabeschleife eine Iteration weniger als die Zuweisungsschleife.

3

Ihr Code verliert die letzte Zuweisung, weil il nie inkrementiert wird, wenn fgets(array[il], max_line_len-1, file)NULL zurückgibt.

Moving array[il] = malloc(max_line_len); zusammen mit seiner NULL Prüfung nach fgets wird dieses Problem beheben. Ein zusätzlicher Vorteil dieses Ansatzes besteht darin, dass Sie Ihre Zuweisungen in der genauen Größe vornehmen können, anstatt sie unter max_line_len zuzuordnen.

// Check that we will need the allocation 
char temp[max_line_len]; 
if (fgets(temp, max_line_len-1, file)==NULL) { 
    break; 
} 
// Allocate only when we are sure that we are going to need it 
temp[max_line_len-1] = '\0'; 
size_t len = strlen(temp); 
array[il] = malloc(len+1); 
if (array[il]==NULL) { 
    fprintf(stderr,"Out of memory (3).\n"); 
    exit(4); 
} 

Anmerkung: realloc Zuweisen an die Variable, könnte wieder in die undichten Speicher umverteilt führen, die auf diese Variable zuvor zugeordnet wurde. Es ist kein Problem in Ihrem Code, weil Sie sofort exit(4) aufrufen, aber Sie sollten sich des allgemeinen Problems mit dieser Aufgabe bewusst sein.

2

Wenn Sie verschiedene Probleme haben, und Vermischungs Anrufe zu verschiedenen Verteilern und sind furchtbar persnickety über Dinge, implementieren dann eine Wrapper für die verschiedenen Speicherverteilern (dies geschickt gemacht werden können Makros), die speichert die Adresse des neu zugewiesenen Puffers irgendwo (z. B. auf einem Stapel) und dann - irgendwann - navigiert der Stapel und befreit alle. Do not zufällig zufällig Anrufe hier und da zu free() oder seine Analoga mischen. Wenn etwas frei ist, überschreiben Sie das Objekt mit Null, so dass Sie nicht versehentlich versuchen, free() es ein zweites Mal, die free() verärgert.

WIE GENIAL Makros (ich sagte es, so würde ich besser es jetzt funktioniert) und vermeiden rekursive Probleme:

des malloc() als unser erstes Opfer verwenden lassen. Erstellen Sie in einer anderen Quelldatei eine Funktion _malloc(), die malloc() aufruft.

in der Quelldatei, die alle Ihre Speicherzuweisung und Aufhebung der Zuordnung enthält, definieren malloc() wie folgt:

#define malloc(n) (*sp++ = _malloc(n)) 

Es Code als Präambel aufgerufen werden muss, die sp an seiner Basis einen Stapel und Punkte einrichtet. Machen Sie es schön und groß: Sie wären überrascht, wie oft malloc() und seine Brüder am Ende aufgerufen werden können. Zu verschiedenen Zeiten - wenn es angebracht ist, tatsächlich - rufen Sie Ihre eigene free_all(), das dies tut:

void free_all() { 
    while(--sp >= base_of_malloc_buf_stack) { 
    free(*sp); 
    *sp = 0; /* avoid future re-free() */  
    } 
}