私は自分自身にCを教えています。私の目標は、クエリ文字列をたどってアンパサンドと等号で分割するC関数を作成することです。 Valgrindからこのエラーが発生します。
==5411== Invalid free() / delete / delete[] / realloc()
==5411== at 0x402AC38: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5411== by 0x804857C: main (leak.c:28)
==5411== Address 0x420a02a is 2 bytes inside a block of size 8 free'd
==5411== at 0x402AC38: free (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==5411== by 0x804857C: main (leak.c:28)
==5411==
==5411==
==5411== HEAP SUMMARY:
==5411== in use at exit: 0 bytes in 0 blocks
==5411== total heap usage: 1 allocs, 2 frees, 8 bytes allocated
==5411==
==5411== All heap blocks were freed -- no leaks are possible
==5411==
==5411== For counts of detected and suppressed errors, rerun with: -v
==5411== ERROR SUMMARY: 20 errors from 9 contexts (suppressed: 0 from 0)
そしてバックトレース:
*** Error in `./leak': free(): invalid pointer: 0x08c1d00a ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x767c2)[0xb75f17c2]
/lib/i386-linux-gnu/libc.so.6(+0x77510)[0xb75f2510]
./leak[0x804857d]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0xb7594905]
./leak[0x8048421]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:05 262764 /home/danny/dev/c-qs-parser/leak
08049000-0804a000 r--p 00000000 08:05 262764 /home/danny/dev/c-qs-parser/leak
0804a000-0804b000 rw-p 00001000 08:05 262764 /home/danny/dev/c-qs-parser/leak
08c1d000-08c3e000 rw-p 00000000 00:00 0 [heap]
b757a000-b757b000 rw-p 00000000 00:00 0
b757b000-b7729000 r-xp 00000000 08:05 1312132 /lib/i386-linux-gnu/libc-2.17.so
b7729000-b772b000 r--p 001ae000 08:05 1312132 /lib/i386-linux-gnu/libc-2.17.so
b772b000-b772c000 rw-p 001b0000 08:05 1312132 /lib/i386-linux-gnu/libc-2.17.so
b772c000-b772f000 rw-p 00000000 00:00 0
b772f000-b774a000 r-xp 00000000 08:05 1312589 /lib/i386-linux-gnu/libgcc_s.so.1
b774a000-b774b000 r--p 0001a000 08:05 1312589 /lib/i386-linux-gnu/libgcc_s.so.1
b774b000-b774c000 rw-p 0001b000 08:05 1312589 /lib/i386-linux-gnu/libgcc_s.so.1
b774c000-b7750000 rw-p 00000000 00:00 0
b7750000-b7751000 r-xp 00000000 00:00 0 [vdso]
b7751000-b7771000 r-xp 00000000 08:05 1312116 /lib/i386-linux-gnu/ld-2.17.so
b7771000-b7772000 r--p 0001f000 08:05 1312116 /lib/i386-linux-gnu/ld-2.17.so
b7772000-b7773000 rw-p 00020000 08:05 1312116 /lib/i386-linux-gnu/ld-2.17.so
bfe93000-bfeb4000 rw-p 00000000 00:00 0 [stack]
Aborted (core dumped)
最後にコードがあります:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
//char p[] = "t=quote&k=id&v=10";
char p[] = "t=quote";
char* token;
char* tk;
char* s;
unsigned short int found;
s = strdup(p);
if (s != NULL) {
while ((token = strsep(&s, "&")) != NULL) {
found = 0;
printf("TOKEN: %s\n\n", token);
while ((tk = strsep(&token, "=")) != NULL) {
printf("TK: %s\n\n", tk);
free(tk);
}
free(token);
}
}
free(s);
return 0;
}
ありがとう
「解放可能な」メモリアドレスへのポインタではない何かを解放しようとしています。何かがアドレスだからといって、それが必要なわけではなく、should解放する必要があるわけでもありません。
紛らわしいと思われるメモリには、スタックメモリとヒープメモリの2種類があります。
スタックメモリは関数のライブスパンに存在します。大きくなりすぎないようにするための一時的なスペースです。関数main
を呼び出すと、宣言した変数(p
、token
など)のメモリが確保されます。
ヒープメモリは、malloc
からfree
に至るまで存続します。スタックメモリよりもはるかに多くのヒープメモリを使用できます。また、それを追跡する必要があります-それはスタックメモリのように簡単ではありません!
いくつかのエラーがあります:
ヒープメモリではないメモリを解放しようとしています。しないでください。
メモリブロック内を解放しようとしています。実際にメモリブロックを割り当てた場合、malloc
によって返されたポインタからのみ解放できます。つまり、ブロックの先頭からのみです。ブロックの一部を内側から解放することはできません。
ここでのコードの一部については、メモリの関連部分を別の場所にコピーする方法を見つけたいと思うでしょう。たとえば、別のメモリブロックを確保しておきます。または、必要に応じて元の文字列を変更できます(ヒント:char値0はnullターミネーターであり、printfなどの関数に文字列の読み取りを停止するように指示します)。
EDIT: malloc関数はヒープメモリを割り当てます*。
「9.9.1 mallocおよびfree関数
C標準ライブラリは、mallocパッケージと呼ばれる明示的なアロケーターを提供します。プログラムは、malloc関数を呼び出して、ヒープからブロックを割り当てます。」
〜Computer Systems:A Programmer's Perspective、2nd Edition、Bryant&O'Hallaron、2011
編集2: * C標準は、実際には、ヒープまたはスタックについて何も指定していません。ただし、関連するデスクトップ/ラップトップマシンで学習している人にとっては、特にプログラムがどのように保存および実行されるかを学習している場合、区別はおそらく不要で混乱します。 H2CO3のようにAVRマイクロコントローラーのようなもので作業していることに気付いたとき、組み込みシステムでの私自身の経験から、メモリー割り当てをはるかに超えて広がるすべての違いに注意することは間違いなく価値があります。
どこからfree(token)
とfree(tk)
が必要だと思いましたか?あなたはしません。 strsep()
はメモリを割り当てず、元の文字列内のポインターのみを返します。もちろん、これらはmalloc()
(または同様の)によって割り当てられたポインタではないため、free()
_ ingは未定義の動作です。文字列全体を使い終わったときのみ、free(s)
が必要です。
また、この例では、動的なメモリ割り当てまったくは必要ないことに注意してください。 strdup()
とfree()
を完全に避けるには、単にchar *s = p;
。
free
から返されたポインターに対してstrsep
を呼び出すことはできません。これらは個別に割り当てられた文字列ではなく、割り当て済みの文字列s
へのポインタです。 s
の処理がすべて完了したら、解放する必要がありますが、strsep
の戻り値を使用する必要はありません。