なぜこの単純な文字列の割り当てはセグメンテーション違反ですか?
-
03-07-2019 - |
質問
I <!>#8217;次のコードを入手しました:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
cout << a << ", " << b << endl;
return 0;
}
これはコンパイルして動作します。 bar,
bar
を出力します。ここで、文字列をコピーしないことを説明します。 b
を変更し、a
も変更されることを示したいと思います。この単純なコードを思いつきました:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
b[1] = 'u'; // ← just this line added
cout << a << ", " << b << endl;
return 0;
}
<!>#8230;しかし、それはセグメンテーション違反です。どうして?興味深いのは、次の変更が正常に実行されることです:
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char b[] = "bar"; // ← declaration changed here
a = b;
b[1] = 'u';
cout << a << ", " << b << endl;
return 0;
}
なぜ<!>#8217;前のようにセグメンテーション違反ではないのですか?ポインタスタイルと配列スタイルの文字列の初期化の重要な違いを見逃していると思います。
解決
文字列定数を変更することはできません。これは、最初のコードサンプルのように、リテラルへのポインター構文を使用した場合に得られるものです。
この質問も参照してください:文字列リテラルです静的メモリに作成されたC ++で?。
他のヒント
これを書くとき:
char *b = "bar";
コンパイラは、文字列リテラル<!> quot; bar <!> quot;を格納するための匿名(名前のない)メモリ領域を割り当てます。文字列リテラルは変更できないため、コンパイラは(リンカーとオペレーティングシステムの助けを借りて)実行中のプログラムの書き込み保護されているメモリスペースの一部に文字列リテラルを配置します。変更しようとすると、オペレーティングシステムがそれをキャッチし、プログラムでセグメンテーションエラーが発生します。
(コードはCではなくC ++ですが、これはこの質問とは無関係です。)
書くとき:
char *foo = "bar";
実際に起こるのは、<!> quot; bar <!> quot;メモリの読み取り専用セグメントに保存されます。したがって、不変です。読み取り専用セグメントを変更しようとするため、セグメンテーション違反が発生します。
ポインタの値を出力することにより、「a」が変更されたことを示すこともできます。
#include <iostream>
using namespace std;
int main()
{
char* a = "foo";
char* b = "bar";
a = b;
cout << (void*)a << ", " << (void*)b << endl;
}
これにより、「a」と「b」が指すアドレスが印刷されます。
演算子<!> lt; <!> lt;のため、 'void *'にキャストする必要があります。 'char *'に対してオーバーロードされ、他のポインターがアドレスを出力する文字列を出力します。
理論上、文字列リテラルはchar *に割り当てられるべきではなく、 'const char *'のみに割り当てられるべきです。その後、コンパイラは、セグフォールトコードを記述する前に停止します。
この違いはおそらくコンパイラ固有のものです。ポイントを示すには、mallocを使用してバッファーを割り当て、文字列をこのバッファーにコピーし、文字列が不要になったらfreeを使用することを忘れないでください。