strcpy がグローバル変数でセグメンテーション違反を引き起こすのはなぜですか?

StackOverflow https://stackoverflow.com/questions/122877

質問

それで、私はいくつかの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 用のスペースを割り当てるのを忘れました。私は好きです strdupmalloc+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() バッファオーバーランを避けるため。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top