2010-09-15 26 views
5

gcc 4.4.4 c89no se puede liberar la memoria

Tengo la siguiente función pero no puedo liberar la memoria. El mensaje que recibo en Valgrind sospecha de la función getline. Sin embargo, estoy libre del puntero al final de la función. Entonces no puede ser eso.

Tengo una matriz global de punteros a char 'candidates_names'. Sin embargo, no he asignado ningún recuerdo para eso.

Muchas gracias por cualquier consejo,

El mensaje que recibo en valgrind es la siguiente.

HEAP SUMMARY: 
==4021==  in use at exit: 840 bytes in 7 blocks 
==4021== total heap usage: 22 allocs, 15 frees, 1,332 bytes allocated 
==4021== 
==4021== Searching for pointers to 7 not-freed blocks 
==4021== Checked 48,412 bytes 
==4021== 
==4021== 840 bytes in 7 blocks are still reachable in loss record 1 of 1 
==4021== at 0x4005BDC: malloc (vg_replace_malloc.c:195) 
==4021== by 0xAAE38D: getdelim (iogetdelim.c:68) 
==4021== by 0xAAADD2: getline (getline.c:34) 
==4021== by 0x804892B: load_candidates (candidate.c:61) 
==4021== by 0x8048686: main (driver.c:24) 

mi código fuente:

#define NUMBER_OF_CANDIDATES 7 
static char *candidate_names[NAME_SIZE] = {0}; 

int load_candidates() 
{ 
    FILE *fp = NULL; 
    size_t i = 0; 
    ssize_t read = 0; 
    size_t n = 0; 
    char *found = NULL; 

    fp = fopen("votes.txt", "r"); 

    /* open the votes file */ 
    if(fp == NULL) { 
     fprintf(stderr, "Cannot open votes file [ %s ]\n", strerror(errno)); 
     return FALSE; 
    } 

    /* fill the structure with candidates */ 
    for(i = 0; i < NUMBER_OF_CANDIDATES;) { 
     read = getline(&candidate_names[i], &n ,fp); 
     if(read == -1) { 
      fprintf(stderr, "Cannot read candidate [ %d ] [ %s ]\n", 
        i, strerror(errno)); 
      candidate_names[i] = "Invalid candidate"; 
      i++; 
      continue; 
     } 
     /* Just ignore the key work in the text file */ 
     if(strcmp("CANDIDATES\n", candidate_names[i]) != 0) { 
      /* Find and remove the carriage return */ 
      found = strchr(candidate_names[i], '\n'); 
      if(found != NULL) { 
       /* Remove the carriage return by nul terminating */ 
       *found = '\0'; 
      } 
      i++; 
     } 
    } 

    fclose(fp); 

    return TRUE; 
} 

EDITAR ========= LIBERAR candidate_names ======

All heap blocks were freed -- no leaks are possible 
==4364== 
==4364== ERROR SUMMARY: 84 errors from 2 contexts (suppressed: 12 from 8) 
==4364== 
==4364== 42 errors in context 1 of 2: 
==4364== Invalid free()/delete/delete[] 
==4364== at 0x40057F6: free (vg_replace_malloc.c:325) 
==4364== by 0x8048A95: destroy_candidate (candidate.c:107) 
==4364== by 0x8048752: main (driver.c:44) 
==4364== Address 0x401e1b8 is 0 bytes inside a block of size 120 free'd 
==4364== at 0x40057F6: free (vg_replace_malloc.c:325) 
==4364== by 0x8048A95: destroy_candidate (candidate.c:107) 
==4364== by 0x8048752: main (driver.c:44) 
==4364== 
==4364== 
==4364== 42 errors in context 2 of 2: 
==4364== Invalid read of size 1 
==4364== at 0x400730E: strcmp (mc_replace_strmem.c:426) 
==4364== by 0x8048A7F: destroy_candidate (candidate.c:106) 
==4364== by 0x8048752: main (driver.c:44) 
==4364== Address 0x401e1b8 is 0 bytes inside a block of size 120 free'd 
==4364== at 0x40057F6: free (vg_replace_malloc.c:325) 
==4364== by 0x8048A95: destroy_candidate (candidate.c:107) 
==4364== by 0x8048752: main (driver.c:44) 


