не может освободить память - PullRequest
       34

не может освободить память

5 голосов
/ 15 сентября 2010

gcc 4.4.4 c89

У меня есть следующая функция, но я не могу освободить память. Сообщение, которое я получаю в Valgrind, подозревает функцию getline. Тем не менее, я свободен указатель файла в конце функции. Так не может быть.

У меня есть глобальный массив указателей на символ char_names. Однако я не выделил для этого памяти.

Большое спасибо за любой совет,

Сообщение, которое я получаю в valgrind, следующее.

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)

Мой исходный код:

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

РЕДАКТИРОВАТЬ ========= БЕСПЛАТНЫЕ ИМЯ Кандидатов ======

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

РЕДАКТИРОВАТЬ с кодом из 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);
}

Ответы [ 7 ]

7 голосов
/ 15 сентября 2010

Посмотрите справочную страницу getline() .

Если * lineptr равен NULL, тогда getline () выделит буфер для хранения строки, что должнобыть освобожденным пользовательской программой.(В этом случае значение в * n игнорируется.)

В конце вашей программы вам нужно перебрать массив candidate_names и вызвать free для не NULLзаписей, но в этом случае вы не должны делать candidate_names[i] = "Invalid candidate";, как указал @pmg в своем ответе, поскольку вы пытаетесь освободить строковый литерал.

Обратите также внимание на:

В качестве альтернативыперед вызовом getline () * lineptr может содержать указатель на выделенный для malloc (3) буфер размером * n байт.Если буфер недостаточно велик для размещения строки, getline () изменяет ее размер с помощью realloc (3), обновляя * lineptr и * n по мере необходимости.

В любом случае при успешном вызове * lineptr и* n будет обновлен, чтобы отразить адрес буфера и выделенный размер соответственно.

6 голосов
/ 15 сентября 2010

Что такое candidate_names?Это массив указателей.
Когда вы делаете

candidate_names[i] = "Invalid candidate";

, вы назначаете указатель на строковый литерал.Возможно, позже в программе вы захотите free это.Это NO-NO !

В любом случае предыдущее значение candidate_names[i] теряется.Если значение не было NULL, вы просто вытекли немного памяти.

4 голосов
/ 15 сентября 2010

getline() выделяет место для только что прочитанной строки, вызывая malloc() для вас за кулисами. Вы храните эти строковые буферы в массиве candidate_names, но никогда не освобождаете их. Утечка - это не указатель на файл - вы это просто исправляете. Это строки, которые вы читаете из файла, которые должны быть освобождены в другом месте, когда вы закончите использовать их.

2 голосов
/ 15 сентября 2010

getline выделяет память, которую вы храните в массиве candidate_names указателей. Эти указатели не освобождаются. Когда вы закончите с ними, вы должны позвонить:

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

Кроме того, этот массив должен быть объявлен как:

static char *candidate_names[NUMBER_OF_CANDIDATES];

А прямо перед вашей getline вам нужно:

candidate_names[i] = NULL;

NAME_SIZE не требуется, поскольку эта память выделяется динамически, если только вы не используете ее в другом месте для проверки ввода или чего-то еще.

1 голос
/ 15 сентября 2010

Вы выделяете память внутри getline ().Вы никогда не освобождаете эту память.Вот что говорит вам valgrind: у вас есть семь (== NUMBER_OF_CANDIDATES) блоков, которые вы не освободили.

Закрытие указателя файла не освобождает память, выделенную getline ().

Вам нужно сделать что-то вроде этого в конце load_candidates ():

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

РЕДАКТИРОВАТЬ

В вашем редакторе вы освобождаете нулевые указатели.Попробуйте:

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]);
        }
    }
}
1 голос
/ 15 сентября 2010

Я вижу, что у вас есть два разных макроса NUMBER_OF_CANDIDATES и NAME_SIZE.Похоже на неприятности.

0 голосов
/ 15 сентября 2010

Не думаю, что это правильно.

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

Нет причин передавать указатель на целое число.

...