我在glibc中看到了调试printfs,如果定义了
NDEBUG,它的内部定义为
(void) 0
。同样,Visual C++ 编译器的 __noop
也在那里。前者适用于 GCC 和 VC++ 编译器,而后者仅适用于 VC++。现在我们都知道,上述两条语句都会被视为无操作,不会生成相应的代码;但这就是我有疑问的地方。
对于
__noop
,MSDN 说它是编译器提供的内部函数。来到(void) 0
〜为什么它被编译器解释为no op?这是 C 语言的一种棘手用法还是标准对此有明确说明?或者甚至这与编译器实现有关?
(void)0
(+;
) 是一个有效的,但“什么也不做”的 C++ 表达式,仅此而已。它不会转换为目标体系结构的 no-op
指令,只要语言需要完整的语句(例如作为跳转标签的目标,或在 if
子句的主体中),它只是一个作为占位符的空语句).
来自 Chris Lutz 的评论:
应该注意的是,当用作宏时(例如,
#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
。
任何没有任何副作用的表达式都可以被编译器视为无操作,编译器不必为其生成任何代码(尽管可能会生成)。碰巧,编译器(和人类)很容易将转换然后不使用转换结果视为没有副作用。
我认为你正在谈论glibc,而不是glib,并且所讨论的宏是
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
没有多大意义。 你能告诉我们这是在哪里完成的吗?
即使是这样,为什么 type 将其强制转换为 void?另外,如果是 #define dbgprintf (void) 0,当它被称为
时 - 这意味着什么?dbgprintf("Hello World!"); -> (void) 0("Hello World!");
宏用其他东西替换你的代码,所以如果你#define dbgprint(接受x)为
void (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”字符。
所以我想一切都清楚了:根据C++语法,宏TRACE[#define TRACE ((void)0)]的定义是非法的,但是Visual Studio的C++编译器作为编译器扩展支持这种行为。
所以我的结论是: [#define TRACE ((void)0)] 是非法的 c++ 语句,你最好不要使用它。但 [#define TRACE(x) ((void)0)] 是合法的语句。仅此而已。
这是最近 GCC 抱怨的警告
-Wall -Wextra
打开的情况,即使出现误报,您也应该这样做:
#define dbgprintf(x)
if (cond) dbgprintf(x)
会导致 #define dbgprintf(x) 0
#define dbgprintf(x) (void)0
void
会使 GCC 抑制警告。顺便说一句,同样的
-Wall -Wextra
不会抱怨这些。