4

以下は、可変長の定数文字を取得し、ログに記録するための適切な形式で出力するように設計されています。読者の皆さんには、これをどのように改善できるかについての提案があると確信しており、歓迎します。

私が困惑しているのは、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の「StillReachable」は、割り当てられたすべてのヒープブロックが、終了時にそれらにアクセス/操作/解放するための有効なポインターをまだ持っていることを意味します。main()これは、メモリを割り当てて解放せず、OSに依存してメモリを再利用することに似ています。

要するに、はい-あなたには漏れがあります。ただし、かなり簡単に修正できます。ToHexString終了時に使用できる静的ポインタでfreeを呼び出すように指示する別の引数を関数に追加しない限り、実際にはOSに依存してメモリを再利用していることに注意してください。

これに似ています:(完全なテストプログラム)

#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

最終出力に関する注意

はい、実際には、プログラムの実行中に返された内容に従って6144バイトが割り当てられましたmalloc()が、これは、静的ポインターが解放され、入力された回数に応じて再割り当てされたことを意味します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

コードを調べるときに頭に浮かぶ2つのことを指摘したいと思います。

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

なぜこれでエラーチェックをしなかったのですか....コードからわかるように、メモリが常に利用可能であるという仮定でゼロチェックがあります....危険です....から戻るときに常にNULLポインタをチェックします、などのメモリ割り当て関数呼び出しcallocmallocおよびrealloc...ポインタをヒープfreeに戻すためのポインタのアップを管理するのは、常にプログラマの責任です。

また、cStrへの静的ポインタとして宣言しているのでchar *、それをまったく解放していません。実際、この行はそれを証明しています。

printf( "%sから%i文字を読み取る\ n%s \ n"、nChars、cPort、ToHexString(cBuff、nChars));
                                                  ^^^^^^^^^^^

あなたはこのようにそれをするほうがよいでしょう:

char * hexedString;
hexedString = ToHexString(cBuff、nChars);
...。
printf( "%sから%i文字を読み取る\ n%s \ n"、nChars、cPort、hexedString);
...。
free(hexedString);
于 2010-06-19T17:20:32.753 に答える
0

ルーチンの結果を新しい配列で返します。モデルでは、この配列を結果で解放する責任は呼び出し元にあります。したがって、呼び出し元では、ルーチンの結果を一時的に保存し、それを使って好きなことを行い、最後にfree()する必要があります。

于 2010-06-19T17:08:56.753 に答える