简体   繁体   English

尽管内存已被释放,Valgrind仍检测到内存泄漏

[英]Valgrind detects memory leak despite the fact memory has been freed

I have a file "a", with 2000 characters, char "a" only, no spaces. 我有一个文件“ a”,包含2000个字符,仅char“ a”,没有空格。

Then I have this code, that runs trough the loop, add it to buffer, eventually reallocs if the limit is reached and on errors it frees strBuffer variable. 然后,我得到了这段代码,该代码在循环中运行,将其添加到缓冲区中,如果达到限制,最终将重新分配,并在出现错误时释放strBuffer变量。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int maxS = 200;
    int numericExpression;
    int strLength;


    char *strBuffer;
    strBuffer = malloc(sizeof(char)*maxS+1);
        if(strBuffer == NULL)
        {
            printf("Failed to allocate requested memory!\n");
            free(strBuffer);
            strLength = sizeof(strBuffer);
            printf("Freed %d bytes of memory!\n", strLength);
            exit(99);
        }
        else
        {
            numericExpression = sizeof(char)*maxS+1;
            printf("Alocated: %d Bytes of memory.\n", numericExpression);
        }

    // while simulation

    int fcv = -1;
    int numEx;


    // file opening simulation
    FILE* fd = fopen("a", "r");


    int c;
    while((c=fgetc(fd) != EOF)) // condition to make sure we realloc only once
    {
        fcv++;
        strBuffer[fcv] = c;
        if(fcv == (maxS))   
        {
            printf("Additional memory space required!\n");      
            int strlensize = strlen(strBuffer); 
            numEx = (sizeof(char)*(2*strlensize));


            strBuffer = realloc(strBuffer, numEx);

            if(strBuffer == NULL)
            {
                printf("Failed to allocate requested memory!\n");
                strLength = sizeof(strBuffer);
                free(strBuffer);
                printf("Freed %d bytes of memory!\n", strLength);
                exit(99);
            }
            else
            {
                maxS = numEx;
                printf("Reallocation successful!\n");
                printf("Alocated: %d Bytes of memory.\n", numEx);
            }

        }

    }
    strLength = sizeof(strBuffer);
    free(strBuffer);
    printf("Freed %d bytes of memory!\n", strLength);
}

Problem is that it tells me in the end, that I freed only 8 bytes of memory. 问题是它最终告诉我,我只释放了8个字节的内存。 I suppose this is because sizeof(strBuffer) doesn't respond to the expected size. 我想这是因为sizeof(strBuffer)没有响应预期的大小。 When I use strlen(strBuffer) instead, I free only 2001 bytes. 当我改用strlen(strBuffer)时,我仅释放2001个字节。

I suppose this could be only problem of printing out the amount of bytes freed. 我想这可能只是打印出释放的字节数的问题。 I could be doing it wrong. 我可能做错了。 So maybe I'm just not able to tell how many bytes I free. 因此,也许我只是无法说出我释放了多少字节。 But then I try valgrind and it tells me, I don't free enough, that there are memory leaks. 但是然后我尝试使用valgrind,它告诉我,我的内存不足,导致内存泄漏。 But in every branch of a program, I get to free the memory used by strBuffer. 但是在程序的每个分支中,我都可以释放strBuffer使用的内存。

When I run it through valgrind ("valgrind ./realloc"), it tells me this: 当我通过valgrind(“ valgrind ./realloc”)运行它时,它告诉我:

