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

gcc 4.4.4 c89

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

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

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

Сообщение, которое я получаю в 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);
}

person ant2009    schedule 15.09.2010    source источник
comment
Ваше обновление с destroy_candidate() все еще ошибочно! Ошибка никогда не отобразится в вашей программе, но если вам когда-нибудь понадобится вызвать load_candidates() более одного раза (или более конкретно, если вам нужно getline() на тот же указатель имя_кандидата [i]), то есть вероятность того, что библиотека попытается перераспределить неверный указатель.   -  person pmg    schedule 15.09.2010


Ответы (7)


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

И прямо перед получением вам нужно:

candidate_names[i] = NULL;

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

person Karl Bielefeldt    schedule 15.09.2010
comment
Я обновил свой вопрос. Я добавил функцию в destroy_candidate () и освободил всю память. Однако я освобождаю всю выделенную память. «Утечки невозможны». Однако я получаю сообщение об ошибке «Недействительно бесплатно». Спасибо. - person ant2009; 15.09.2010
comment
К отредактированному вопросу я добавил свою основную функцию. Однако там есть и другая функция. Я сохранил их на случай, если они понадобятся для решения проблемы. Спасибо. - person ant2009; 16.09.2010
comment
Ваш destroy_candidate вызов не соответствует определению, которое вы опубликовали ранее. - person Karl Bielefeldt; 16.09.2010
comment
@ Карл, это был полный код. Однако он сделал гораздо больше. Я просто выложил образец в порядковый вопрос. Я попробую разобраться сам. Похоже, мне просто придется его разобрать. Спасибо. - person ant2009; 16.09.2010

Взгляните на getline() страницу руководства .

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

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

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

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

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

person Gregory Pakosz    schedule 15.09.2010

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

candidate_names[i] = "Invalid candidate";

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

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

person pmg    schedule 15.09.2010
comment
Я думал, что все в порядке. Разве строковый литерал не является указателем? И я назначаю этот указатель на элемент массива указателей на char. Я не выделяю память, поэтому освобождать не нужно. Это будет освобождено операционным сервером после выхода из программы, поскольку оно выделено в стеке. Я прав? Спасибо. - person ant2009; 15.09.2010
comment
Предполагая, что имена ваших кандидатов достаточно длинные, попробуйте вместо этого strcpy(candidate_names[i], "Invalid candidate"); - person pmg; 15.09.2010
comment
@robUK: Дело в том, что хранилище строкового литерала находится в текстовом сегменте вашего исполняемого файла. Он не был выделен malloc из кучи. Вы не можете его освободить. - person JeremyP; 15.09.2010
comment
@robUK: вы не распределяете, а getline() выделяет. Эту память нужно освободить, иначе valgrind жалуется. - person pmg; 15.09.2010
comment
@JeremyP, теперь я понимаю. Итак, строковый литерал был размещен в стеке. Таким образом, попытка освободить его приведет к ошибке «Недопустимый бесплатный». Поскольку он никогда не выделялся при использовании malloc. Спасибо. - person ant2009; 15.09.2010
comment
@robUK: строковые литералы не размещаются в стеке, но вы правы в том, что они не выделяются с помощью malloc. - person jamesdlin; 15.09.2010
comment
@JeremyP, если строковые литералы размещены в стеке, то где они находятся? Строковый литерал - это указатель, который должен иметь некоторую область в памяти. Спасибо. - person ant2009; 16.09.2010
comment
@robUK: возможно, этот пост SO поможет ... (stackoverflow.com/questions/1966920/) - person pmg; 16.09.2010
comment
@robUK: строковые литералы не выделяются в стеке, они выделяются компилятором как часть кода вашей программы. - person JeremyP; 16.09.2010

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

person bdonlan    schedule 15.09.2010

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

person Jens Gustedt    schedule 15.09.2010
comment
Это может быть так, но это не отвечает на вопрос ... Это, вероятно, было бы лучше в качестве комментария, а не ответа. - person bdonlan; 15.09.2010
comment
Это не проблема, но проблема, особенно если NUMBER_OF_CANDIDATES когда-либо становится больше, чем NAME_SIZE. - person Karl Bielefeldt; 15.09.2010
comment
#define NAME_SIZE 80. Извините, что упустил это. - person ant2009; 15.09.2010
comment
@bdonlan: это могло быть просто проблемой, поскольку это могло привести к повреждению памяти, неопределенному поведению и всему, что могло случиться. - person Jens Gustedt; 15.09.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]);
        }
    }
}
person Andy Johnson    schedule 15.09.2010

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

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

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

person Novikov    schedule 15.09.2010
comment
getline - это стандартная функция POSIX с прототипом `ssize_t getline (char ** lineptr, size_t * n, FILE * stream);`. Передача указателя на size_t является правильной. - person bdonlan; 15.09.2010
comment
упс, по какой-то причине я подумал о C ++ fstream getline. - person Novikov; 15.09.2010