1

私はこのコードでセグメンテーション違反を予期していました:

char * foo (char my_ascii[10])
{
  strcpy (my_ascii, "0123456789");

  return my_ascii;
}

char bar[2];

printf("%s\n", foo (bar));

bar はスタックに 2 文字の配列を予約し、foo() は 10 文字を書き込もうとするためです。ただし、printf() は stdout 10 文字で書き込み、エラーは発生しません。なぜこうなった?

さらに、このように foo() 関数を変更すると:

char * foo (char my_ascii[1])
{
  strcpy (my_ascii, "0123456789");

  return my_ascii;
}

動作はまったく同じです。10 文字が my_ascii にコピーされます。説明はありますか?

事前にどうもありがとうございました。

4

5 に答える 5

2

のように配列パラメータの長さを指定する

char * foo (char my_ascii[1]) ...

省略されているため、違いはありません(配列は関数内のポインターに崩壊します)。

さらに、バッファ オーバーフローは未定義の動作です。つまり、プログラムがクラッシュするという保証はありません。問題がなかったかのように完全に法的に見えるかもしれません... または、満月がある木曜日にのみセグメンテーション違反を生成するか、DB からすべてのレコードを静かに削除します。本当に、何でも。

于 2011-03-22T16:08:57.133 に答える
1

まず、これらの定義は完全に同一です。

char *foo1(char arr[10]) { /* ... */ }
char *foo2(char arr[1]) { /* ... */ }
char *foo3(char arr[]) { /* ... */ }
char *foo4(char *arr) { /* ... */ }

第二に、オブジェクトの制限外に書き込むことはUndefined Behaviorです。何でも起れる!運が良ければ、テストの実行がクラッシュし、正しく実行できます。運が悪ければ、クライアント (または上司) にデモを行ったときに失敗するだけで、テストの実行は期待どおりに機能します。

于 2011-03-22T16:10:33.547 に答える
1

char * foo (char my_ascii[10])でありchar * foo (char my_ascii[1])、どちらも同等ですchar * foo (char *my_ascii)

注: 配列型は、関数に渡されると、(配列の最初の要素への) ポインター型に崩壊します。

barスタックに 2 文字の配列を確保し、foo()10 文字を書き込もうとするためです。ただし、printf()stdout 10 文字で書き込み、エラーは発生しません。なぜこうなった?

これは、未定義の動作とは、何でも起こり得ることを意味するためです。

記録のために

未定義の動作とは、この国際規格が要件を課していない、移植不能またはエラーのあるプログラム構造またはエラーのあるデータの使用時の動作を意味します。

注: 未定義の可能性のある動作の範囲は、状況を完全に無視して予測不能な結果を​​もたらすことから、翻訳中またはプログラム実行中に環境に特有の文書化された方法で動作すること (診断メッセージの発行の有無にかかわらず)、翻訳または実行の終了 (診断メッセージの発行)。

于 2011-03-22T16:07:38.083 に答える
0

残念ながら、未定義の動作は、エラーの症状がないことを含め、何かが起こる可能性があることを意味します。この場合、スタックの一部を上書きしましたが、何も影響しませんでした。

于 2011-03-22T16:09:27.077 に答える
0

が 2 文字を予約したのは事実でbarあり、処理できるよりも 8 文字多く入力しています。

それは自動的にセグメンテーション違反を意味するわけではありません。

オーバーフローした 8 文字に何が入っているかわかりません。上書きしても問題ない無意味なゴミである可能性があります。仮想メモリの別のページに実際に上書きしたり、重要なもの (デバイス ドライバーやプログラム コードなど) を上書きしたりすると、セグ フォールトが発生します。

これは未定義の動作の良い例です。未定義は失敗するという意味ではなく、実際には動作が未定義であることを意味します。うまくいくかもしれないし、失敗するかもしれないし、サルが USB ポートから飛び出してくるかもしれない。この場合、実際には動作しますが、次にプログラムを実行したときに異なる可能性があるため、その動作に依存することはできません。

最後に、すぐに障害が発生しないからといって、システムに損傷がなかったとは限りません。上書きでメモリを台無しにした可能性があり、同じメモリ領域に依存していた完全に正常なコードで突然クラッシュしたときに、プログラムのずっと先までそれが表示されない場合があります。


ところで: あなたのコードには別のバグがあります。
10 文字と説明my_asciiしていますが、11 文字をコピーしようとしています。
文字列の末尾にある NULL ターミネータを忘れないでください。
これは、文字列"0123456789"が実際に 11 文字のストレージを必要とすることを意味します。

于 2011-03-22T16:08:19.190 に答える