==780== Memcheck, a memory error detector
==780== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==780== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
==780== Command: ./realloc
==780== 
Alocated: 201 Bytes of memory.
Additional memory space required!
==780== Invalid read of size 1
==780==    at 0x4C2E0F4: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780==    by 0x400846: main (in /home/dan/Desktop/test_ifj/realloc)
==780==  Address 0x51fd109 is 0 bytes after a block of size 201 alloc'd
==780==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780==    by 0x40078C: main (in /home/dan/Desktop/test_ifj/realloc)
==780== 
Reallocation successful!
Alocated: 402 Bytes of memory.
==780== Invalid write of size 1
==780==    at 0x400823: main (in /home/dan/Desktop/test_ifj/realloc)
==780==  Address 0x51fd562 is 0 bytes after a block of size 402 alloc'd
==780==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780==    by 0x400866: main (in /home/dan/Desktop/test_ifj/realloc)
==780== 
Additional memory space required!
Reallocation successful!
Alocated: 806 Bytes of memory.
Additional memory space required!
==780== Conditional jump or move depends on uninitialised value(s)
==780==    at 0x4C2E0F8: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==780==    by 0x400846: main (in /home/dan/Desktop/test_ifj/realloc)
==780== 
Reallocation successful!
Alocated: 804 Bytes of memory.
Freed 8 bytes of memory!
==780== 
==780== HEAP SUMMARY:
==780==     in use at exit: 568 bytes in 1 blocks
==780==   total heap usage: 5 allocs, 4 frees, 2,781 bytes allocated
==780== 
==780== LEAK SUMMARY:
==780==    definitely lost: 0 bytes in 0 blocks
==780==    indirectly lost: 0 bytes in 0 blocks
==780==      possibly lost: 0 bytes in 0 blocks
==780==    still reachable: 568 bytes in 1 blocks
==780==         suppressed: 0 bytes in 0 blocks
==780== Rerun with --leak-check=full to see details of leaked memory
==780== 
==780== For counts of detected and suppressed errors, rerun with: -v
==780== Use --track-origins=yes to see where uninitialised values come from
==780== ERROR SUMMARY: 1200 errors from 3 contexts (suppressed: 0 from 0)

How do I properly free memory I have allocated? 如何正确释放已分配的内存? So it won't cause memory leaks? 这样不会引起内存泄漏吗? Eventually - am I doing it wrong and there's a better way to do it? 最终-我做错了吗,还有更好的方法吗? Thank you for your help. 谢谢您的帮助。

QUESTION UPDATE 问题更新

I followed advises and got it to 3 errors in 1 context. 我遵循了建议,并在1个上下文中将其纠正为3个错误。 This is how my code looks now: 这是我的代码现在的样子:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int maxS = 200;
    int numericExpression;


    char *strBuffer;
    strBuffer = malloc((maxS+1));
        if(strBuffer == NULL)
        {
            printf("Failed to allocate requested memory!\n");
            printf("Freed %d bytes of memory!\n", maxS);
            exit(99);
        }
        else
        {
            numericExpression = sizeof(char)*maxS+1;
            printf("Alocated: %d Bytes of memory.\n", numericExpression);
        }

    // while simulation

    int fcv = -1;
    int numEx;


    // file opening simulation
    FILE* fd = fopen("a", "r");

    if(fd == NULL)
    {
        printf("Error opening a file!\n");

        if(strBuffer != NULL)
        {free(strBuffer);}


        exit(99);
    }


    int c;

    char *tmpBuffer;
    while((c=fgetc(fd)) != EOF) // condition to make sure we realloc only once
    {
        fcv++;
        strBuffer[fcv] = c;

        if(fcv == (maxS))   
        {
            printf("Additional memory space required!\n");      

            numEx = ((2*fcv));


            tmpBuffer = realloc(strBuffer, numEx);
            if(!tmpBuffer)
            {
                free(strBuffer);
                printf("Realloc() failed!\n");
                exit(99);
            }       
            else
            {
                strBuffer = tmpBuffer;
            }   


            if(strBuffer == NULL)
            {
                printf("Failed to allocate requested memory!\n");
                printf("Freed %d bytes of memory!\n", maxS); // well this is questionable, I think
                exit(99);
            }
            else
            {
                maxS = numEx;
                printf("Reallocation successful!\n");
                printf("Alocated: %d Bytes of memory.\n", maxS);
            }

        }

    }

    free(strBuffer);fclose(fd); // ADDED, still errors occur

    printf("Freed %d bytes of memory!\n", maxS);
}