void destroy_candidate() 
{ 
    size_t i = 0; 
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) { 
     if(strcmp(candidate_names[i], "Invalid candidate") != 0) { 
      free(candidate_names[i]); 
     } 
    } 
} 

edición con código de main.c =====================

typedef struct Candidate_data_t { 
    size_t candidate_data_id; 
    Candidates_t *candidate; 
} Candidate_data; 

static Candidate_data* create_candidate_data(Candidates_t *candidate, size_t i); 
static void destroy_candidata_data(Candidate_data *cand_data); 

int main(void) 
{ 
    Candidates_t *candidate = NULL; 
    Candidate_data *cand_data[NUMBER_OF_CANDIDATES] = {0}; 
    size_t i = 0; 

    load_candidates(); 

    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) { 
     candidate = create_candidates(i); 
     if(candidate == NULL) { 
      fprintf(stderr, "Cannot failed to initalize candidate [ %d ]\n", i); 
     } 

     /* Create array of candidates */ 
     cand_data[i] = create_candidate_data(candidate, i); 
     fill_candidates(cand_data[i]->candidate); 
    } 

    /* Display each candidate */ 
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) { 
     display_candidate(cand_data[i]->candidate); 
     printf("\n"); 
    } 

    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) { 
     destroy_candidata_data(cand_data[i]); 
    } 

    return 0; 
} 

static Candidate_data* create_candidate_data(Candidates_t *candidate, size_t id) 
{ 
    Candidate_data *cand_data = NULL; 

    cand_data = malloc(sizeof *cand_data); 

    if(cand_data == NULL) { 
     fprintf(stderr, "Failed to allocate memory [ %s ]\n", 
       strerror(errno)); 

     return NULL; 
    } 
    cand_data->candidate_data_id = id; 
    cand_data->candidate = candidate; 

    return cand_data; 
} 

static void destroy_candidata_data(Candidate_data *cand_data) 
{ 
    destroy_candidate(cand_data->candidate); 
    free(cand_data); 
} 
+0

¡Su actualización, con 'destroy_candidate()' todavía está en error! El error nunca se mostrará con su programa, pero si alguna vez necesita llamar 'load_candidates()' más de una vez * (o más al punto, si necesita 'getline()' para el mismo apuntador candidate_name [i]) * existe la posibilidad de que la biblioteca intente reasignar un puntero no válido. – pmg

Respuesta

2

getline asigna memoria que se almacenan en su candidate_names matriz de punteros. Esos indicadores no están siendo liberados. Cuando haya terminado con ellos, debe llamar:

for(i = 0; i < NUMBER_OF_CANDIDATES; i++) 
{ 
    if (strcmp(candidate_names[i], "Invalid candidate") != 0) 
     free(candidate_names[i]); 
} 

Además, esa matriz debe declararse como:

static char *candidate_names[NUMBER_OF_CANDIDATES]; 

Y justo antes de su getline que necesita:

candidate_names[i] = NULL; 

NAME_SIZE no es necesario porque esa memoria está asignada dinámicamente, a menos que la esté utilizando en otra parte para la validación de la entrada o algo así.

+0

He actualizado mi pregunta. He agregado una función para destroy_candidate() y libero toda la memoria. Sin embargo, estoy liberando toda la memoria asignada. 'No hay fugas posibles'. Sin embargo, recibo algunos errores 'Invalid free'. Gracias. – ant2009

+0

¿Podría publicar su 'principal'? –

+0

He agregado mi función principal a la pregunta editada. Sin embargo, hay alguna otra función allí. Los guardé en caso de que fueran necesarios para encontrar una solución. Gracias. – ant2009

7

Tener un vistazo al getline() man page.

Si * lineptr es NULO, entonces getline() asignará un búfer para almacenar la línea, que debe ser liberada por el programa de usuario. (En este caso, el valor de * n se ignora.)

