4

SO で範囲外のエラーが発生している投稿されたコードを見て、不思議に思いました。コンパイラがこのコードに対して(少なくとも最高レベルで)警告を生成することを期待します

#pragma warning(push,4)
int main(){
    int x[2];
    x[2]=0;     
    return 0;
}
#pragma warning(pop)

しかし、そうではありません。

EDGコンパイラはうまく言っています:

"sourceFile.cpp", line 3: warning:
          subscript out of range
          x[2]=0;
          ^

実際、EDGはもう少し多くのことを言っています(すべて予想されます)

"sourceFile.cpp", line 1: warning: 
          unrecognized #pragma
  #pragma warning(push,4)
          ^

"sourceFile.cpp", line 4: warning: 
          subscript out of range
      x[2]=0;     
      ^

"sourceFile.cpp", line 3: warning: 
          variable "x" was set but never used
      int x[2];
          ^

"sourceFile.cpp", line 7: warning: 
          unrecognized #pragma
  #pragma warning(pop)

しかし、それは私の質問ではありません。

私は、この失敗が VC9 での省略の深刻なエラーを警告していると考えています (auto 変数以来、なおさらです!!!!)。考えを変える重大な理由を教えてくれる人はいますか?

4

7 に答える 7

17

多くのコンパイラには、この種のものをエラーにするオプションがあります。

しかし、これは非常に伝統的であり、C コンパイラがデフォルトでこれを許可するのは適切なことですらあります。これには複数の理由があります。

  1. x[i]i[x]は C で同じことを覚えておいてください。OR"string"[2]を実行し2["string"]ても同じ結果が得られます。それを試してみてください。これは、x[i]is が and として定義される*(x + i)と、C がポインター演算を実行し、式の結果を逆参照するだけで、それが機能するかどうかを決定するコンパイラのドメインにないためです。

  2. ポインター演算が合法であることを考えると、多くの日常的にかなりまともな設計パターンは、実際には技術的な添字違反に依存しています。

    構造体 {
        ...たくさんのもの...
        int ポイント[1]; // そうではない [1]
    };
    ...
    struct s *p = malloc(sizeof (struct s) + someNumber * sizeof(int));
    

    今日、あちこちでこのようなコードが実行されています...    更新:へー、これはstackoverflow の構造体ハックの実際の例です。

于 2009-09-02T00:35:57.753 に答える
8

コンパイラは、未定義の動作 (このような「深刻な」動作であっても) に対して警告を発行する必要はありません。多くのコンパイラには、チェックする傾向があるさまざまな動作セットがあります。VSTS を使用している場合は、有効にできる追加のセキュリティ チェックがいくつかあるので、それによってキャッチされる可能性があると思います。さらに、コンパイラは、このメモリの上書きをキャッチするランタイム チェックを挿入できるため (おそらくデバッグ ビルドの場合)、それらが有効になっていることを確認する必要があります。

于 2009-09-02T00:19:45.357 に答える
7

与えられた例は非常に単純ですが、一般に、コンパイル中に静的解析を適切に行うには、かなりのコードが必要になり、コンパイルが遅くなります (単純な実装は、AST を介した別のパスを意味します)。

コンパイルが遅いことですでによく非難されている言語で。

あなたを馬鹿にすることは、C++ の一部です。自分自身からあなたを救おうとするコンパイラは素晴らしいですが、それは面倒です。

FWIW:g++ -Wall警告にも失敗します。

于 2009-09-02T01:07:25.747 に答える
5

この警告は、ソースに対して静的コード分析が実行されるときに発行されます。ただし、静的コード分析はコンパイラ仕様の一部ではなく (少なくとも私の知る限り)、別のツールによって実行されます。

C/C++ コード解析の概要は次のとおりです。ツールがカバーする警告のリスト。

于 2009-09-02T00:34:51.637 に答える
3

これについて警告しない理由は、ほとんど役に立たないからです。あなたのコードを見る:

int main(){
    int x[2];
    x[2]=0;     
    return 0;
}

この場合、コンパイラは警告を発行できることがわかりますがそれは次の理由のみです。

  • 配列はまだポインターに減衰していません。つまり、サイズ情報はまだ利用可能です。
  • コンパイル時の定数式を使用して配列にインデックスを付けています。

ほとんどの実際のコードでは、これら 2 つの条件は成立しません。配列はほとんど常にポインターであり、その場合、コンパイラーはサイズ情報をまったく持っていません。同様に、多くの場合、実行時に決定される値を使用して配列にインデックスを付けます。繰り返しますが、これを行うと、コンパイラは範囲外になる可能性があるかどうかを判断できません。

言い換えれば、はい、コンパイラはこの場合に警告を発行することができます.

代わりに、コードが次のようになっている場合:

void foo(int* x){
    x[2]=0;     
}

またはこれ:

void foo(int i){
    int x[2];
    x[i]=0;
}

コンパイラは無力だったでしょう。そして、それらのケースははるかに一般的です。他の人が既に述べたように、C++ コンパイラーの最大の問題の 1 つは、既に非常に遅いことです。チェックしなければならない新しい警告ごとに、オーバーヘッドが追加されます。では、基本的にこのような小さなおもちゃの例でのみ発生するエラーに対して警告を追加する価値はありますか?

なぜそんなに悪い反応が返ってきたのかについては、おそらくあなたの質問に答えがあります:

私は、この失敗がVC9での省略の深刻なエラーを警告していると考えています (auto 変数 **!!!!** 以来、なおさらです)。考えを変える重大な理由を教えてくれる人はいますか?

ロードされた言語を減らします。単一の感嘆符に固執します。あなたがヒューズを飛ばそうとしているように聞こえる場合、人々はあなたが激怒していると思い込み、過剰反応しているので黙っているべきだと言うでしょう.

于 2009-09-08T21:52:03.980 に答える
0

注目に値するかもしれないいくつかのこと:

  1. すべての最適化をオフにしない限り、これは何もコンパイルされません。メモリにはまったく書き込まれません-次のように書いた方がよいでしょう:

    #pragma warning(push,4)
    int main(){
        return 0;
    }
    #pragma warning(pop)
    
  2. 他の場所で述べたように、このような分析を行うことは困難です (計算不可能な解決しようとしている停止問題のように)。最適化を行うときは、分析が容易な場合にのみ実行しても問題ありません。警告を探している場合、簡単なケースを見つけることができるかどうかと、人々が警告に依存するようにするかどうかとの間にはトレードオフがあります。どの時点で警告を停止しても問題ありませんか? はい、定数オフセットによってアクセスされるローカルで宣言された変数は簡単です-しかし、簡単であるため、それほど重要ではありません。アクセスが簡単にインライン化された関数内にある場合はどうなりますか? または、アクセスが一定の境界を持つ for ループ内にある場合は? これらはどちらも簡単に探すことができますが、それぞれがまったく新しい一連のテスト、考えられるリグレッションなどを表しており、ほとんどメリットがありません (存在する場合)。

この警告が役に立たないと言っているのではありません。あなたが思っているほど明確ではありません。

于 2009-09-08T18:50:49.800 に答える
0

コンパイラはあなたのベビーシッターではないからです。

于 2009-09-02T00:28:28.310 に答える