strcpy がグローバル変数でセグメンテーション違反を引き起こすのはなぜですか?
-
02-07-2019 - |
質問
それで、私はいくつかのCコードを持っています:
#include <stdio.h>
#include <string.h>
/* putting one of the "char*"s here causes a segfault */
void main() {
char* path = "/temp";
char* temp;
strcpy(temp, path);
}
これは、見た目どおりにコンパイル、実行、動作します。ただし、文字ポインターの一方または両方がグローバル変数として宣言されている場合、strcpy はセグメンテーション違反になります。なぜこのようなことが起こるのでしょうか?明らかにスコープの理解に誤りがあります。
解決
他の投稿者が述べたように、問題の根本は temp が初期化されていないことです。スタック上で自動変数として宣言されると、そのメモリ位置にたまたま存在するあらゆるガベージが含まれます。どうやら、実行しているコンパイラ+CPU+OSにとって、その場所のガベージは有効なポインタです。strcpy はセグメンテーション違反にならないという点で「成功」していますが、実際には文字列をメモリ内の任意の場所にコピーしました。この種のメモリ破損の問題は、デバッグが非常に難しいため、どこの C プログラマの心にも恐怖を与えます。
一時変数宣言をグローバル スコープに移動すると、それは BSS セクションに配置され、自動的にゼロに設定されます。*temp を逆参照しようとすると、セグメンテーション違反が発生します。
*path をグローバル スコープに移動すると、*temp はスタック上の 1 つ上の位置に移動します。その場所にあるガベージは明らかに有効なポインタではないため、*temp の参照を解除するとセグメンテーション違反が発生します。
他のヒント
temp 変数はストレージ (メモリ) を指しておらず、初期化されていません。
temp が次のように宣言されている場合 char temp[32];
そうすれば、コードはどこで宣言されても機能します。ただし、このように固定サイズで temp を宣言する場合には他にも問題がありますが、それについてはまた別の機会に説明します。
では、なぜローカルではなくグローバルに宣言するとクラッシュするのでしょうか。運...
ローカルで宣言された場合、temp の値は、その時点でスタック上にある値から取得されます。クラッシュを引き起こさないアドレスを指しているのは幸運です。ただし、それは他の人が使用したメモリをゴミ箱に捨てることになります。
グローバルに宣言すると、ほとんどのプロセッサでは、これらの変数はデマンド ゼロ ページを使用するデータ セグメントに格納されます。したがって char *temp
あたかも宣言されているかのように見える char *temp=0
.
temp の割り当てと初期化を忘れました。
temp = (char *)malloc(TEMP_SIZE);
TEMP_SIZE が十分な大きさであることを確認してください。これを実行時に計算して、サイズが十分であることを確認することもできます (少なくとも strlen(path) である必要があります)。
上で述べたように、temp 用のスペースを割り当てるのを忘れました。私は好きです strdup
に malloc+strcpy
. 。それはあなたがやりたいことを実現します。
いいえ、これは変数に関係なく機能しません。運が悪かったために機能したように見えるだけです。変数を初期化しないままにするのではなく、文字列の内容を保存するためのスペースを割り当てる必要があります。
スタック上の初期化されていない変数は、メモリのほぼランダムな場所を指すことになります。これらのアドレスがたまたま有効な場合、コードはそこにあったものをすべて踏みにじることになりますが、エラーは発生しません (ただし、コードの他の場所でメモリ破損に関連した厄介なバグが発生する可能性があります)。
グローバルは通常、マップされていないメモリを指す特定のパターンに設定されるため、常に失敗します。これらを逆参照しようとすると、すぐにセグメンテーション違反が発生します (そのほうが良いです。後で放置しておくと、バグを追跡するのが非常に困難になります)。
最初のアダムの断片を次のように書き換えたいと思います
// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, sizeof(temp), path);
temp[sizeof(temp)-1] = '\0';
そうすれば、次のようになります。
1. don't have magic numbers laced through the code, and
2. you guarantee that your string is null terminated.
2 番目の点は、ソース文字列の長さが 256 文字以上の場合、ソース文字列の最後の文字が失われることです。
注意すべき重要な部分:
宛先文字列 dest は、コピーを受信するのに十分な大きさでなければなりません。
あなたの状況では、tempにはコピー先のメモリが割り当てられていません。
strcpy のマニュアルページからコピー:
DESCRIPTION
The strcpy() function copies the string pointed to by src (including
the terminating '\0' character) to the array pointed to by dest. The
strings may not overlap, and the destination string dest must be large
enough to receive the copy.
を初期化していないため、未定義の動作を呼び出しています。 temp
変数。これはメモリ内のランダムな場所を指すため、プログラムは 5月 動作しますが、おそらくセグメンテーション違反になります。宛先文字列を配列にするか、動的メモリを指すようにする必要があります。
// Make temp a static array of 256 chars
char temp[256];
strncpy(temp, 256, path);
// Or, use dynamic memory
char *temp = (char *)malloc(256);
strncpy(temp, 256, path);
また、使用 strncpy()
の代わりに strcpy()
バッファオーバーランを避けるため。