web-dev-qa-db-ja.com

別の機能で割り当てられた空きメモリ

私は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;
}

--leak-check=full --track-origins=yesvalgrindを介して実行すると、次のエラーが表示されます。

==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だけではできないのはなぜですか? (そして、この動作は何らかの形でメインに固有ですか?)

65
Jeff Tratner

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

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

51

これは値渡しです。つまり、コピーが作成されるため、ローカル変数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)を直接呼び出すことをお勧めします。

37
LihO

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

問題を知ったとしても、エラーに戻って、見ているもののテキストを問題に結び付けようとすると、次回問題が発生したときにすぐに解決できる可能性が高くなります。 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に投稿することを妨げるものではありません。何百人ものプログラマーがあなたのそうすることから利益を得るでしょう。

9
gkimsey