为什么 (void) 0 是 C 和 C++ 中的无操作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2198950/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is (void) 0 a no operation in C and C++?
提问by legends2k
I have seen debug printfs in glibcwhich internally is defined as (void) 0
, if NDEBUGis defined. Likewise the __noop
for Visual C++ compiler is there too. The former works on both GCC and VC++ compilers, while the latter only on VC++. Now we all know that both the above statements will be treated as no operation and no respective code will be generated; but here's where I've a doubt.
我在glibc 中看到调试 printfs (void) 0
,如果定义了NDEBUG,它在内部被定义为。同样__noop
,Visual C++ 编译器也在那里。前者适用于 GCC 和 VC++ 编译器,而后者仅适用于 VC++。现在我们都知道,上面的两个语句都将被视为没有操作,也不会生成相应的代码;但这是我怀疑的地方。
In case of __noop
, MSDN says that it's a intrinsic function provided by the compiler. Coming to (void) 0
~ Why is it interpreted by the compilers as no op? Is it a tricky usage of the C language or does the standard say something about it explicity? Or even that is something to do with the compiler implementation?
对于__noop
,MSDN 说它是编译器提供的内在函数。来到(void) 0
~为什么它被编译器解释为没有操作?这是 C 语言的一个棘手用法还是标准是否明确说明了它?或者甚至这与编译器实现有关?
回答by Alexander Gessler
(void)0
(+;
) is a valid, but 'does-nothing' C++ expression, that's everything. It doesn't translate to the no-op
instruction of the target architecture, it's just an empty statement as placeholder whenever the language expects a complete statement (for example as target for a jump label, or in the body of an if
clause).
(void)0
(+ ;
) 是一个有效但“无所作为”的 C++ 表达式,仅此而已。它不会转换为no-op
目标体系结构的指令,它只是一个空语句作为占位符,只要语言需要完整的语句(例如作为跳转标签的目标,或在if
子句的主体中)。
EDIT:(updated based on Chris Lutz's comment)
编辑:(根据 Chris Lutz 的评论更新)
It should be noted that when used as a macro, say
需要注意的是,当用作宏时,说
#define noop ((void)0)
the (void)
prevents it from being accidentally used as a value like
所述(void)
防止它被意外地用作的值等
int x = noop;
For the above expression the compiler will rightly flag it as an invalid operation. GCC spits error: void value not ignored as it ought to be
and VC++ barks 'void' illegal with all types
.
对于上述表达式,编译器会正确地将其标记为无效操作。GCC 吐槽error: void value not ignored as it ought to be
和 VC++ 吠叫'void' illegal with all types
。
回答by legends2k
Any expression that doesn't have any side-effects can be treated as a no-op by the compiler, which dosn't have to generate any code for it (though it may). It so happens that casting and then not using the result of the cast is easy for the compiler (and humans) to see as not having side-effects.
任何没有任何副作用的表达式都可以被编译器视为无操作,编译器不必为其生成任何代码(尽管可能)。碰巧的是,转换然后不使用转换的结果很容易被编译器(和人类)视为没有副作用。
回答by Alok Singhal
I think you are talking about glibc, not glib, and the macro in question is the assert
macro:
我认为您在谈论glibc,而不是glib,并且有问题的assert
宏是宏:
In glibc's <assert.h>
, with NDEBUG
(no debugging) defined, assert
is defined as:
在 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
which basically means assert(whatever);
is equivalent to ((void)(0));
, and does nothing.
这基本上意味着assert(whatever);
相当于((void)(0));
,并且什么都不做。
From the C89 standard (section 4.2):
来自 C89 标准(第 4.2 节):
The header
<assert.h>
defines theassert
macro and refers to another macro,NDEBUG
which is not defined by
<assert.h>
. IfNDEBUG
is defined as a macro name at the point in the source file where<assert.h>
is included, theassert
macro is defined simply as#define assert(ignore) ((void)0)
标头
<assert.h>
定义了assert
宏并引用了另一个宏,NDEBUG
这不是由 定义的
<assert.h>
。如果NDEBUG
在包含源文件的点处定义为宏名称<assert.h>
,则assert
宏定义为#define assert(ignore) ((void)0)
I don't think defining a debug print macro to be equal to (void)0
makes much sense. Can you show us where that is done?
我认为将调试打印宏定义为等于没有(void)0
多大意义。你能告诉我们这是在哪里完成的吗?
回答by Darko Maksimovic
Even if so, why type cast it to void? Also, in case of the #define dbgprintf (void) 0, when it's called like dbgprintf("Hello World!"); -> (void) 0("Hello World!"); - what does it mean? – legends2k
即使是这样,为什么将其强制转换为 void?此外,在 #define dbgprintf (void) 0 的情况下,当它被调用时就像 dbgprintf("Hello World!"); -> (void) 0("Hello World!"); - 这是什么意思?– Legends2k
Macros replace your code with something else, so if you #defined dbgprint (that accepts x) as
宏用其他东西替换你的代码,所以如果你#defined dbgprint(接受x)作为
void (0)
无效 (0)
then no rewriting of X will occur in replacement, so dbgprintf("Helloworld") will not be converted to (void) 0("Hello world"), but to (void) 0; - not only macro name dbgprint is replaced by (void) 0, but the whole call dbgprintf("...")
那么替换时不会发生X的重写,所以dbgprintf("Helloworld")不会被转换成(void) 0("Hello world"),而是转换成(void) 0; - 不仅宏名 dbgprint 被 (void) 0 替换,而且整个调用 dbgprintf("...")
回答by baojing.zhou
On Windows, I try some code like this in Main.cpp:
在 Windows 上,我在 Main.cpp 中尝试了一些这样的代码:
#include <iostream>
#define TRACE ((void)0)
int main() {
TRACE("joke");
std::cout << "ok" << std::endl;
return 0;
}
Then, I build the Release version exe file with Main.i output. In Main.i file, the TRACE macro was replaced to:((void)0)("joke")
, and visual studio give an warning:"warning C4353: nonstandard extension used: constant 0 as function expression. Use '__noop' function intrinsic instead". Run the exe file, console print out "ok" characters. So I think all is clear: the definition of macro TRACE[#define TRACE ((void)0)] is illegal according to c++ syntax, but c++ compiler of visual studio supports this behavior as a compiler extension. So my conclusion is: [#define TRACE ((void)0)] is illegal c++ statement, and you at best DO NOT use this. But [#define TRACE(x) ((void)0)] is legal statement. That's all.
然后,我使用 Main.i 输出构建 Release 版本的 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)] 是合法的声明。就这样。