67

私はCを学ぼうとしていて、現在基本的なスタックデータ構造を書き込もうとしていますが、基本的malloc/free正しく理解できないようです。

これが私が使用しているコードです(コード全体ではなく、特定の問題を説明するためにここに小さな部分を投稿していますが、エラーメッセージはこのサンプルコードを実行するだけで生成されましたvalgrind

#include <stdio.h>
#include <stdlib.h>

typedef struct Entry {
    struct Entry *previous;
    int value;
} Entry;

void destroyEntry(Entry entry);

int main(int argc, char *argv[])
{
    Entry* apple;
    apple = malloc(sizeof(Entry));
    destroyEntry(*(apple));
    return 0;
}

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

を実行するvalgrind--leak-check=full --track-origins=yes、次のエラーが発生します。

==20674== Invalid free() / delete / delete[] / realloc()
==20674==    at 0x4028E58: free (vg_replace_malloc.c:427)
==20674==    by 0x80485B2: destroyEntry (testing.c:53)
==20674==    by 0x8048477: main (testing.c:26)
==20674==  Address 0xbecc0070 is on thread 1's stack

destroyEntryこのエラーは、関数がmainで明示的に割り当てられたメモリを変更できないことを意味していると思います。そうですか?別の関数でfree割り当てたメモリだけではどうしてできないのですか?main(そして、この振る舞いはどういうわけかmainに固有のものですか?)

4

3 に答える 3

52

関数にパラメーターを渡すたびにコピーが作成され、関数はそのコピーで機能します。したがって、あなたの場合、free元のオブジェクトのコピーを作成しようとしていますが、これは意味がありません。

free関数を変更してポインターを取得する必要があります。そうすれば、そのポインターを直接呼び出すことができます。

于 2012-06-17T12:14:41.043 に答える
38

これは値の受け渡しです。つまり、コピーが作成されるため、ローカル変数が存在するメモリを解放しようとしentryます。これentryは、自動保存期間を備えたオブジェクトであり、プログラムが機能の範囲外になると、そのオブジェクトが存在するメモリが自動的に解放されることに注意してくださいdestroyEntry

void destroyEntry(Entry entry)
{
    Entry *entry_ptr = &entry;
    free(entry_ptr);
    return;
}

関数はポインタを取る必要があります(参照渡し):

void destroyEntry(Entry *entry)
{
    free(entry);
}

次に、代わりにdestroyEntry(*(apple));を呼び出しますdestroyEntry(apple);。関数に関連する他の機能がない場合はdestroyEntry冗長であり、直接呼び出す方がよいことに注意してくださいfree(apple)

于 2012-06-17T12:16:04.263 に答える
10

ここでの他の回答は主な問題を指摘しています.main()でdestroyEntryを呼び出すときにリンゴを逆参照するため、参照渡しでコピーが作成されます。

問題がわかったとしても、エラーに戻って、表示されているテキストと問題を関連付けてみると、次回問題が発生したときにすぐに解決できる可能性が高くなります。C および C++ のエラーは、ときどき非常にあいまいに見えることがあります。

一般に、ポインターの解放やオブジェクトの削除に問題がある場合、特にアドレスを割り当てるときと解放しようとするときに、アドレスを出力するのが好きです。valgrind は既に不良ポインターのアドレスを提供していますが、それを正常なポインターと比較するのに役立ちます。

int main()
{
  Entry * apple;
  apple = malloc(sizeof(Entry));
  printf("apple's address = %p", apple);  // Prints the address of 'apple'
  free(apple);   // You know this will work
}

それを行った後、printf() ステートメントは 0x8024712 のようなアドレス (正しい一般的な範囲のアドレスを構成するだけ) を提供しますが、valgrind の出力は 0x4028E58 を提供します。それらが2つの非常に異なる場所にあることに気付くでしょう(実際、「0x4 ...」はスタック上にあり、malloc()が割り当てられるヒープではありませんが、始めたばかりの場合はまだ危険信号ではない)、間違った場所からメモリを解放しようとしていることがわかります。したがって、「無効な free()」です。

そこから、「わかりました、どういうわけか私のポインターが壊れています」と自分に言い聞かせることができます。すでに問題をコンパイル可能な小さな例に煮詰めているので、そこから解決するのにそれほど時間はかかりません。

TL;DR - ポインター関連のエラーが発生した場合は、アドレスを印刷するか、お気に入りのデバッガーで検索してみてください。多くの場合、少なくとも正しい方向に向けられます。

もちろん、これは Stack Exchange に質問を投稿することを思いとどまらせるものではありません。そうすれば、何百人ものプログラマーが恩恵を受けるでしょう。

于 2012-06-20T15:39:44.037 に答える