0

malloc された文字列で strtok を使用することについて知っておくべきことはありますか?

私のコードでは(一般的に)

char* line=getline();
Parse(dest,line);
free(line);

wheregetline()は、char * を malloced メモリに返す関数です。とParse(dest, line)は、オンラインで解析を行い、結果を に保存する関数ですdest(以前に他の情報から部分的に埋められています)。

Parse()strtok()オンラインで可変回数呼び出し、いくつかの検証を行います。各トークン ( によって返されるものへのポインターstrtok()) は、自分が持っている数がわかるまでキューに入れられます。

次に、それらは dest の malloc された char** にコピーされます。

ここfree(line) で、dest の char*[] の各部分を解放する関数は、両方とも valgrind で次のように表示されます。

「アドレス 0x5179450 は、解放されたサイズ 38 のブロック内の 8 バイトです」

または似たようなもの。

トークンを char** に直接保存するのではなく、代わりにそれらのコピーを保存するようにコードをリファクタリングすることを検討しています (space == を strlen(token)+1 に malloc し、次に を使用しますstrcpy())。

4

4 に答える 4

2

あなたが尋ねる:

マロックされた文字列でstrtokを使用することについて知っておくべきことはありますか?

知っておくべきことがたくさんあります。まず、文字列を処理するときに文字列を変更し、区切り文字が見つかった場所にstrtok()null()を挿入します。'\0'これは、割り当てられたメモリの問題ではありません(変更可能です!)。定数文字列をに渡そうとすると問題になりますstrtok()

free()第二に、あなたはあなたがするのと同じくらい多くの呼び出しをしなければなりませんmalloc()calloc()しかしrealloc()、カウントを台無しにする可能性があります)。

私のコードには(一般的に)

   char* line=getline();
   Parse(dest,line);
   free(line);

保持するスペースを割り当てない限り、の呼び出し後に構造体(より正確には、構造体内の行へのポインター)をParse()使用することはできません。リリースによって割り当てられたスペースと、その後のポインターの使用により、未定義の動作が発生します。未定義の動作には、「動作しているように見えるが、偶然にのみ」というオプションが含まれていることに注意してください。destdestfree()free()getline()

ここで、getline()はchar *をマロックされたメモリに返す関数であり、Parse(dest、line)はオンラインで解析を行い、結果をdest(他の情報から部分的に以前に入力されたもの)に格納する関数です。

Parse()はstrtok()をオンラインで可変回数呼び出し、いくつかの検証を行います。各トークン(strtok()によって返されるものへのポインター)は、私がいくつ持っているかがわかるまで、キューに入れられます。

strtok()によって返されるポインタはすべて、によって割り当てられたスペースの単一のチャンクへのポインタであることに注意してくださいgetline()。追加のメモリ割り当てについては説明していません。

次に、それらはdestのmallocされたchar**にコピーされます。

これは、ポインタをからポインタの配列にコピーするように聞こえますstrtok()が、それらのポインタが指しているデータのコピーには注意を払っていません。

これで、free(line)とdestのchar * []の各部分を解放する関数は、どちらもvalgrindに次のように表示されます。

"Address 0x5179450 is 8 bytes inside a block of size 38 free'd"

または同様のもの。

free()の' char *[]'部分の最初の部分には、destおそらくへのポインタがあるlineため、メモリのブロック全体が解放されます。の部分での後続のすべての解放は、destによって返されないアドレスを解放しようとしてmalloc()おり、それvalgrindを通知しようとしています。の最初のポインタがすでにそのスペースを解放しているfree(line)ため、操作は失敗します。free()dest

コードをリファクタリングして[...]それらのコピーを保存することを検討しています。

提案されたリファクタリングはおそらく賢明です。他の人がすでに述べた機能strdup()は、きちんとそして確実に仕事をします。

リファクタリング後も行を解放する必要がありますが、によって返されるポインターは解放されないことに注意してくださいstrtok()。これらは、(によって識別される)によって管理されるスペースへの単なるポインタでlineあり、を解放するとすべて解放されますline

strdup()個別に割り当てられた( 'd)文字列のそれぞれと、を介してアクセスされる文字ポインタの配列を解放する必要があることに注意してくださいdest

または、を呼び出した直後に回線を解放しないでくださいParse()dest割り当てられたポインター()を記録し、ポインターのline配列を解放するときにそれを解放します。ただし、によって返されたポインタはまだ解放されませんstrtok()

于 2009-10-17T20:04:47.110 に答える
2

次に、それらはdestのmallocされたchar**にコピーされます。

文字列がコピーされますか、それともポインタがコピーされますか?このstrtok関数は、指定した文字列を変更して、何もコピーせずに同じ文字列へのポインタを提供できるようにします。そこからトークンを取得するときは、それらをコピーする必要があります。それか、トークンポインタのいずれかが使用されている限り、入力文字列を保持します。

strtokエラーが発生しやすいため、多くの人が完全に避けることをお勧めします。また、スレッドを使用していて、CRTがスレッドに対応していない場合はstrtok、アプリがクラッシュする可能性があります。

于 2009-10-17T11:19:49.767 に答える
2

strdupメモリを割り当ててから別の文字列をコピーする関数があります。

于 2009-10-17T12:49:10.053 に答える
0

parse() で 1 を指定すると、strtok() はすべての一致する位置に '\0' のみを書き込みます。実際、このステップは特別なものではありません。strtok() を使うのは簡単です。もちろん、読み取り専用メモリ バッファでは使用できません。

2 parse() で取得した各部分文字列について、それに応じて malloc() されたバッファにコピーします。サブストリングを格納するための簡単な例を挙げると、概念的には以下のコードのように見えますが、実際のコードとまったく同じではない可能性があります。

    char **dest;
    dest = (char**)malloc(N * sizeof(char*));
    for (i: 0..N-1) {
        dest[i] = (char*)malloc(LEN);
        strcpy(dest[i], sub_strings[i]);
    注: 上記の 2 行は、以下のように 1 行である可能性があります
        dest[i] = strdup(sub_string[i]);
    }

3 つの自由な宛先、概念的には次のとおりです。

    for (i: 0..N-1) {
        無料 (dest[i]);
    }
    無料 (宛先);

4 コールフリー(回線)も特別なことではなく、あなたの「宛先」に少しでも影響を与えることはありません。

「dest」と「line」は異なるメモリ バッファを使用するため、必要に応じてステップ 3 の前にステップ 4 を実行できます。上記の手順に従っていれば、エラーは発生しません。コードのステップ 2 でスタックを間違えたようです。

于 2009-10-17T15:10:32.827 に答える