【问题标题】:Valgrind detects memory leak despite the fact memory has been freed尽管内存已被释放,Valgrind 仍检测到内存泄漏
【发布时间】:2015-01-30 02:01:38
【问题描述】:

我有一个文件“a”,有 2000 个字符,只有字符“a”,没有空格。

然后我有这段代码,它通过循环运行,将其添加到缓冲区,如果达到限制,最终重新分配,并在错误时释放 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);
}

问题是它最后告诉我,我只释放了 8 个字节的内存。我想这是因为 sizeof(strBuffer) 没有响应预期的大小。当我改用 strlen(strBuffer) 时,我只释放了 2001 个字节。

我想这可能只是打印出释放的字节数的问题。我可能做错了。所以也许我只是无法说出我释放了多少字节。但是后来我尝试了 valgrind,它告诉我,我没有足够的空闲,存在内存泄漏。但是在程序的每个分支中,我都会释放 strBuffer 使用的内存。

当我通过 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)

如何正确释放已分配的内存?那么它不会导致内存泄漏吗?最终 - 我做错了,有更好的方法吗? 感谢您的帮助。

问题更新

我遵循了建议,并在 1 个上下文中得到了 3 个错误。这就是我的代码现在的样子:

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

使用相同的 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)

有什么提示会导致这个问题吗?

【问题讨论】:

  • 如果strBuffer 为NULL,那么将它传递给free() 是没有意义的。另外,sizeof() 不是运行时函数,所以它不知道分配了多少内存。
  • invalid write 可能很关键。您应该使用更多调试信息 (-g3) 来构建以获取行号。
  • 回复。更新的版本:你的分配仍然有一个错误,因为你在检查你是否在最后之前写了。要么将 realloc 更改为具有 +1 的大小,要么(最好)使 fcv0 开始并将顺序更改为 strBuffer[fcv] = c; fcv++;
  • @mattmcnabb 我知道最好不要使用这个评论系统来表示感谢,但是......谢谢。你修好了。
  • @user3629249 他从fvc == -1 开始,所以它确实设置了索引0;但导致超过分配结束

标签: c memory memory-management memory-leaks valgrind


【解决方案1】:

while((c=fgetc(fd) != EOF)) 应该是 while((c=fgetc(fd)) != EOF)。这样做的结果是您试图将 1 存储在您的字符串中,而不是您读取的字符中。

内存问题源于strlen(strBuffer);。您在不是以空字符结尾的字符串上调用 strlen,这会导致未定义的行为。 (这显示为 valgrind 报告说“strlen 中大小为 1 的读取无效)。

要解决此问题,请删除 strlenSize 并执行以下操作:

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

请注意,如果您在内存不足的情况下想要一个“干净”的 valgrind,您需要在将其分配给 strBuffer 之前检查 realloc 的返回值,正如 squeamish ossifrage 指出的那样。


注意。您的错误处理代码很差。 sizeof(strBuffer) 查找指针的大小。您要打印的值是maxS。另外,free(NULL) 无效;在您调用 exit() 的块之后有一个 else 块是没有意义的。

【讨论】:

    【解决方案2】:

    这是你的问题:

    strBuffer = realloc(strBuffer, numEx);
    

    如果您对realloc() 的调用失败,则它会返回一个空指针,但不会释放原始内存分配。

    你需要先检查返回值,如果成功则将其赋值给原始指针:

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

    您的代码还有一些其他问题,包括:

    • 如果 strBuffer 为 NULL,则将其传递给 free() 毫无意义。

    • sizeof() 不是运行时函数,因此它不知道分配了多少内存。

    • strBuffer = malloc(sizeof(char)*maxS+1); 有点草率。我想你的意思是strBuffer = malloc(sizeof(char)*(maxS+1));,但你可以只输入strBuffer = malloc(maxS+1);,因为sizeof(char)的定义是1。

    【讨论】:

    • 感谢您提供有用的评论,它(连同所有其他答案)帮助我将错误编号仅设为 3 合 1 上下文。但是我仍然无法找出问题所在。有没有办法可以更新代码,或者我应该更新原始问题,我现在得到了什么? (我的意思是如果允许的话?)谢谢。
    • 我的建议是自己解决这个问题一段时间。尝试隔离出现问题的代码,然后使用minimal complete verifiable example 发布一个新问题,并明确说明问题行为和预期行为。如果可以避免的话,没有人会真正喜欢涉足大量未注释的代码。
    • @squeamishossifrage nitpick: sizeof 是 C99 中的运行时运算符,问题是它返回其操作数类型的大小,而在此代码中,操作数是 char *,因此产生的值是sizeof(char *)
    【解决方案3】:

    嗯,到目前为止,每个人都给了你非常重要的建议,你应该考虑使用它们(主要是 tempBuffer)。但是您的问题是您忘记关闭文件描述符:

    fclose(fd);
    

    另外,sizeof 是编译时间,因此它不能给你动态分配的内存大小,strlen 需要一个\n 字符才能工作。计算分配和释放的内存是一项艰巨的任务,它的解决方案并不是那么简单。

    答案更新

    我执行了你更新的代码,我只得到 1 个来自 1 个上下文的错误,这可以通过更改以下行来解决: tmpBuffer = realloc(strBuffer, numEx + 1);

    除此之外,fclose(fd) 之后的所有内存都是空闲的。我在使用 gcc 4.8.1 的 Ubuntu 12.04 机器上。

    【讨论】:

    • 我错过了这个,真的没有意识到,谢谢。但问题仍然存在...... 1 个上下文中的 3 个错误。正如建议的那样,我将尝试自己处理。
    【解决方案4】:
    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
    

    【讨论】:

      猜你喜欢
      • 2015-02-28
      • 2018-06-04
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 2017-07-17
      • 1970-01-01
      • 2012-07-16
      相关资源
      最近更新 更多