これは機能します:
int main()
{
char *t = "Hello";
t = "World";
printf("%s", t);
}
しかし、これはセグメンテーション違反を引き起こします:
int main()
{
char *t = "Hello";
strcpy(t, "World"); // the only difference
printf("%s", t);
}
なんで?
たとえば、明示的に定義する文字列"Hello"
は、通常、読み取り専用メモリの領域に配置されます。これらの文字列は変更できません。
最初の例では、"Hello" 文字列を "World" 文字列に変更していません。「Hello」ではなく「World」を指すt
ように再割り当てしています。「Hello」文字列は、読み取り専用メモリ内にそのまま残っています。
初期状態は次のとおりです。
t -> "Hello"
"World"
2 番目の状態は次のとおりです。
"Hello"
t -> "World"
2 番目の例では、"Hello" 文字列を上書きしようとしています。これはできません。
char *t
宣言を からに変更する必要がありますconst char *t
。これを強制するようにGCCを構成できると思います。
最初の例では、ポインタt
は文字列定数を指すように作成され"Hello"
、その後すぐに文字列定数を指すようになります"World"
。後者の値が出力されます。
2 番目の例のコードは、文字列定数が書き込み可能でないため、segfault でクラッシュします。(strcpy は、テキストを保持するメモリを変更しようとします"Hello"
)。でコンパイルされていない限り、GCC は文字列定数を読み取り専用セクションに配置し-fwriteable-strings
ます。
コード
char *test = "Hello";
は、コンパイラとリンカーがバイト文字列 "Hello\0" を読み取り専用セクションに配置し、test
ポイントをその最初の文字に配置することを意味します。このポインターを介して書き込みを試みると、オペレーティング システムによって厳しく処罰されます。
一方で
char test[] = "Hello";
( ) の初期値を持つ 6 文字の配列を宣言します{ 'H', 'e', 'l', 'l', 'o', '\0' }
。
一部の古いプログラムでは、文字列定数が書き込み可能であると想定されていました。したがって、GCC は-fwriteable-strings
コマンド ライン スイッチを使用してこれらのプログラムのコンパイルをサポートする必要があります。
1 つ目は、 の値をt
のアドレスから のアドレスに"Hello"
変更します"World"
。"Hello"
2 番目は、データ自体を上書きしようとします。
読み取り専用の場所にchar *t="Hello"
t
「Hello」を割り当てます。したがって、読み取り専用の場所に書き込むと、セグメンテーション違反が発生します。
割り当てとコピーには違いがあります。
最初の例では、別の文字列のアドレスを に割り当てようとしていますt
。
2 番目の例では、読み取り専用の場所に書き込もうとしています。
char を使用しますt[] = "Hello"
。ここで t は上書きできます
詳しい説明はこちら
割り当てt = "World"
はポインターのみをstrcpy
変更しますが、 は t が指すメモリを変更します。文字列リテラルは、読み取り専用セグメントに存在する場合があります。
char* t
ポインタです。最初の例では、ある文字列リテラルから別の文字列リテラルへのポインターを割り当てているだけです。最初t
に を指し"Hello"
、次に を指してい"World"
ます。これは完全に合法です。
ただし、文字列リテラル自体はリテラルであり、変更することはできません。通常、それらはメモリの読み取り専用セクションにあります。"Hello"
2 番目の例では、文字列リテラルに割り当てられたメモリの内容を で上書きして変更しようとしてい"World"
ます。これは違法であり、セグメンテーション違反が発生します。
「こんにちは」は文字列定数です。constantの定義により、記述されることを意図したものではありません。
最初の例では、「t」はポインターであり、いずれかの文字列定数を指す (割り当てる) ことができます。