2011-01-22 8 views
1

私はこれに2日間答えを探しましたが、成功しませんでした。私はこの問題に遭遇したことがないので、私はベストを尽くします。私と一緒に抱きしめてください。C++コードが最適化されずにスキップされることがあります。なぜどんなアイデア?

私は1年以上前に作成したC++プロジェクトに戻ってきましたが、その時点では問題なく動作していました。私は同じプログラムを動かそうとしていた頃、この面白いと信じられないほど面倒な問題に遭遇しました。

ファイル.h一定の数字は、私はこの例では、拡張16進表記のマクロた

... 
short id; 
... 

file.cc

id = 0; 
while (id < some_large_number) 
{ 
    id = foo(); 
    if (id == 2) 
    { 
     //do something 
    } 
    else if (id == 2900) 
    { 
     //do something 
    } 
    else if (id == 30000) 
    { 
     //do something 
    } 
    else if (id == 40000) 
    { 
     //do something 
    } 
    else if (id == 45000) 
    { 
     //do something 
    } 
    else 
    { 
     //do something else 
    } 
} 

:コードのようなものでした。これは本当にバグだったが、デバッガは簡単に見つけられなかったことが分かる。

GDB(最適化なし)を使用してコードをステップ実行しようとしていたときに、毎回if (id == 30000)に達した後、GDBがelseステートメントに直接ジャンプすることに気づきました。数字は16進表記のcマクロであるため、最初に40000signed shortの制限を超えていることに気付かなかった。これは非常に誤解を招き、それを理解しようと時間を費やしました。私は外部ライブラリを再コンパイルし、g ++を再インストールしました。

明らかに、idunsigned shortに修正すると問題が解決しました。もう1つの問題はコンパイラの問題のようです。しかし、私はまだ理解していません。実行中にコードのセクションが完全にスキップされ、最適化されていないのはなぜですか?なぜそれが各ifの声明を通らないのでしょうか?それで私は本当の問題を特定することができましたか?何か案は?

ありがとうございます。私はこれが最初の質問のために大丈夫だと思います。

+1

C++でマクロを使用して、あなたが求めるものを入手してください。 – Puppy

+1

コンパイラは警告しませんでしたか?余分な警告がなくても、g ++ 4.5はケースラベルがそのタイプの最大値を超えていると不満を持ちます。他のコンパイラでは、いくつかのフラグを付けて余分な警告を追加したいかもしれません。 *すべての警告を読んで、読んで、修正することを学ぶ*。 –

+0

コンパイラは私に警告しませんでした。そして、私はウォールオプションを設定しました。しかし、if文を変更してケースを切り替えると、警告が表示されます。私が使用しているコンパイラのバージョンはg ++ 4.4.3です。 – lospro7

答えて

1

shortは16ビット長で、その範囲は-32768〜32767です。したがって、決して40000または45000になることはありません。また、コンパイラはデッドコードを排除します(到達しないため)。

+0

私は最初の部分を自分で考え出しました。しかし、私は、基本的に、コンパイラは実行されるコードセクションが実行され、実行されないかどうかを判断します。コンパイラのいくつかのバージョンはそれを報告しているようですが、他のバージョンは報告していないようです。それが再び起こるなら、それを念頭に置いておきます。 – lospro7

3

すべての警告をgccから有効にすると、コンパイル時にこれが起こることが通知されます。

+0

+1コンパイラのレベルによっては、余分な警告なしで警告することさえあります(g ++ 4.5ではフラグなし、4.0/4.2では警告しません)。 –

+0

残念ながら、これはそうではありませんでした(g ++ 4.4.3)。 if文をswitch caseに変換すると警告が出ます。 – lospro7

1

私の結論はあなたと同じです:「最適化」をオンにしなくても最適化されたようです。たぶん、これらの定数述語「always true」/「always false」は、コード生成ステップのどこかで直接コードをスキップするために使用されます。つまり、-Oスイッチの最適化が実行されるよりも早くです。ちょうど推測。

1

GCCは優れた最適化コンパイラですが、-Werror、-Wallなどでエラーや警告情報が有効になっていても、GCCは診断コンパイルと同じレベルの情報を生成しません。コードを開発する際には、診断コンパイラのClangを使用して、バグやエラーを見つけるのを助けてください。 ClangはGCCとの互換性を意図していますが、いくつかの難解な機能を除いて、私はMakefile内の2つの間で自分のCCを変更することに問題はありませんでした。

コンパイラが最適化されているため、GCCはデフォルトでデッドコード除去を有効にすると信じています。これにより、あなたのid変数の範囲外のもののように、コンパイラが検出したすべてのブランチは不可能になります。このタイプのデッドコード除去を無効にすることができます。

1

コンパイル時に、C++コンパイラはコードを調べ、コードのどの部分がどのような順序で実行されるかを決定します。コンパイラがコードの一部が決して実行されないと判断した場合、コンパイラはコードを最適化しません。例えば

は:

ここ
int i = 0; 
if(i == 1) { 
    printf("This will never be printed\n"); 
} 

、それを実行することはありませんよう、if文を最適化する理由はありません。

インスタンスのこの種は、あなたがしてコンパイルした場合に拾われます:

g++ -Wall mycode.c

-Wall手段はすべての警告を表示し、mycode.cは、プロジェクトファイルです。

GDBを実行すると、プログラムの現在の流れが表示されます。 (if文の)分岐が偽である場合、なぜコードのその部分を通過するのでしょうか? if-elseif-elseステートメントでは1つのブランチしか使用できません。

私はあなたを助けてくれることを願っています。

関連する問題