2

私の質問は、C でアサートを使用する必要がある場合に部分的に関連している可能性があります。しかし、いくつかの場所に散らばっているアサーションの束を含むプロジェクトがある場合、どちらがより良いプラクティスになるのかまだ疑問に思っています:

DNDEBUG フラグを使用してアサートをノーオペレーションに変えるか (関連する質問で提案されているように)、またはすべてのアサートを次のような if マクロで囲みますか?

#ifdef TEST
#include <assert.h>
#endif

....

#ifdef TEST
    assert(...);
#endif

....

コンパイル時に -D TEST オプションを使用しますか? これには、ある種の標準または「慣習」がありますか? 後者の方がすっきりするような気がします..ありがとう!

4

2 に答える 2

3

これにはある種の標準または「慣習」がありますか?

はい:NDEBUG本番コードで定義します。これはISOCの標準的な方法であり、コンパイルサイクルに数ミリ秒を追加するだけです(またはNDEBUG、とにかくそれを含めているので、定義しない場合と比較して一部を節約します)。これは、コンパイラ/プリプロセッサがスキャンし<assert.h>て、コードから物事を削除します。#ifndef NDEBUGブロック内でクレイジーなことをしない限り、実行可能ファイルを肥大化させたり、他の種類のランタイムオーバーヘッドを追加したりすることはありません。

于 2012-11-16T22:44:59.053 に答える
0

これが私がこれを行う方法です。コードの下の議論。

一部の .h ファイルでは:

#define PASS ((void)0)

extern void hook(void);
extern void asserthook(void);

#ifdef DEBUG
#define ENABLE_ASSERTS
#endif // DEBUG

#ifdef ENABLE_ASSERTS
#define AssertMesg(expr, mesg) \
    do { \
        if (!(expr)) \
        { \
            asserthook(); \
            fprintf(streamErr, \
                    "%s(%d): assertion failed: ", __FILE__, __LINE__); \
            fprintf(streamErr, "%s\n", mesg); \
            fflush(streamErr); \
            Exit(10); \
        } \
    } while (0)
#define Assert(expr)  AssertMesg(expr, "")

#else // !ENABLE_ASSERTS

#define AssertMesg(expr, mesg)  PASS
#define Assert(expr)  PASS

#endif // !ENABLE_ASSERTS

そして、いくつかの .c ファイルで:

void hook(void)
{
    PASS;
}

void asserthook(void)
{
    hook();
}

まず第一に、私のアサートは常にasserthook()which calls を呼び出しますhook()。これらの関数は、ブレークポイントを設定する場所にすぎません。errhook()エラーのために呼び出されることもあります。通常、hook()自分自身にブレークポイントを設定するだけで、アサートによってコードがダウンするたびに、必要な場所でスタック バックトレースを使用して、デバッガーをエラーで停止させます。

C ステートメントのように動作する複数行のマクロを作成しようとする場合、通常はマクロを中かっこで囲み、それらの中かっこをdo/で囲みますwhile (0)。これは 1 回実行するループなので、実際にはループではありません。しかし、そのようにラップするということは、それがステートメントであることを意味し、行にセミコロンを置いてステートメントを終了させると、実際には正しいことになります。したがって、このようなコードはエラーなしでコンパイルされ、正しいことを行います。

if (error)
    AssertMesg(0, "we have an error here");
else
    printf("We don't have an error after all.\n");

doばかげた/ラッパーを実行せずwhile (0)、中括弧だけを使用した場合、上記のコードは機能しません! まず、コンパイラは、なぜ;中括弧の後とelse;の前に右があるのか​​疑問に思うでしょう。2 番目に、はマクロ内elseの非表示の に関連付けられ、は決して呼び出されません。明示的な中括弧を使用して後者の問題を修正できますが、すべての状況で機能するようにマクロを設定する方が明らかに優れており、それが/の役割です。ifAssertMesg()printf()dowhile (0)

(void)0私の好みの何もしないステートメントです。必要に応じて使用do {} while (0)することも、副作用がなく、変数を使用しないものを使用することもできます。

do/トリックの最悪の点while (0)は、ループについて不平を言うエラー メッセージが時々表示されることですdo。これはマクロ内に隠されているため、実際に何が起こっているのかを覚えておく必要があります。しかし、複数行のマクロを正しく動作させるには、これが私が知っている最善の方法です。

可能であれば、マクロではなく静的インライン関数を使用する必要があります。しかし、マクロは下手な C コンパイラにも完全に移植可能であり、適切__FILE__に展開できるようにアサートにマクロを使用する必要があります。__LINE__

(実際のアサートを行う varargs 関数を作成してから、使用するすべてのコンパイラで varargs が正しく機能する限り、その関数への単一の呼び出しに展開されるマクロを作成することができます。私は今それを行うことができると思います,しかし、私はすでに複数行のマクロソリューションを機能させており、長い間それに触れていません.)

于 2012-11-16T23:14:01.417 に答える