4

以下旨在获取可变长度的常量 char 并以良好的格式打印出来以进行日志记录。我相信读者会对如何改进这一点提出建议,我很欢迎。

令我困惑的是,我预计每次调用 ToHexString() 时都需要 free() 返回的静态字符。相反,我没有看到任何内存泄漏。即使我使用内联函数,因此也不会将其返回值分配给变量。

我创建了一个简单的测试,在循环中调用这个函数,每次都使用不同长度的 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

4 回答 4

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()对程序退出时分配的最后一个块的有效引用。只是不是以前的块。

以下不是泄漏:

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()进入时会继续这样做。

解释:

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 来分配新指针,而旧指针仍然存在等待复制到新指针。

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

于 2010-06-19T16:37:09.813 回答
1

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

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

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

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

于 2010-06-19T17:09:40.203 回答
1

在检查代码时,我想指出两件事:

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

你为什么不对此进行错误检查....正如我从代码中看到的那样,假设内存将始终可用,则检查为零....危险....总是在返回时检查NULL指针内存分配函数调用,例如calloc,mallocrealloc...它始终是程序员的责任来管理free指针的“ing”以将它们返回到堆中。

此外,由于您已cStr声明为指向 的静态指针char *,因此您根本没有释放它,实际上这一行证明了这一点:

printf("从 %s\n%s\n 读取 %i 个字符",nChars,cPort,ToHexString(cBuff,nChars));
                                                  ^^^^^^^^^^^^

你最好这样做:

字符 *hexedString;
hexedString = ToHexString(cBuff, nChars);
……
printf("从 %s\n%s\n 读取 %i 个字符",nChars,cPort,hexedString);
……
免费(十六进制字符串);
于 2010-06-19T17:20:32.753 回答
0

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

于 2010-06-19T17:08:56.753 回答