Al final de su programa, es necesario para recorrer la matriz candidate_names y llamar free en NULL entradas no, pero en ese caso no se debe hacer candidate_names[i] = "Invalid candidate"; como @pmg señaló en su respuesta como trataría de liberar un literal de cadena.

respete también:

Alternativamente, antes de llamar a getline(), * lineptr puede contener un puntero a una malloc (3) tampón -allocated * n bytes de tamaño. Si el búfer no es lo suficientemente grande como para contener la línea, getline() la cambia de tamaño con realloc (3), actualizando * lineptr y * n según sea necesario.

En cualquier caso, en una llamada exitosa, * lineptr y * n se actualizarán para reflejar la dirección del búfer y el tamaño asignado, respectivamente.

4

getline() asigna espacio para la línea que acaba de leer, llamando malloc() para usted detrás de las escenas. Guarde estos almacenamientos intermedios de línea en la matriz candidate_names, pero nunca los libere. La fuga no es el puntero del archivo; lo liberas muy bien. Son las líneas que lee del archivo, que deben ser liberadas en otro lugar, cuando haya terminado de usarlas.

-2

No creo que esto sea correcto.

getline(&candidate_names[i], &n ,fp); 

No hay razón para pasar un puntero a un número entero.

+0

getline es una función estándar POSIX con el prototipo 'ssize_t getline (char ** lineptr, size_t * n, FILE * stream);'. Pasar un puntero-a-un-tamaño_t es correcto. – bdonlan

+0

whoops, por alguna razón estaba pensando en C++ fstream getline. – Novikov

1

Veo que tiene dos macros diferentes NUMBER_OF_CANDIDATES y NAME_SIZE. Parece un problema

+1

Eso puede ser así, pero no responde la pregunta ... Esto probablemente sería mejor como comentario, no como respuesta. – bdonlan

+1

No es * el * problema, pero es un problema, especialmente si 'NUMBER_OF_CANDIDATES' alguna vez crece más que' NAME_SIZE'. –

+0

#define NAME_SIZE 80. Lo siento por ahí. – ant2009

6

¿Qué es candidate_names? Es una variedad de punteros.
Al hacer

candidate_names[i] = "Invalid candidate"; 

se asigna el puntero a una cadena literal. Tal vez más tarde en el programa que desea free. Eso es NO-NO!

En cualquier caso, se pierde el valor anterior de candidate_names[i]. Si el valor no era NULO, acaba de filtrar un poco de memoria.

+0

Pensé que estaba perfectamente bien. ¿No es la cadena literal un puntero? Y estoy asignando ese puntero a un elemento de la matriz de punteros a char. No estoy asignando ningún recuerdo, por lo que no hay necesidad de liberar. Esto sería liberado por el O/S una vez que el programa sale como está asignado en la pila. ¿Estoy en lo correcto? Gracias. – ant2009

+0

buena captura, +1 y actualicé mi respuesta para reflejarlo –

+1

Suponiendo que los nombres de sus candidatos son lo suficientemente largos, intente 'strcpy (candidate_names [i]," Inválido candidato ");' en su lugar – pmg

1

Está asignando memoria en getline(). Nunca estás liberando ese recuerdo. Esto es lo que le está diciendo Valgrind: que tiene siete (== NUMBER_OF_CANDIDATES) bloques que no ha liberado.

Al cerrar el puntero del archivo no se libera la memoria que getline() asignó.

que tiene que hacer algo como esto al final de load_candidates():

for(int i = 0; i < NUMBER_OF_CANDIDATES; i++) 
{ 
    free(candidate_names[i]); 
} 

EDITAR

En su edición que están liberando punteros nulos. Proveedores:

void destroy_candidate() 
{ 
    size_t i = 0; 
    for(i = 0; i < NUMBER_OF_CANDIDATES; i++) { 
     if ((candidate_names[i] != NULL) && (strcmp(candidate_names[i], "Invalid candidate") != 0)){ 
      free(candidate_names[i]); 
     } 
    } 
} 
Cuestiones relacionadas