3

次のコードが、任意の種類のgcc / g ++最適化を有効にしてコンパイルすると、エラーまたは警告をスローするかどうかを知りたいです。

int a;
a = func();
if (a == 2) {
    assert(false);
}

次のコードは、リリース構成で「設定されているが未使用の変数」という警告をスローする可能性があると思います。

int a;
a = func();
assert(a != 2);

しかし、上記のコードはどうですか?(gcc、ifステートメントまたはifブロック(リリースビルド内)のいずれでも何も実行されないため、ifステートメント自体を削除し、「未使用ですが変数を設定します」という警告をスローできます)

編集:これは間違いなくコードやexeのサイズを減らすことに関する質問ではありません。どのビルド構成でも成功するコードを知りたいです。

編集:リリースモードでassertを無効にします

4

6 に答える 6

4

私のテストによると、次のコードは次のような警告を生成します-Wall -Wextra -O2 -DNDEBUG

int a = func(); // warning: unused variable ‘a’
assert(a != 2);

ただし、次のコードはそうではありません。

// no warnings
int a;
a = func();
assert(a != 2);

ただし、にキャストすることで、未使用の変数の警告をいつでも抑制できますvoid

int a = func();
(void) a; // suppresses "unused variable" warning
assert(a != 2);

私の知る限り、linea = func()ステートメントは常に変数の使用としてカウントされますがa、初期化は使用としてカウントされませ

コンパイラが変更され、診断が改善されたときに、将来起こりうるコンパイラの警告をヘッジしようとはしません。ヘッジによって、誤って有効な警告が抑制されることがあるからです。

アサーションはどのように定義されますか?

標準化委員会とC実装者は、assert誤った警告を生成しないように注意深く設計されています。一般的なキャストに注意してくださいvoid...

  • がないNDEBUG場合、glibcはassert大まかに次のように定義します(以外のものを除くabort)。

    #define assert(expr) ((expr) ? (void) 0 : abort())
    
  • を使用NDEBUGすると、glibcはそれを次のように定義します(C標準で義務付けられているように)。

    #define assert(expr) ((void) 0)
    
  • 次のの定義はassert、式に展開されないため、準拠していません。

    #define assert(expr) { if (expr) { ... } } // wrong
    

C++でも定義が少し異なります。ご覧のとおり、assertは正しい方法で定義されているため、偽のコンパイラ警告は作成されず、実際には関数呼び出しのように構文的に動作します。

于 2012-06-22T08:13:09.497 に答える
1

assertあなたがプリプロセッサによって削除された場合、次のように問題が発生する可能性があります。

#ifdef ENABLE_ASSERT
#define assert (CONDITION) {if (!(CONDITION)) abort ();}
#else
#define assert (CONDITION) /* Nothing */
#endif

ただし、適切に実行すれば問題はありません。

#define assert (CONDITION) {if ((ENABLE_ASSERT) && !(CONDITION)) abort ();}

この場合、コンパイラはaが で使用されていることを引き続き認識しますが、がゼロCONDITIONの場合は最適化して削除します。ENABLE_ASSERT通常は、プリプロセッサを使用するよりもコンパイラ オプティマイザにコードを削除させる方が適切です。これにより、このような警告が回避され、通常はより読みやすいコードにつながります。また、いつか実行時テストになった場合でも、コードを書き直す必要はありません。 .

明らかに、ENABLE_ASSERT常にゼロまたは非ゼロに定義する必要があります。

于 2012-06-22T08:24:07.593 に答える
1

将来的に新しい警告が追加される可能性があるか、コンパイラのバグが誤った警告を引き起こす可能性があるため、一般に、コードの一部がコンパイラから警告を受け取ることはないとは言えません。

これifは、このような有効なコードで簡単に発生する可能性があるためです(これは本質的にあなたのケースと非常に似ています):

int a = func();
if (a == 2)
{
#ifdef SOME_BUILD_SETTING
    launch_missiles();
#endif
}

マニュアルは示しています

-Wempty-body ,またはステートメント
  で空の本体が発生した場合に警告します。この警告は-Wextraによっても有効になります。ifelsedo while

どちらが適用されますか:

#ifdef SOME_BUILD_OPTION
# define LAUNCH_MISSILES launch_missiles()
#else
# define LAUNCH_MISSILES
#endif
if (a==2)
  LAUNCH_MISSILES;

次に、定義-Wextraせずにコンパイルします。SOME_BUILD_OPTION

しかし、ブレースでは警告しません。ディートリッヒ エップがコメントしているように、定義されassertていても何も展開されないため、警告しません。NDEBUG

あなたのコードaでは初期化され、その値が使用されているので、警告するのは驚きです。

于 2012-06-22T08:03:33.080 に答える
0

ご存知かもしれませんが、assertマクロはマクロで管理されていNDEBUGます。#ifdef NDEBUGを使用したものは読みやすく、同じ効果があると思います。

于 2012-06-22T08:13:16.980 に答える
0

どちらのコード ブロックも問題ありませんが、私は次のことを好みます。

int a = func();
assert(a != 2);
于 2012-06-22T07:41:21.670 に答える
0

はい、このコードは、将来のある時点で奇妙なコンパイラ設定で警告を発する可能性があります。

あなたの質問は決して肯定的に答えられることはありません。それはできません。未来は予測不可能です。現在でも、コンパイラ フラグの数は、完全に分析するのが非常に困難な組み合わせの爆発を表しています。それに「はい」と答えた人は、何かを見落としている可能性があります。

ここで、一般的に (私の経験では) コンパイラは、オプティマイザが終了した後のコードではなく、コードが実際にどのように見えるかについてのみ警告を発行すると言います。はい、オプティマイザーを実行すると、より深い分析を行い、より微妙な問題を見つけることができる場合があります。しかし、オプティマイザーがそれを完全に削除できたので、不要になった構造についてフラグを立て始めることはありません。

それで、あなたはここでほとんど安全だと思います。これは、あなたが私から得ようとしているのと同じくらい「はい」に近いものです。

于 2012-06-22T08:14:19.443 に答える