範囲外の配列にアクセスしてもエラーが発生しないのはなぜですか? 質問する

範囲外の配列にアクセスしてもエラーが発生しないのはなぜですか? 質問する

次のように、C++ プログラムで範囲外の値を割り当てています。

#include <iostream>
using namespace std;
int main()
{
    int array[2];
    array[0] = 1;
    array[1] = 2;
    array[3] = 3;
    array[4] = 4;
    cout << array[3] << endl;
    cout << array[4] << endl;
    return 0;
}

プログラムは3と を出力します4。これはあり得ないはずです。私は g++ 4.3.3 を使用しています。

コンパイルと実行のコマンドは次のとおりです

$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4

割り当て時にのみarray[3000]=3000セグメンテーション違反が発生します。

gcc が配列の境界をチェックしない場合、後で深刻な問題を引き起こす可能性があるので、プログラムが正しいことをどのように確認すればよいのでしょうか?

上記のコードを

vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout << vint[5] << endl;

これもエラーは発生しません。

ベストアンサー1

すべての C/C++ プログラマーの親友、未定義の動作へようこそ。

さまざまな理由により、言語標準で指定されていないことがたくさんあります。これはその 1 つです。

一般に、未定義の動作に遭遇すると、何が起きても不思議ではありません。アプリケーションがクラッシュしたり、フリーズしたり、CD-ROM ドライブが取り出されたり、鼻から悪魔が出てくるかもしれません。ハードドライブがフォーマットされたり、ポルノ画像がすべて祖母にメールで送信されたりするかもしれません。

本当に運が悪ければ、正しく動作しているように見えることもあります。

この言語は、配列の範囲内の要素にアクセスした場合に何が起こるかだけを規定しています。範囲外になった場合に何が起こるかは未定義のままです。現在、コンパイラでは動作しているように見えるかもしれませんが、これは C または C++ の正規言語ではなく、次にプログラムを実行したときにも動作するという保証はありません。または、現時点でも重要なデータが上書きされておらず、それが引き起こす問題にまだ遭遇していないという可能性もあります。

境界チェックがない理由については、答えにはいくつかの側面があります。

  • 配列は C の名残です。C 配列は、可能な限り原始的です。連続したアドレスを持つ要素のシーケンスにすぎません。生のメモリを公開するだけなので、境界チェックはありません。堅牢な境界チェック メカニズムを C で実装することはほぼ不可能でした。
  • C++ では、クラス型で境界チェックが可能です。しかし、配列は依然として C 互換の単純な配列です。クラスではありません。さらに、C++ は境界チェックを理想的ではないものにする別のルールに基づいて構築されています。C++ の基本原則は、「使用しないものにはコストを支払わない」です。コードが正しい場合は境界チェックは不要であり、実行時の境界チェックのオーバーヘッドに対してコストを支払う必要はありません。
  • そのため、C++ ではstd::vector、両方を可能にするクラス テンプレートを提供しています。operator[]は、効率性を重視して設計されています。言語標準では、境界チェックを実行する必要はありません (ただし、禁止もされていません)。ベクターには、境界チェックの実行が保証されているat()メンバー関数もあります。したがって、C++ では、ベクターを使用すると、両方の長所を活用できます。境界チェックなしで配列のようなパフォーマンスが得られ、必要なときに境界チェックされたアクセスを使用できるようになります。

おすすめ記事