char s[] ではなく文字列リテラルで初期化された char *s に書き込むと、セグメンテーション違反が発生するのはなぜですか?

C FAQ、質問 1.32 を参照してください


通常、文字列リテラルは、プログラムの実行時に読み取り専用メモリに格納されます。これは、文字列定数を誤って変更するのを防ぐためです。最初の例では、 "string" 読み取り専用メモリと *str に格納されます 最初の文字を指します。最初の文字を 'z' に変更しようとすると、セグメンテーション違反が発生します .

2 番目の例では、文字列 "string" コピー 読み取り専用のホームから str[] へのコンパイラによる 配列。その後、最初の文字の変更が許可されます。これは、それぞれのアドレスを出力することで確認できます:

printf("%p", str);

また、str のサイズを印刷します。 2 番目の例では、コンパイラが 7 バイトを割り当てたことを示しています:

printf("%d", sizeof(str));

これらの答えのほとんどは正しいですが、もう少し明確にするために...

人々が言及している「読み取り専用メモリ」は、ASM 用語のテキスト セグメントです。命令がロードされるメモリ内の同じ場所です。セキュリティなどの明らかな理由から、これは読み取り専用です。文字列に初期化された char* を作成すると、文字列データはテキスト セグメントにコンパイルされ、プログラムはテキスト セグメントを指すようにポインターを初期化します。変更しようとすると、カブーン。セグメンテーション。

配列として書き込まれると、コンパイラは代わりに初期化された文字列データをデータ セグメントに配置します。これは、グローバル変数などが存在するのと同じ場所です。データセグメントには命令がないため、このメモリは変更可能です。今度は、コンパイラが文字配列 (これはまだ単なる char* です) を初期化するときに、実行時に安全に変更できるテキスト セグメントではなく、データ セグメントを指しています。