为什么没有内存泄漏?

时间:2010-06-19 16:31:22

标签: c memory-leaks free malloc

以下内容旨在获取一个可变长度的常量char,并以一种很好的格式打印出来以进行日志记录。我确信读者会就如何改进这方面提出建议,我很欢迎。

让我感到困惑的是,我希望每次调用ToHexString()时都需要free()返回的静态char。相反,我认为没有任何内存泄漏。即使我使用内联函数,因此也不会将其返回值赋给变量。

我创建了一个简单的测试,它在循环中调用此函数,每次使用不同长度的cString和nMaxChars参数。然后我看了VM的状态。我的测试程序和可用内存的内存分配从未改变过。

在我看来,每次调用malloc并且没有空闲时它应该增加。

static char *ToHexString(const char *cString,int nMaxChars)
{
    static char *cStr;



    /*if (80>strlen(cString))
        nRawChars=strlen(cString);
    if (nMaxChars>nRawChars)
        nRawChars=nMaxChars;
    */
    if (nMaxChars==0)
        nMaxChars=80;

    printf("There are %i chars\n",nMaxChars);

    char *cStr1;
    char *cStr2;
    char *cStr3;
    int nLen=nMaxChars*6;
    cStr=calloc(nLen,sizeof(char));

    cStr1=calloc(10,sizeof(char));
    cStr2=calloc(nLen,sizeof(char));
    cStr3=calloc(nLen,sizeof(char));
    cStr1[0]='\0';
    cStr2[0]='\0';
    cStr3[0]='\0';
    int nC1=0;
    int nRowCnt=0;

    for (nC1=0;nC1<nMaxChars;nC1++)
    {
        ++nRowCnt;
        if (cString[nC1]==0x00)
            snprintf(cStr1,8,"[00] ");
        else
            snprintf(cStr1,8,"[%02x] ",(unsigned char)cString[nC1]);

        if ( (nRowCnt%8==0) )
        {
            snprintf(cStr3,nLen,"%s%s\n",cStr2,cStr1);
        }
        else
            snprintf(cStr3,nLen,"%s%s",cStr2,cStr1);

        snprintf(cStr2,nLen,"%s",cStr3);
    }
    snprintf(cStr,nLen,"%s",cStr3);
    free(cStr1);
    free(cStr2);
    free(cStr3);
    return(cStr);
}

以下是调用例程:

for (i=0;i<100;i++)
{
    memset(&cBuff, 0,255);
    printf("Reading %s now..\n",cPort);
    while (sleep(1)==-1);
    nChars=read(nPort, cBuff, 255);
    //printf("Read %i chars from %s\n",nChars,cPort);
    if (nChars<=0)
        printf("Read 0 chars from %s\n",cPort);
    else
        printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars));
}

4 个答案:

答案 0 :(得分:10)

以下是泄密:

static void memeat(void)
{
        static char *foo = NULL;

        foo = malloc(1024);

        return;

}

Valgrind输出:

==16167== LEAK SUMMARY:
==16167==    definitely lost: 4,096 bytes in 4 blocks
==16167==    indirectly lost: 0 bytes in 0 blocks
==16167==      possibly lost: 0 bytes in 0 blocks
==16167==    still reachable: 1,024 bytes in 1 blocks
==16167==         suppressed: 0 bytes in 0 blocks
==16167== Rerun with --leak-check=full to see details of leaked memory

注意,still reachable(1024字节)是上次输入memeat()的结果。静态指针仍保持对程序退出时分配的最后一个块memeat()的有效引用。只是不是以前的块。

以下是 NOT 泄密:

static void memeat(void)
{
        static char *foo = NULL;

        foo = realloc(foo, 1024);

        return;

}

Valgrind输出:

==16244== LEAK SUMMARY:
==16244==    definitely lost: 0 bytes in 0 blocks
==16244==    indirectly lost: 0 bytes in 0 blocks
==16244==      possibly lost: 0 bytes in 0 blocks
==16244==    still reachable: 1,024 bytes in 1 blocks
==16244==         suppressed: 0 bytes in 0 blocks
==16244== Rerun with --leak-check=full to see details of leaked memory

此处,指向的地址foo已被释放,foo现在指向新分配的地址,并在下次memeat()输入时继续执行此操作。< / p>

<强>解释

static存储类型表示每次输入函数时,指针foo将指向与初始化时相同的地址。但是,如果每次通过malloc()calloc()输入功能时更改该地址,您就会丢失对先前分配的块的引用。因此,泄漏,因为要么将返回一个新地址。

valgrind中的“仍然可访问”意味着所有已分配的堆块仍然具有在退出时访问/操作/释放它们的有效指针。这类似于在main()中分配内存而不释放内存,只是依靠操作系统来回收内存。

简而言之,是的 - 你有泄漏。但是,您可以轻松地修复它。请注意,你确实依赖你的操作系统来回收内存,除非你在你的函数中添加另一个参数,它只是告诉ToHexString在静态指针上调用free,你可以在退出时使用它。

与此类似:(完整的测试程序)

#include <stdlib.h>

static void memeat(unsigned int dofree)
{
        static char *foo = NULL;

        if (dofree == 1 && foo != NULL) {
                free(foo);
                return;
        }

        foo = realloc(foo, 1024);

        return;

}


int main(void)
{
        unsigned int i;

        for (i = 0; i < 5; i ++)
                memeat(0);

        memeat(1);
        return 0;
}

Valgrind输出:

==16285== HEAP SUMMARY:
==16285==     in use at exit: 0 bytes in 0 blocks
==16285==   total heap usage: 6 allocs, 6 frees, 6,144 bytes allocated
==16285==
==16285== All heap blocks were freed -- no leaks are possible

关于最终输出的注意事项

是的,根据程序运行时返回的malloc()实际分配了6144个字节,但这只是意味着释放了静态指针,然后根据输入memeat()的次数重新分配。在任何给定时间,程序的实际堆使用实际上只有2 * 1024,1k用于分配新指针,而旧指针仍然存在,等待复制到新指针。

同样,调整代码应该不会太难,但我不清楚为什么要开始使用静态存储。

答案 1 :(得分:1)

这是内存泄漏。如果持续调用该函数,则程序使用的内存会增加。例如:

int main() {
   while (1) {
      ToHexString("testing the function", 20);
   }
}

如果您运行此操作并使用系统监视工具观察该过程,您将看到已使用的内存不断增加。

程序中的泄漏可能并不明显,因为该函数每次调用只会泄漏几个字节,并且不会经常调用。因此,内存使用量的增加并不是很明显。

答案 2 :(得分:1)

我想在检查代码时指出两个让我头脑发热的东西:

cStr=calloc(nLen,sizeof(char));

为什么你没有对此进行错误检查....正如我从代码中看到的那样,对假设记忆零检查永远可用....危险.... 总是< / strong>从内存分配函数调用(例如callocmallocrealloc)返回时检查 NULL 指针。 .it将始终成为程序员的责任,以管理指针的free以使它们返回堆中。

另外,因为你将cStr声明为char *的静态指针,所以你根本没有释放它,事实上这条线证明了这一点:

printf("Read %i chars from %s\n%s\n",nChars,cPort,ToHexString(cBuff,nChars));
                                                  ^^^^^^^^^^^

你最好这样做:

char *hexedString;
hexedString = ToHexString(cBuff, nChars);
....
printf("Read %i chars from %s\n%s\n",nChars,cPort,hexedString);
....
free(hexedString);

答案 3 :(得分:0)

您可以在新数组中返回例程的结果。使用您的模型,使用结果释放此数组的责任在于调用者。所以,在调用者中,你应该将你的例程的结果存储在一个临时的,用它做你想做的任何事情,然后在最后释放()它。