これは機能します:
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」はポインターであり、いずれかの文字列定数を指す (割り当てる) ことができます。