And with the same valgrind call ("valgrind ./realloc") I get this: 使用相同的valgrind调用(“ valgrind ./realloc”),我得到了:

==1213== Invalid write of size 1
==1213==    at 0x4007FD: main (in /home/dan/Desktop/test_ifj/realloc)
==1213==  Address 0x51fd560 is 0 bytes after a block of size 400 alloc'd
==1213==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==1213==    by 0x400831: main (in /home/dan/Desktop/test_ifj/realloc)
==1213== 
Additional memory space required!
Reallocation successful!
Alocated: 800 Bytes of memory.
Additional memory space required!
Reallocation successful!
Alocated: 1600 Bytes of memory.
Additional memory space required!
Reallocation successful!
Alocated: 3200 Bytes of memory.
Freed 3200 bytes of memory!
==1213== 
==1213== HEAP SUMMARY:
==1213==     in use at exit: 568 bytes in 1 blocks
==1213==   total heap usage: 6 allocs, 5 frees, 6,769 bytes allocated
==1213== 
==1213== LEAK SUMMARY:
==1213==    definitely lost: 0 bytes in 0 blocks
==1213==    indirectly lost: 0 bytes in 0 blocks
==1213==      possibly lost: 0 bytes in 0 blocks
==1213==    still reachable: 568 bytes in 1 blocks
==1213==         suppressed: 0 bytes in 0 blocks
==1213== Rerun with --leak-check=full to see details of leaked memory
==1213== 
==1213== For counts of detected and suppressed errors, rerun with: -v
==1213== ERROR SUMMARY: 3 errors from 1 contexts (suppressed: 0 from 0)

Any tips what could cause this one? 任何提示可能导致这一问题?

This is your problem: 这是你的问题:

strBuffer = realloc(strBuffer, numEx);

If your call to realloc() fails, then it returns a null pointer, but does not free the original memory allocation. 如果您对realloc()调用失败,则它将返回一个空指针,但不会释放原始的内存分配。

You need to check the return value first, then assign it to the original pointer if successful: 您需要先检查返回值,如果成功,则将其分配给原始指针:

char *tmpBuffer = realloc(strBuffer, numEx);
if (!tmpBuffer) {
  free(strBuffer);
  puts("realloc() failed");
  exit(1);
}
else {
  strBuffer = tmpBuffer;
}

There are a few other issues with your code, including: 您的代码还有其他一些问题,包括:

  • If strBuffer is NULL, then there is no point passing it to free(). 如果strBuffer为NULL,则没有意义将其传递给free()。

  • sizeof() is not a runtime function, so it has no idea how much memory was allocated. sizeof()不是运行时函数,因此它不知道分配了多少内存。

  • strBuffer = malloc(sizeof(char)*maxS+1); is a bit sloppy. 有点草率。 I think you meant strBuffer = malloc(sizeof(char)*(maxS+1)); 我认为您的意思是strBuffer = malloc(sizeof(char)*(maxS+1)); , but you could just have put strBuffer = malloc(maxS+1); ,但您只需将strBuffer = malloc(maxS+1); since sizeof(char) is 1 by definition. 因为sizeof(char)根据定义为1。

Well, everybody up to now gave you really important advises and you should consider using them (mainly the tempBuffer ). 好吧,到目前为止,每个人都为您提供了非常重要的建议,您应该考虑使用它们(主要是tempBuffer )。 BUT your problem is that you forgot to close your file descriptor: 但是您的问题是您忘记关闭文件描述符:

fclose(fd);

Also, sizeof is compile time, thus it cannot give you dynamic allocated memory size and strlen needs a \\n character to work. 同样, sizeof是编译时间,因此它不能给您动态分配的内存大小,并且strlen需要一个\\n字符才能工作。 Calculating allocated and freed memory is a hard task and its solution it's not so trivial. 计算分配和释放的内存是一项艰巨的任务,它的解决方案并不是那么简单。

Answer Update 答案更新

