web-dev-qa-db-ja.com

なぜ(void)0はCおよびC ++で操作なしですか?

デバッグprintfsをglibcで見ましたが、これは内部的に(void) 0[〜#〜] ndebug [〜#〜]が定義されている場合。同様に __noop Visual C++コンパイラの場合もあります。前者はGCCコンパイラとVC++コンパイラの両方で動作し、後者はVC++でのみ動作します。これで、上記の両方のステートメントが操作なしとして扱われ、それぞれのコードが生成されないことがわかりました。しかし、ここに私が疑っているところがあります。

の場合には __noop、MSDNは、コンパイラによって提供される組み込み関数だと言います。に来ます (void) 0〜コンパイラーによってopとして解釈されないのはなぜですか?それはC言語のトリッキーな使い方ですか、それとも規格は明示的にそれについて何かを言っていますか?それとも、コンパイラの実装と関係がありますか?

47
legends2k

(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を吐き出します。

63

副作用を持たない式は、コンパイラーによってノーオペレーションとして扱われます。コンパイラーは、コードを生成する必要はありません(可能性はありますが)。そのため、コンパイラー(および人間)がキャストを行い、キャストの結果を使用しないことは、副作用がないと簡単に判断できます。

8
anon

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_と等しくなるように定義することはあまり意味がないと思います。それがどこで行われているのか教えていただけますか?

2
Alok Singhal

そうだとしても、なぜ型をキャストして無効にしますか?また、#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( "...")になります

1

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)]は有効なステートメントです。それで全部です。

0
baojing.zhou