私は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=yes
でvalgrind
を介して実行すると、次のエラーが表示されます。
==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
だけではできないのはなぜですか? (そして、この動作は何らかの形でメインに固有ですか?)
パラメーターを関数に渡すと、コピーが作成され、そのコピーで関数が機能します。したがって、あなたの場合、元のオブジェクトのコピーをfree
しようとしていますが、これは意味がありません。
ポインターを取得するように関数を変更する必要があります。その後、そのポインターでfree
を直接呼び出すことができます。
これは値渡しです。つまり、コピーが作成されるため、ローカル変数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)
を直接呼び出すことをお勧めします。
ここでの他の回答は、主な問題を指摘しています。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に投稿することを妨げるものではありません。何百人ものプログラマーがあなたのそうすることから利益を得るでしょう。