1

やあ。これが難問です。私はこのコードを持っています:

#include<stdio.h>
#include<conio.h>
#include<string.h>
int main(){

char a[5];
char b[5];

memset(a, 0, 5); 
memset(b, 0,5);

strcpy(a, "BANG");

printf("b = "); 

scanf("%s", &b);
printf("a = %s\n", a);  
getch();
}

実行すると、十分に長い文字列を に読み込むとb、 の値もa変化することがわかります。あなたはそれが「BANG」のままであることを期待するでしょうが、それは起こりません。これについて説明をいただきたいと思います。ありがとうございました!

4

7 に答える 7

2

文字列が十分に長い場合、バッファ オーバーランが発生し、他の配列の上書きやアプリケーションのクラッシュなどの動作が未定義になります。動作は定義されていないため、回避する必要がありますが、理解のために、コンパイラはメモリ内の配列のa後に配列をレイアウトしました(この特定のコンパイラの実行では)。bあなたが書くとき、b+sizeof(b)あなたはに書いていa[0]ます。

于 2012-10-16T14:26:29.977 に答える
2

「バッファオーバーフロー」を作成しています。配列は 5 バイト (4 文字と標準の C 文字列ターミネータ) のみを保持するようにサイズ設定されており、それ以上のバイトを配置すると、残りがあふれてしまいます。

通常、重要な何かに影響を与え、プログラムをクラッシュさせます。

valgrindこの種のバグを検出するための自動ツール ( など) があります。

于 2012-10-16T14:25:45.750 に答える
2

おめでとうございます。最初のバッファ オーバーフローに遭遇しました (最初に気付いたのは :) )。

配列はプログラムのスタックに割り当てられ、これらの配列は隣接しています。C は配列境界の違反をチェックしないため、任意の配列のセルとしてメモリの許可された部分にアクセスできます。

非常に一般的なランタイムの例を見てみましょう。このプログラムは x86 で実行されています。x86 のスタックは最小アドレスまで成長しているため、通常、コンパイラはスタックa[]b[]上に配置します。にアクセスしようとすると、 、isなどとb[5]同じアドレスになります。a[0]b[6]a[1]

バッファ オーバーフロー エクスプロイトは次のように機能します。一部の不注意なプログラマがバッファ内の文字列サイズをチェックせず、悪意のあるハッカーが悪意のあるコードをスタックに書き込んで実行します。

于 2012-10-16T14:32:00.150 に答える
2

プログラムのメモリの観点から考えてみてください。 aは 5 文字のb配列、 は 5 文字の配列です。スタック上の次のようなもの:

[0][0][0][0][0][0][0][0][0][0]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

したがって、「bang」の strcpy を実行すると、次のようになります。

[0][0][0][0][0][B][A][N][G][0]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

「長い」文字列を に入れると、次のようになりますb

[T][h][i][s][I][s][l][o][n][g]
 ^              ^
 |              +--"a" something like 0xbfe69e52
 +-----------------"b" something like 0xbfe69e4d

おっと、ちょうど失われましたa。オーバーフローしたため、これは「バッファ オーバーフロー」ですb(aこの場合は to に)。Cはあなたがそれをするのを止めるつもりはありません。

于 2012-10-16T14:42:45.693 に答える
1

上記の誰もが言及するのを忘れているように思われることの1つは、スタックは通常、予想とは逆の方向に処理されるという事実です。

事実上、「a」の割り当ては、現在のスタックポインタ(x86/x64ではesp/rsp)から5バイトを減算します。次に、「b」の割り当てにより、さらに5バイトが減算されます。

したがって、最初のスタック割り当てを行うときに、espが0x1000であるとしましょう。これにより、「a」にメモリアドレス0xFBが与えられます。'b'は0xF6を取得するため、0xF6の6番目のバイト(つまりインデックス5)は0xF6 + 5または0xFBであるため、aの配列に書き込みます。

これは、次のコードで簡単に確認できます(32ビットを想定)。

printf( "0x%08x\n", a );
printf( "0x%08x\n", b );

bのメモリアドレスがaよりも低いことがわかります。

于 2012-10-16T14:49:00.697 に答える
0

C はメモリ アクセスの境界チェックを行わないため、配列の宣言された末尾を超えて自由に読み書きできます。ab、宣言の逆順であってもメモリ内で隣接する可能性があるため、たとえば に属する文字よりも多くの文字を読み取ら ないbようにコードで注意しないと、 が破損する可能性がありますa。実際に何が起こるかは未定義であり、実行ごとに変わる可能性があります。

scanfこの特定のケースでは、フォーマット文字列の幅を使用して読み取る文字数を制限できることに注意してください。scanf("%4s", &b);

于 2012-10-16T14:26:06.270 に答える
0

b は 5 文字しかありません。したがって、より長い文字列を書き込むと、b に隣接するメモリを書き込むことになります。

于 2012-10-16T14:30:34.830 に答える