1. ホーム
  2. スクリプト・コラム
  3. その他

[解決済み】C free(): 無効なポインタ

2022-01-11 07:40:58

質問内容

クエリー文字列をブラウズして、アンパサンドと等号で分割する簡単な関数をC言語で実装したいのですが、以下のようなコードになります。

#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;
}

実行すると、エラーが発生します。

==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)

解決方法は?

メモリアドレスへのポインタでないものを解放しようとしています。アドレスがあるからといって、そのアドレスを解放しなければならないわけではありません。 すべき それを解放する。

あなたが混同しているようなメモリは主に2種類あります。スタックメモリとヒープメモリです。

  • スタック メモリは関数のライブスパンに住んでいます。あまり大きくなってはいけないもののための一時的なスペースです。関数を呼び出すと main を宣言すると、宣言した変数のためのメモリが確保されます ( p , token といった具合に)。

  • ヒープ のメモリが生きています。 malloc から free です。ヒープメモリはスタックメモリよりもずっと多く使うことができます。スタックメモリのように簡単にはいきません。

いくつかエラーがありますね。

  • ヒープメモリでないメモリを解放しようとしています。そんなことはしないでください。

  • メモリブロックの内部を解放しようとしているのですね。実際にメモリブロックを割り当てた場合、それを解放できるのは malloc . ということです。 のみで、ブロックの先頭から . ブロックの内側から一部を解放することはできません。

このコードでは、おそらくメモリの関連部分をどこか別の場所、たとえば確保しておいた別のメモリ・ブロックにコピーする方法を見つけたいのでしょう。あるいは、元の文字列を変更することもできます(ヒント:文字列の値0はヌルターミネーターで、printfなどの関数に文字列の読み取りを停止するように指示します)。

EDITです。 malloc関数は、ヒープメモリ*を割り当てます。

9.9.1 malloc 関数と free 関数

C標準ライブラリはmallocパッケージとして知られる明示的なアロケータを提供します。プログラムはmalloc関数を呼び出すことで、ヒープからブロックを割り当てます。

~コンピュータ・システム:プログラマの視点 第2版』ブライアント&オハラロン、2011年

EDIT 2: * C言語規格では、ヒープやスタックについて何も規定されていません。しかし、関連するデスクトップ/ラップトップ・マシンで学習する人にとっては、特にプログラムがどのように保存され実行されるかについて学習する場合、この区別はおそらく不要で、どちらかといえば混乱させるものです。H2CO3のようにAVRマイクロコントローラのようなものに取り組むことになった場合、私自身の組み込みシステムの経験から、メモリ割り当てをはるかに超えるすべての違いに注意することは間違いなく価値があります。