无法释放记忆

时间:2010-09-15 15:30:07

标签: c malloc free valgrind

gcc 4.4.4 c89

我有以下功能,但我无法释放内存。我在Valgrind得到的消息是怀疑getline函数。但是,我在函数末尾释放文件指针。所以不可能。

我有一个指向char'relection_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;
}

编辑=========释放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]);
        }
    }
}

使用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 个答案:

答案 0 :(得分:7)

查看getline() man page

  

如果* 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将分别更新以反映缓冲区地址和分配的大小。

答案 1 :(得分:6)

什么是candidate_names?这是一系列指针 当你这样做

candidate_names[i] = "Invalid candidate";

将指针指定给字符串文字。也许以后在程序中你想要free它。这是 NO-NO

在任何情况下,candidate_names[i]的先前值都将丢失。如果该值不是NULL,则只是泄漏了一些内存。

答案 2 :(得分:4)

getline()为刚读取的行分配空间,在幕后为您调用malloc()。您将这些行缓冲区存储在candidate_names数组中,但从不释放它。泄漏不是文件指针 - 你释放就好了。这是您从文件中读取的行,当您使用它们时需要在其他地方释放。

答案 3 :(得分:2)

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,因为该内存是动态分配的,除非您在其他地方使用它进行输入验证等。

答案 4 :(得分:1)

我看到你有两个不同的宏NUMBER_OF_CANDIDATESNAME_SIZE。看起来很麻烦。

答案 5 :(得分:1)

您正在getline()中分配内存。你永远不会释放那段记忆。这就是valgrind告诉你的:你有七个(== NUM​​BER_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]);
        }
    }
}

答案 6 :(得分:-2)

我不认为这是正确的。

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

没有理由将指针传递给整数。