无法释放内存

发布于 2024-09-18 22:55:05 字数 5223 浏览 13 评论 0原文

gcc 4.4.4 c89

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

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

gcc 4.4.4 c89

I have the following function but I cannot free the memory. The message I get in Valgrind is suspecting the getline function. However, I am free the file pointer at the end of the function. So it cannot be that.

I have a global array of pointers to char 'candidate_names'. However, I haven't allocated any memory for it.

Many thanks for any advice,

The message I get in valgrind is the following.

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)

My source code:

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

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

EDIT with code from 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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

烟─花易冷 2024-09-25 22:55:05

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

Have a look at the getline() man page.

If *lineptr is NULL, then getline() will allocate a buffer for storing the line, which should be freed by the user program. (In this case, the value in *n is ignored.)

At the end of your program, you need to loop over your candidate_names array and call free on non NULL entries but in that case you must not do candidate_names[i] = "Invalid candidate";as @pmg pointed in his answer as you would try to free a string literal.

Pay also attention to:

Alternatively, before calling getline(), *lineptr can contain a pointer to a malloc(3)-allocated buffer *n bytes in size. If the buffer is not large enough to hold the line, getline() resizes it with realloc(3), updating *lineptr and *n as necessary.

In either case, on a successful call, *lineptr and *n will be updated to reflect the buffer address and allocated size respectively.

停顿的约定 2024-09-25 22:55:05

candidate_names 是什么?它是一个指针数组。
当您这样做时,

candidate_names[i] = "Invalid candidate";

您将指针分配给字符串文字。也许稍后在程序中您想要释放它。这是一个NO-NO

无论如何,candidate_names[i] 之前的值都会丢失。如果该值不为 NULL,则您只是泄漏了一些内存。

What is candidate_names? It's an array of pointers.
When you do

candidate_names[i] = "Invalid candidate";

you assign the pointer to a string literal. Maybe later in the program you want to free it. That's a NO-NO!

In any case, the previous value of candidate_names[i] is lost. If the value was not NULL, you just leaked some memory.

雨巷深深 2024-09-25 22:55:05

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

getline() allocates space for the line it just read, calling malloc() for you behind the scenes. You store these line buffers in the candidate_names array, but never free it. The leak isn't the file pointer - you free that just fine. It's the lines you read from the file, which need to be freed elsewhere, when you're done using them.

来日方长 2024-09-25 22:55:05

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

getline allocates memory which you store in your candidate_names array of pointers. Those pointers are not getting freed. When you are done with them, you should call:

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

Additionally, that array should be declared as:

static char *candidate_names[NUMBER_OF_CANDIDATES];

And right before your getline you need:

candidate_names[i] = NULL;

NAME_SIZE isn't needed because that memory is dynamically allocated, unless you are using it elsewhere for input validation or something.

空城缀染半城烟沙 2024-09-25 22:55:05

我发现您有两个不同的宏NUMBER_OF_CANDIDATES
NAME_SIZE。看起来有麻烦了。

I see that you have two different macros NUMBER_OF_CANDIDATES
and NAME_SIZE. Looks like trouble.

帅气尐潴 2024-09-25 22:55:05

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

You are allocating memory inside getline(). You are never freeing that memory. This is what valgrind is telling you: that you have seven (== NUMBER_OF_CANDIDATES) blocks that you have not freed.

Closing the file pointer does not free the memory that getline() allocated.

You need to do something like this at the end of load_candidates():

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

EDIT

In your edit you are freeing null pointers. Try:

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]);
        }
    }
}
衣神在巴黎 2024-09-25 22:55:05

我认为这是不正确的。

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

没有理由传递指向整数的指针。

I don't think this is correct.

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

No reason to pass a pointer to an integer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文