3

奇妙なことに、最適化がオンになっている場合にのみ発生するバグを見つけました ( g++ -O2)。Arithmetic exception次のコードでは、(intervalコマンド ライン引数から) がゼロに設定されていました。

for(int i = 0; i < n; ++i) {
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

モジュロ ゼロ演算がゼロ除算の例外をスローしたことは明らかですが、最適化を有効にしてコードをコンパイルした場合にのみこれが発生したのはなぜでしょうか?

4

4 に答える 4

13

ゼロ除算は常に未定義の動作です。異なる最適化設定で異なる結果が得られるという事実は、依然として未定義の動作の定義内に収まります。

于 2010-10-04T01:51:09.033 に答える
1

一定の折り畳み。

interval をグローバルな const int として宣言すると、コンパイラはあなたの言葉を受け入れました。

于 2010-10-04T02:14:04.267 に答える
0

'interval'が設定される場所は表示されません。オプティマイザーは、「interval」を0に設定する何かを実行している可能性があります。コードを次のように変更します。

for(int i = 0; i < n; ++i) {
  if (0==interval) { break; }
  if((i + 1) % interval == 0) { // exception here
    DoSomething();
  }
}

それでもエラーが発生するかどうかを確認してください。または、さらに良いことに、「間隔」がその値を取得している場所を示してください。

于 2010-10-04T01:58:08.337 に答える
0

問題を示す例を挙げていただけますか? 最適化によって結果が変わる場合は、コードを逆アセンブルして違いを比較する必要があります。ターゲット プラットフォームは何ですか? x86、アーム、ppc? オペレーティング·システム?等?

#含む
const int interval=BOB;
int main (ボイド)
{
    int i,n;
    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % 間隔 == 0)
        { // ここで例外
            printf("%d\n",i);
        }
    }
    リターン (0);
}
gcc interval.c -DBOB=0 -O2 -o interval
interval.c: 関数 'main' 内:
interval.c:15: 警告: ゼロ除算

コンパイラはそれを理解しました...

編集:

コマンドライン引数から割り当てようとすると、実行するものが何もないため、コンパイラエラーが発生するはずです。

#include <stdio.h>
const int 間隔;
int main ( int argc, char *argv[] )
{
    int i,n;
    if(argc<2) return(1);
    interval=atoi(argv[1]);

    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % 間隔 == 0)
        { // ここで例外
            printf("%d\n",i);
        }
    }
    リターン (0);
}
gcc -o インターバル interval.c
interval.c: 関数 'main' 内:
interval.c:7: エラー: 読み取り専用変数 'interval' の割り当て

完全な例を提供してください。

const を使用してコンパイラを機能させることは、変数が間違ったアドレスから取り出され、そのアドレスと残りのすべてのコードに応じてゼロになる場合とそうでない場合があることを意味する可能性があります。 . 最適化設定を変更すると、そのアドレスがどこにあるのか、何を指すのか、またはその時点までの実行中に変更されたものに移動し、結果が変わります。

編集:

#include <stdio.h>
int main ( int argc, char *argv[] )
{
const int 間隔;
    int i,n;
    if(argc<2) return(1);
    interval=atoi(argv[1]);

    n=10;
    for(i = 0; i < n; ++i)
    {
        if((i + 1) % 間隔 == 0)
        { // ここで例外
            printf("%d\n",i);
        }
    }
    リターン (0);
}

gcc -c interval.c
interval.c: 関数 'main' 内:
interval.c:7: エラー: 読み取り専用変数 'interval' の割り当て

コンパイラは、それが読み取り専用変数であることをまだ認識しています。アドレスを使用して非 const 変数を指すようにしても、読み取り専用状態は変更されず、コンパイラ エラーが解消され、長期的には失敗します。たとえば、.text が読み取り専用メモリ (rom/flash) に配置されている場合、設計どおり、const を削除して読み取り/変数を書き込みます。とにかく、そのようなポインター操作は大罪です。なぜなら、最適化すると失敗する可能性があり、最終的に失敗するからです(本当に優れたコンパイラーを使用し、必ずしもgccではない場合、gccでも失敗します)(99.999999999999%の時間それが機能するのは幸運ですが、失敗してコンパイラや言語ではなくソフトウェア設計を指している場合は非常に説明できます)。const がこの質問の根本的な原因でない限り、const を削除して、問題を示す完全な例を示してください。午後または 1 日以内にこれを閉じることができます。

編集2:

unsigned int fun ( unsigned int a )
{
    const unsigned int b = 7;
    *(unsigned int *)&b = 5;
    戻ります(a+b);
}

上記を最適化してコンパイルすると、次のようになります。

    .グローバルな楽しみ
楽しい:
    r0、r0、#7 を追加
    bx lr

予想通り、const は b を読み取り専用にします。const なし:

unsigned int fun ( unsigned int a )
{
    unsigned int b = 7;
    *(unsigned int *)&b = 5;
    戻ります(a+b);
}
    .グローバルな楽しみ
楽しい:
    r0、r0、#5 を追加
    bx lr

私は驚いていますが、const がどのように機能するかを示しています。

于 2010-10-04T05:24:23.693 に答える