I executed your updated code and I only got 1 error from 1 context , which can be solved by changing the following line: tmpBuffer = realloc(strBuffer, numEx + 1); 我执行了更新的代码, 从1个上下文中仅收到1个错误 ,可以通过更改以下行来解决: tmpBuffer = realloc(strBuffer, numEx + 1);

Other than that, all memory, after fclose(fd) , is free. 除此之外, fclose(fd)之后的所有内存都是可用的。 I'm on a Ubuntu 12.04 machine using gcc 4.8.1. 我在使用gcc 4.8.1的Ubuntu 12.04计算机上。

while((c=fgetc(fd) != EOF)) should be while((c=fgetc(fd)) != EOF) . while((c=fgetc(fd) != EOF))应该是while((c=fgetc(fd)) != EOF) The result of this is that you're trying to store 1 in your string instead of the character you read. 这样做的结果是您试图在字符串中存储1而不是读取的字符。

The memory problem stems from strlen(strBuffer); 内存问题源于strlen(strBuffer); . You are calling strlen on something that is not a null-terminated string, which causes undefined behaviour. 您在非空终止的字符串上调用strlen ,这会导致未定义的行为。 (This shows up as the valgrind report saying "Invalid read of size 1 in strlen ). (这在valgrind报告中显示为“ strlen中大小为1的无效读取)。

To fix this, get rid of strlenSize and do: 要解决此问题,请摆脱strlenSize并执行以下操作:

maxS = 2 * maxS;
strBuffer = realloc(strBuffer, maxS + 1);

Note that if you want a "clean" valgrind in the case when you ran out of memory, you will need to check the return value of realloc before assigning it to strBuffer , as squeamish ossifrage points out. 请注意,如果要在内存不足的情况下使用“干净”的valgrind,则需要在将其分配给strBuffer之前检查realloc的返回值,如提示ossifrage所指出。


NB. 注意 Your error handling code is poor. 您的错误处理代码不正确。 sizeof(strBuffer) finds the size of a pointer. sizeof(strBuffer)查找指针的大小。 The value you wanted to print is maxS . 您要打印的值为maxS Also, free(NULL) has no effect; 另外, free(NULL)无效; and there is no point having an else block after a block in which you called exit() . 在调用exit()块之后没有else块是没有意义的。

this compiles, and does the job
it includes error handling
it eliminated many meaningless variables
it eliminates the errors in the logic of the OPs code



#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    int maxS = 200; // current allocation size

    char *strBuffer = NULL;
    if( NULL == (strBuffer = malloc(maxS) ) )
    { // then, malloc failed
        perror( "malloc failed" );
        exit(99);
    }

    // implied else, malloc successful

    printf("Alocated: %d Bytes of memory.\n", )maxS+1));

    // file opening simulation
    FILE* fd = fopen("a", "r");
    if(fd == NULL)
    { // then fopen failed
        perror( "fopen failed for file: a" );
        free(strBuffer);
        exit(99);
    }

    // implied else, fopen successful

    int c;       // receives input char from fgetc()
    int fcv = 0; // index into malloc'd memory

    // tmpBuffer used in realloc()
    // so will not lose pointer to already allocated memory
    // in case realloc() fails
    char *tmpBuffer;

    while((c=fgetc(fd)) != EOF)
    {
        strBuffer[fcv] = c;
        fcv++;

        if(fcv >= maxS)
        {
            printf("Additional memory space required!\n");

            if( NULL == ()tmpBuffer = realloc(strBuffer, 2*maxS) )
            {
                perror( "realloc failed" );
                free(strBuffer);
                fclose(fd);
                exit(99);
            }

            // implied else, realloc successful

            maxS *= 2;  // only update after being sure realloc successful
            strBuffer = tmpBuffer;

            printf("Reallocation successful!\n");
            printf("Allocated: %d Bytes of memory.\n", maxS);
        } // end if
    } // end while

    free(strBuffer);
    fclose(fd);

    printf("Freed %d bytes of memory!\n", maxS);
    return( 0 );
} // end function: main

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM