1

私の質問は、C で free() を使用するのが適切な場合に関するものです。私は gcc 4.3.2 を使用しています。

リンクされたリストで大量のメモリの割り当てを解除する必要がある場合、それを行う理想的な方法は次のとおりです(私は推測しています):

int freeLL(node *a)
{
    if(a->next != NULL)
         freeLL(a->next);
    free(a);
    return 0;
}

ここで、次の ADT で同様のことを行うとします。

「頂点」と「エッジ」(たとえば)の2つのポインターを持つポインター「VertexNode」。次のように言うのと同じです:

struct vertexnode
{
     vertex *v;
     edge *e;
}
typedef struct vertexnode* VertexNode;

後で、インスタンスを初期化するときに、次のようにします-

VertexNode V = malloc(sizeof(struct vertexnode));
V->v = malloc(sizeof(vertex));

したがって、最終的に解放する際に、リンクされたリストに使用したのと同じアナロジーを使用しました。

free(V->v);
free(V);

これにより実行時エラーが発生し、「free(V->v)」をコメントアウトすると、プログラムは正常に動作しました。私の質問は次のとおりです。

a) free(V) を実行するだけで十分ですか? つまり、 free() は、特定のポインター内のすべてのポインターに対して再帰的に機能しますか?

b) そうでない場合、この場合、メモリ リークがありますか? そして、それを理想的に防ぐにはどうすればよいですか?

c) 最後に、malloc() によって割り当てられたバイト数と free() によって解放されたバイト数を追跡​​する方法はありますか?

長い質問で大変申し訳ありません。どうぞよろしくお願いいたします。

4

6 に答える 6

2

いいえ、freeは再帰的に機能しないため、実際にメモリリークが発生します。発生しているランタイムエラーは、おそらく論理エラーに関係しています(おそらく、V->v解放NULLする前に割り当てなかったか、割り当てていませんでした)。

Linuxを使用している場合、valgrindを使用すると、プログラムのプロファイリングとリークエラーの言及に役立ちます。を使用してコンパイルしcc *.c -ggdbてから実行valgrind --leakcheck=full ./a.outすると、リークエラーが出力されます。

于 2012-11-30T12:43:28.763 に答える
2

完全なメモリ管理のために、私はメモリプールを持っているでしょう。これにより、呼び出されるmallocごとに多数のフリーを呼び出さなければならないというすべての苦痛が取り除かれます。メモリプールを実行するためにApachePortableRuntime(APR)を使用しています。初期化するには、開始時にメモリのチャンクを割り当てるだけです。各ポインタに必要なだけ割り当てます。次に、最後に1回呼び出して、すべてのメモリを解放します。これは、メモリリークにつながる多くのmallocと解放を実行するよりもはるかに効率的です。

補足として。メモリプールを使用しない場合は、valgrindを使用してアプリケーションをテストすることをお勧めします。実際、常にvalgrindを使用する必要があります。

于 2012-11-30T13:00:01.623 に答える
2

あなたの質問に答えるには:

a) いいえ。free() は構造体のメンバー ポインターを再帰的に解放しません。

b) はい、その場合はメモリ リークが発生します。コードで割り当てられたすべてのメモリを解放する必要があります。

c) valgrind などのツールを使用して、簡単にメモリ リークをチェックできます。また、一部のプロジェクトは独自のメモリ管理を実装していることを知っています。それらのプロジェクトは独自の API で malloc と free をラップしているため、API でメモリ使用量を追跡できます。

于 2012-11-30T12:47:17.863 に答える
1

重要なのは、解放しようとしているポインタが実際に初期化されていることを確認する必要があるということです。

私はそれを呼び出す前にそうV->vではないかどうかもチェックします(あなたの質問に対する私のコメントを参照してください)。NULLfree

free「再帰的」ではありません。解放しないev(あなたの例では)メモリリークが発生します。

于 2012-11-30T12:43:38.420 に答える
1

A:free()再帰的には機能しません。-ADTの概念を使用している場合は、関数を作成して型内で割り当てを実行し、関数内でチェックと割り当て解除を行うことをお勧めしcreateVertexNode()ますfreeVertexNode()

B:解放できなかった場合は、メモリリークになります。ADT関数が割り当てたメモリを確認して解放することで回避できます。または...

C:メモリリークチェッカーを使用します。Visual Studioには組み込みのものがあるか、valgrindやRationalPurifyなどの他のものを使用します。最も単純な場合、malloc()およびfree()呼び出しをオーバーライドする無料のオープンソースライブラリがもっとあると確信しています

于 2012-11-30T12:52:16.647 に答える
1

基本的に覚えておくべきことは、 malloc()/ free()should beの呼び出し1:1です。malloc()メモリを取得するために呼び出しfree()た場合、完了したらそれを返すために呼び出す必要があります。

これは質問a)に答えます。いいえ、再帰的ではありません。(または、1回の呼び出しでfree()十分です)。

b) メモリ リークが発生するに違いありません。簡単なコードを考えてみましょう:

typedef struct pointless {
    struct pointless * next;
}pl;

pl * head = malloc(sizeof(pl)); // head gets 4 bytes
pl->next = malloc(sizeof(pl));  // head->next gets 4 bytes
free(head);  // head's allocated 4 bytes are deleted

次の空の要素を指すだけなので、明らかにこれは無意味なリストですが、要点を示しています。私の合計リストには 8 バイトが割り当てられています ( の場合は 4 バイト、headの場合は 4headバイトnext)。私が呼び出すと、の4バイトfree()で動作しますが、それだけです。head

このように機能するのは良いことです!リンクされたリストの途中から 1 つのアイテムを削除することを考えてみてください。そのノードのメモリを解放したいのですが、全部ではありません! ただし、上記の例を考えると、解放されてhead->nextいないため、メモリリークが発生します。一度電話free(head)したら、もうアクセスできるという保証はありhead->nextません。もちろん可能ですが、現時点ではUBです。

c) そのレベルでのメモリ管理は OS によって行われます。割り当て/解放した量を追跡したい場合は、次のようにする必要があります。

int total_mem = 0;

head = malloc((total_mem += sizeof(pl)));

free(head);
total_mem -= sizeof(head);

もちろん、malloc()との周りにラッパーを配置free()して計算を行うこともできます。

于 2012-11-30T13:15:37.097 に答える