2012-01-22 12 views
10

私は次のコードスニペットは、セグメンテーションフォールトを与えている理由を理解しようとしていますが、はstrtok文字列"this is a test"を直接指し、内部ではcharの配列です。 lineをトークン化しても、配列にコピーする必要はありませんか?セグメンテーションフォールト

+2

Dude - "this is a test"はSTRING LITERALです。 * READ ONLY * "charの配列"を意味します。特定のプラットフォームでクラッシュすることなく、修正しようとすることで逃げるかもしれません。しかし、それは間違いなく* ANY *プラットフォーム上のno-noです。 – paulsm4

答えて

14

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

文字列リテラルの変更が許可されていないと言っているのは、単純化すぎです。その文字列リテラルがconstであると言うと、間違っています。彼らはそうではありません。

警告:以下の改訂が続きます。

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

文字列リテラルで参照される配列を変更しようとすると、定義されません。constではありませんが、C標準では特に定義されていないと言われています。

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

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

だからなぜではない文字列リテラルconst?実際にそれらを変更しようとすべきではないので、それは確かに意味があります - そしてC++は文字列リテラルを作成しますconst。理由は歴史的です。 constというキーワードは、1989年のANSI C標準で導入される前には存在しませんでした(ただし、それ以前はいくつかのコンパイラで実装されていました)。だから、前ANSIプログラムは次のようになります。print_stringは、文字列はsで指さ変更が許可されていないという事実を強制する方法はありませんでした

#include <stdio.h> 

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

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

。 ANSI Cで文字列リテラルconstを作成すると、既存のコードが壊れてしまい、ANSI C委員会はこれを避けようとしました。それ以来、言語にそのような変更を加える良い機会はありませんでした。 (C++のデザイナー、主にBjarne StroustrupはCとの下位互換性について心配していませんでした)

+0

素晴らしい説明!!! – ademar111190

+1

downvoterは説明してくれませんか? –

2

あなたが言ったように、文字列リテラルは変更できません。これはstrtokです。あなたは

char str[] = "this is a test"; 
tokenize(str); 

これは、配列strを作成し、this is a test\0でそれを初期化し、tokenizeにそれへのポインタを渡しを行う必要があります。

0

私はあなたがこれについて暴行するだろうと確信しています...しかし、 "strtok()"は本質的に安全ではなく、アクセス違反のようなものになりやすいです。

ここで、答えはほぼ確実に文字列定数を使用しています。

代わりにこれを試してみてください:

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

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

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

strtokの安全でない性質を引き起こすならば、strncpyはstrcpyよりはるかに安全です。 strcpyはコンパイル時の定数文字列に対して完全に安全ですが、後のリファクタリングによってstrcpy呼び出しがバッファオーバーフローの脆弱性になる可能性があります。 –

1

Strokはそれをトークン化するために、その最初の引数を変更します。したがって、タイプconst char *のようにリテラル文字列を渡すことはできません。したがって、未定義の動作は変更できません。文字列リテラルを変更可能なchar配列にコピーする必要があります。

2

コンパイル時の定数文字列をトークン化しようとすると、セグメント化エラーが発生します。定数文字列は読み取り専用メモリです。

Cコンパイラはコンパイル時定数文字列を実行可能ファイルに書き込み、オペレーティングシステムはそれらを読み込み専用メモリ(* nix ELFファイルの.rodata)に読み込みます。このメモリは読み取り専用としてマークされており、strtokは渡す文字列に書き込みを行うので、読み取り専用メモリに書き込むためのセグメント化エラーが発生します。

1

で「...は内部的にはcharの配列です」のコメントをしていますか?

"this is a test"が内部的にcharの配列であるという事実は、まったく変更されません。これはまだ文字列リテラルです(すべての文字列リテラルはcharの変更不可能な配列です)。 strtokは文字列リテラルをトークン化しようとします。これがクラッシュする理由です。

0

ちょうどセグメンテーションフォールトエラーは、printfを使用してトークンを印刷しようとしたときに発生しました(cmd) NULLになった。

関連する問題