ツールはValgrindです。それはあなたのためにあらゆる種類の隠された記憶の問題を見つけるでしょう。
たとえば、このコードは「機能します」。
$ cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *string = calloc(3, sizeof(char));
strcpy(string, "foo");
printf("%s\n", string);
free(string);
return 0;
}
しかし、Valgrindは微妙なoff-by-oneメモリエラーを検出します。
$ make
cc -Wall -g test.c -o test
$ ./test
foo
$ valgrind ./test
==62034== Memcheck, a memory error detector
==62034== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==62034== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==62034== Command: ./test
==62034==
==62034== Invalid write of size 1
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib)
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F2C: main (test.c:9)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
==62034==
==62034== Invalid read of size 1
==62034== at 0x10000B2C8: strlen (vg_replace_strmem.c:470)
==62034== by 0x1001EDA4B: __vfprintf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1002166C0: __v2printf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1001EC381: vfprintf_l (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x1001EA21B: printf (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F42: main (test.c:10)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
==62034==
foo
==62034==
==62034== HEAP SUMMARY:
==62034== in use at exit: 26,553 bytes in 188 blocks
==62034== total heap usage: 273 allocs, 85 frees, 32,788 bytes allocated
==62034==
==62034== LEAK SUMMARY:
==62034== definitely lost: 0 bytes in 0 blocks
==62034== indirectly lost: 0 bytes in 0 blocks
==62034== possibly lost: 0 bytes in 0 blocks
==62034== still reachable: 0 bytes in 0 blocks
==62034== suppressed: 26,553 bytes in 188 blocks
==62034==
==62034== For counts of detected and suppressed errors, rerun with: -v
==62034== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 19 from 19)
摂取するのは大変ですが、重要な行はtest.cとその真上です。最初のメッセージを見てみると...
==62034== Invalid write of size 1
==62034== at 0x10043B5C0: _platform_memmove$VARIANT$Nehalem (in /usr/lib/system/libsystem_platform.dylib)
==62034== by 0x1001B8421: stpcpy (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x10022BBED: __strcpy_chk (in /usr/lib/system/libsystem_c.dylib)
==62034== by 0x100000F2C: main (test.c:9)
==62034== Address 0x100a8f6d3 is 0 bytes after a block of size 3 alloc'd
==62034== at 0x10000A1B9: calloc (vg_replace_malloc.c:715)
==62034== by 0x100000F11: main (test.c:7)
これは、関数のそれにつながる呼び出し、それを引き起こした可能性があるその後、任意の関連するエラーのスタックに続いて、エラーメッセージ、です。
「サイズ1の書き込みが無効です。」は、割り当てられたメモリから1バイト分歩いたことを示しています。 by 0x100000F2C: main (test.c:9)
は、それがstrcpy
であるtest.cの9行目で起こったとし、コールチェーンの上記の行で確認しています(はおそらくstpcpy
のマクロなのでstpcpy
と表示されます)。
"アドレス0x100a8f6d3はサイズ3のブロックの後に0バイトです。"はエラーがおそらく誤ったメモリ割り当ての結果であることを示しています。 by 0x100000F11: main (test.c:7)
は、それがの次の呼び出しで確認されたcalloc
コールであるtest.c行7にあるとします。
これはすべて、私が必要としていたよりも1バイト少ないバイトを割り当てたと言います。 C文字列には末尾のヌル文字があるので、3バイト文字列には4バイト必要です。
(P.S. strcpy
を使用しないでください。)
しかし、私は混乱していますなぜ、時にはコードの仕事、時にはそれがセグメンテーションフォールトを言うこと?
これは、Cを使用すると、任意のメモリを自由に書き換えられるためです。現代のオペレーティングシステムは、少なくとも自分のプロセスのメモリ内にあなたを保ちます。あなたが何か重要なものを上書きしたり、メモリに読み書きしようとすると、あなたがするべきではない...またはあなたがラッキーになることがあるなら、これはあらゆる種類の問題を引き起こす可能性があります!メモリは毎回わずかに異なるように割り当てられているので、メモリの問題を伴うプログラムの実行ごとに動作が異なる場合があります。
上記の私のコード例は、無効なメモリに書き込みを行っても機能します。それは単なる1バイトなので、多分それは幸運になり、誰も気にしないところに書きました。または、おそらくcalloc
は必要以上のメモリを割り当てられました。
ポイントは、valgrind
、または他のメモリチェッカーを学び、実行して、それが破棄されたことを修正します。
(PS valgrind
のためにあなたのOSパッケージをインストールします。オペレーティングシステムのバグや癖のために微調整されます。そうしないと、オペレーティングシステムの独自のコードに関する警告のすべての種類を取得する可能性があります。)
あなたは非常に奇妙な部門を持っています... –
gdbを使ってデバッグしようとすると – Dave
グローバル変数 'counter'は* next *リストの挿入のためにリセットされません。それを 'insertAt'のローカル変数にして、関数の先頭でそれを初期化します。完全に必要でない限り、グローバル変数は使用しないでください。 –