C の範囲外の配列インデックス

問題は、C/C++ が実際には配列に関する境界チェックを行わないことです。有効なメモリにアクセスしていることを確認するのは、OS によって異なります。

この特定のケースでは、スタック ベースの配列を宣言しています。特定の実装によっては、配列の境界外にアクセスすると、既に割り当てられているスタック領域の別の部分にアクセスするだけです (ほとんどの OS とスレッドは、スタック用にメモリの特定の部分を予約しています)。たまたま事前に割り当てられたスタック スペースで遊んでいる限り、すべてがクラッシュすることはありません (動作するとは言っていないことに注意してください)。

最後の行で何が起こっているかというと、スタックに割り当てられたメモリの一部を超えてアクセスしたということです。その結果、プロセスに割り当てられていないか、読み取り専用で割り当てられているメモリの一部にインデックスを作成しています。 OS はこれを認識し、プロセスにセグメント フォールトを送信します。

これが、境界チェックに関して C/C++ が非常に危険である理由の 1 つです。


segfault は、インデックスが範囲外であることを通知する C プログラムの意図したアクションではありません。むしろ、未定義の動作の意図しない結果です。

C および C++ で、

のように配列を宣言すると、
type name[size];

0 からのインデックスを持つ要素にのみアクセスできます size-1まで .その範囲外のものは、未定義の動作を引き起こします。インデックスが範囲に近い場合は、おそらく自分のプログラムのメモリを読み取っています。インデックスの大部分が範囲外である場合、ほとんどの場合、プログラムはオペレーティング システムによって強制終了されます。しかし、あなたは知ることができません、何かが起こる可能性があります.

なぜCはそれを許可するのですか? C と C++ の基本的な要点は、パフォーマンスが犠牲になる場合は機能を提供しないことです。 C および C++ は、非常にパフォーマンスが重要なシステムで古くから使用されてきました。 C はカーネルとプログラムの実装言語として使用されており、メモリ内で隣接するオブジェクトへの高速アクセスを取得するために、配列境界外へのアクセスが役立つ場合があります。コンパイラにこれを禁止させても無駄です。

なぜそれについて警告しないのですか?まあ、警告レベルを高くして、コンパイラの慈悲を期待することができます.これは実装の品質と呼ばれます (QoI)。一部のコンパイラがオープンな動作 (未定義の動作など) を使用して何か良いことを行う場合、その点で実装の品質は良好です。

[[email protected] cpp]$ gcc -Wall -O2 main.c
main.c: In function 'main':
main.c:3: warning: array subscript is above array bounds
[[email protected] cpp]$

代わりに、配列が範囲外にアクセスされたときにハードディスクをフォーマットする場合 (これは合法的ですが)、実装の品質はかなり悪くなります。 ANSI C Rationale ドキュメントでその内容について読むのが楽しみでした。


通常、プロセスが所有していないメモリにアクセスしようとした場合にのみ、セグメンテーション違反が発生します。

a[11] の場合に表示される内容 (そして a[10] ちなみに) は、プロセスが 行う メモリです 所有していますが、a[] には属していません 配列。 a[25000] a[]にはほど遠い 、それはおそらくあなたの記憶の範囲外です.

a[11] の変更 別の変数 (または、関数が返されたときに別のセグメンテーション違反を引き起こす可能性があるスタック フレーム) に暗黙のうちに影響を与えるため、はるかに陰湿です。