私は次のコードスニペットは、セグメンテーションフォールトを与えている理由を理解しようとしていますが、はstrtok文字列"this is a test"
を直接指し、内部ではchar
の配列です。 line
をトークン化しても、配列にコピーする必要はありませんか?セグメンテーションフォールト
答えて
問題は、文字列リテラルを変更しようとしていることです。これを行うと、プログラムの動作が未定義になります。
文字列リテラルの変更が許可されていないと言っているのは、単純化すぎです。その文字列リテラルが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との下位互換性について心配していませんでした)
素晴らしい説明!!! – ademar111190
downvoterは説明してくれませんか? –
あなたが言ったように、文字列リテラルは変更できません。これはstrtok
です。あなたは
char str[] = "this is a test";
tokenize(str);
これは、配列str
を作成し、this is a test\0
でそれを初期化し、tokenize
にそれへのポインタを渡しを行う必要があります。
私はあなたがこれについて暴行するだろうと確信しています...しかし、 "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);
}
strtokの安全でない性質を引き起こすならば、strncpyはstrcpyよりはるかに安全です。 strcpyはコンパイル時の定数文字列に対して完全に安全ですが、後のリファクタリングによってstrcpy呼び出しがバッファオーバーフローの脆弱性になる可能性があります。 –
Strokはそれをトークン化するために、その最初の引数を変更します。したがって、タイプconst char *
のようにリテラル文字列を渡すことはできません。したがって、未定義の動作は変更できません。文字列リテラルを変更可能なchar配列にコピーする必要があります。
コンパイル時の定数文字列をトークン化しようとすると、セグメント化エラーが発生します。定数文字列は読み取り専用メモリです。
Cコンパイラはコンパイル時定数文字列を実行可能ファイルに書き込み、オペレーティングシステムはそれらを読み込み専用メモリ(* nix ELFファイルの.rodata)に読み込みます。このメモリは読み取り専用としてマークされており、strtokは渡す文字列に書き込みを行うので、読み取り専用メモリに書き込むためのセグメント化エラーが発生します。
で「...は内部的にはchar
の配列です」のコメントをしていますか?
"this is a test"
が内部的にchar
の配列であるという事実は、まったく変更されません。これはまだ文字列リテラルです(すべての文字列リテラルはcharの変更不可能な配列です)。 strtok
は文字列リテラルをトークン化しようとします。これがクラッシュする理由です。
ちょうどセグメンテーションフォールトエラーは、printfを使用してトークンを印刷しようとしたときに発生しました(cmd
) NULLになった。
- 1. セグメンテーションフォールト
- 2. セグメンテーションフォールト
- 3. セグメンテーションフォールト
- 4. セグメンテーションフォールト
- 5. セグメンテーションフォールト
- 6. セグメンテーションフォールト
- 7. セグメンテーションフォールト()()
- 8. セグメンテーションフォールト
- 9. セグメンテーションフォールト()
- 10. セグメンテーションフォールト
- 11. セグメンテーションフォールト
- 12. Regexc - セグメンテーションフォールト
- 13. SDL_BlitSurfaceセグメンテーションフォールト
- 14. セグメンテーションフォールト・ブロック
- 15. シェルスクリプト - セグメンテーションフォールト
- 16. セグメンテーションフォールトPHP
- 17. セグメンテーションフォールトC
- 18. xdrmem_createセグメンテーションフォールト
- 19. セグメンテーションフォールトC
- 20. ncursesセグメンテーションフォールト
- 21. セグメンテーションフォールトC
- 22. セグメンテーションフォールトC++
- 23. セグメンテーションフォールトが
- 24. Z3セグメンテーションフォールト
- 25. セグメンテーションフォールトOpenMPI
- 26. セグメンテーションフォールト:11
- 27. セグメンテーションフォールト(コアダンプ)
- 28. jonesforthセグメンテーションフォールト
- 29. セグメンテーションフォールト(コアダンプ)
- 30. セグメンテーションフォールトC++
Dude - "this is a test"はSTRING LITERALです。 * READ ONLY * "charの配列"を意味します。特定のプラットフォームでクラッシュすることなく、修正しようとすることで逃げるかもしれません。しかし、それは間違いなく* ANY *プラットフォーム上のno-noです。 – paulsm4