デバッグprintfsをglibcで見ましたが、これは内部的に(void) 0
、[〜#〜] ndebug [〜#〜]が定義されている場合。同様に __noop
Visual C++コンパイラの場合もあります。前者はGCCコンパイラとVC++コンパイラの両方で動作し、後者はVC++でのみ動作します。これで、上記の両方のステートメントが操作なしとして扱われ、それぞれのコードが生成されないことがわかりました。しかし、ここに私が疑っているところがあります。
の場合には __noop
、MSDNは、コンパイラによって提供される組み込み関数だと言います。に来ます (void) 0
〜コンパイラーによってopとして解釈されないのはなぜですか?それはC言語のトリッキーな使い方ですか、それとも規格は明示的にそれについて何かを言っていますか?それとも、コンパイラの実装と関係がありますか?
(void)0
(+ ;
)は有効ですが、「does-nothing」のC++式です。それがすべてです。ターゲットアーキテクチャのno-op
命令には変換されません。言語が完全なステートメントを期待するときはいつでもプレースホルダーとしての空のステートメントです(たとえば、ジャンプラベルのターゲットとして、またはif
句の本文に) )。
編集:(クリス・ルッツのコメントに基づいて更新)
マクロとして使用する場合、たとえば
#define noop ((void)0)
(void)
は、次のような値として誤って使用されるのを防ぎます
int x = noop;
上記の式では、コンパイラは無効な操作として正しくフラグを立てます。 GCCはerror: void value not ignored as it ought to be
およびVC++の樹皮'void' illegal with all types
を吐き出します。
副作用を持たない式は、コンパイラーによってノーオペレーションとして扱われます。コンパイラーは、コードを生成する必要はありません(可能性はありますが)。そのため、コンパイラー(および人間)がキャストを行い、キャストの結果を使用しないことは、副作用がないと簡単に判断できます。
glib ではなく glibc について話していると思います。問題のマクロはassert
マクロです:
Glibcの_<assert.h>
_で、NDEBUG
(デバッグなし)が定義されている場合、assert
は次のように定義されます。
_#ifdef NDEBUG
#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif
# define assert(expr) (__ASSERT_VOID_CAST (0))
#else
/* more code */
#endif
_
これは基本的にassert(whatever);
が_((void)(0));
_と同等であり、何もしないことを意味します。
C89標準(セクション4.2)から:
ヘッダー_
<assert.h>
_はassert
マクロを定義し、別のマクロを参照します。_NDEBUG
__
<assert.h>
_で定義されていません。NDEBUG
がソースファイル内の_<assert.h>
_が含まれるポイントでマクロ名として定義されている場合、assert
マクロは単に_#define assert(ignore) ((void)0)
_
デバッグ出力マクロを_(void)0
_と等しくなるように定義することはあまり意味がないと思います。それがどこで行われているのか教えていただけますか?
そうだとしても、なぜ型をキャストして無効にしますか?また、#define dbgprintf(void)0の場合、dbgprintf( "Hello World!");のように呼び出されたとき->(void)0( "Hello World!"); - どういう意味ですか? – legends2k
マクロはコードを別のものに置き換えます。したがって、#xを受け入れるdbgprintを#define
無効(0)
置換ではXの書き換えが行われないため、dbgprintf( "Helloworld")は(void)0( "Hello world")に変換されず、(void)0に変換されます。 -マクロ名dbgprintが(void)0に置き換えられるだけでなく、呼び出し全体がdbgprintf( "...")になります
Windowsでは、Main.cppで次のようなコードを試します。
#include <iostream>
#define TRACE ((void)0)
int main() {
TRACE("joke");
std::cout << "ok" << std::endl;
return 0;
}
次に、Main.i出力でリリースバージョンのexeファイルをビルドします。 Main.iファイルでは、TRACEマクロが次のように置き換えられました:((void)0)("joke")
、およびVisual Studioは警告を表示します。「警告C4353:非標準の拡張機能が使用されました:関数式として定数0。代わりに '__noop'関数組み込みを使用してください」。 exeファイルを実行すると、コンソールは「ok」文字を出力します。したがって、すべてが明確だと思います:マクロTRACE [#define TRACE((void)0)]の定義はc ++構文に従って違法ですが、Visual Studioのc ++コンパイラーはこの動作をコンパイラー拡張機能としてサポートします。したがって、私の結論は次のとおりです。[#define TRACE((void)0)]は違法なc ++ステートメントであり、せいぜいこれを使用しないでください。ただし、[#define TRACE(x)((void)0)]は有効なステートメントです。それで全部です。