1. ホーム
  2. c

[解決済み] strtokのセグメンテーションフォールト

2022-01-22 20:18:46

質問内容

以下のコードの断片がセグメンテーションフォールトを出す理由を理解しようとしています。

void tokenize(char* line)
{
   char* cmd = strtok(line," ");

   while (cmd != NULL)
   {
        printf ("%s\n",cmd);
        cmd = strtok(NULL, " ");
   } 
}

int main(void)
{
   tokenize("this is a test");
}

strtok() は実際には文字列リテラルに対してトークン化しないことは知っていますが、この場合。 line は直接文字列 "this is a test" の配列で、内部的には char . をトークン化する方法はありますか? line 配列にコピーすることなく?

どのように解決するのですか?

問題は、文字列リテラルを変更しようとしていることです。 そうすると、プログラムの動作が不定になります。

文字列リテラルを変更してはいけないというのは、単純化しすぎです。 文字列リテラルは const は正しくありません。

警告。 以下、脱線します。

文字列リテラル "this is a test" は、型 char[15] (長さが14で、終端である '\0' ). この文脈を含むほとんどの場合,このような式は暗黙のうちに配列の最初の要素へのポインタに変換され,その型は char* .

文字列リテラルが参照する配列を変更しようとした場合の動作は未定義です。 const (C標準では未定義と明記されているからです。

コンパイラによっては、これを許してくれるかもしれません。 あなたのコードは実際にリテラルに対応する静的配列を変更するかもしれません(これは後で大きな混乱を引き起こす可能性があります)。

しかし、最近のほとんどのコンパイラは、物理的なROMではなく、仮想メモリシステムによって変更から保護されているメモリの領域に、読み取り専用メモリに配列を格納します。 このようなメモリを変更しようとすると、通常セグメンテーションフォールトが発生し、プログラムがクラッシュします。

では、なぜ はありません。 文字列リテラル const ? このように、文字列を変更することは避けたいものですが、C++では文字列リテラルを作成することができます。 const . その理由は歴史的なものです。 歴史的な理由で const キーワードは、1989年のANSI C規格で導入される以前には存在しませんでした(ただし、それ以前に一部のコンパイラで実装されていたと思われます)。 ですから、ANSI以前のプログラムは次のようなものです。

#include <stdio.h>

print_string(s)
char *s;
{
    printf("%s\n", s);
}

main()
{
    print_string("Hello, world");
}

ということを強制する方法がありませんでした。 print_string が指す文字列を変更することは許されていません。 s . 文字列リテラルの作成 const ANSI Cの委員会は、既存のコードを破壊することを避けようとしました。 それ以来、言語にそのような変更を加える好機はありませんでした。 (C++の設計者、主にBjarne Stroustrupは、Cとの後方互換性についてはそれほど気にしていませんでした)。