55

我在glibc(void) 0中看到了调试 printfs ,如果定义了NDEBUG ,则内部定义为。同样__noop,Visual C++ 编译器也在那里。前者适用于 GCC 和 VC++ 编译器,而后者仅适用于 VC++。现在我们都知道,以上两条语句都将被视为无操作,不会生成相应的代码;但这是我有疑问的地方。

如果是__noop,MSDN 说它是编译器提供的内在函数。来到(void) 0~为什么编译器将其解释为无操作?它是 C 语言的一个棘手用法,还是标准明确说明了它?甚至这与编译器实现有关?

4

5 回答 5

75

(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

于 2010-02-04T10:43:41.660 回答
10

任何没有任何副作用的表达式都可以被编译器视为无操作,它不必为它生成任何代码(尽管它可能)。碰巧的是,转换然后不使用转换的结果很容易让编译器(和人类)认为没有副作用。

于 2010-02-04T10:44:42.940 回答
3

我认为您在谈论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多大意义。你能告诉我们这是在哪里完成的吗?

于 2010-02-04T10:53:09.067 回答
1

即使是这样,为什么要将其强制转换为 void?此外,如果#define dbgprintf (void) 0,当它被称为 dbgprintf("Hello World!"); -> (void) 0("Hello World!"); - 这是什么意思?– 传奇2k

宏用其他东西替换你的代码,所以如果你#defined dbgprint(接受x)为

无效 (0)

那么替换时不会发生 X 的重写,因此 dbgprintf("Helloworld") 不会转换为 (void) 0("Hello world"),而是转换为 (void) 0; - 不仅宏名 dbgprint 被 (void) 0 替换,而且整个调用 dbgprintf("...")

于 2011-02-12T19:06:28.227 回答
0

在 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)] 是法律声明。就这样。

于 2016-06-21T09:36:58.170 回答