【问题标题】:What does the c precompiler do with macros defined as (void)0c 预编译器对定义为 (void)0 的宏做了什么
【发布时间】:2014-07-29 17:07:03
【问题描述】:

我有一些基于编译器标志定义的宏。我正在尝试决定是否希望将宏定义为 (void)0 或将其未定义并导致编译时错误。

#ifdef DEBUG
  #define PRINTF(...) printf(__VA_ARGS__)
#else
  #define PRINTF(...) (void)0
#endif

int main(void) {
  ...
  PRINTF("something");
  ...
}

对比

#ifdef DEBUG
  #define PRINTF(...) printf(__VA_ARGS__)
#endif

int main(void) {
  ...
  #ifdef DEBUG
    PRINTF("something");
  #endif
  ...
}

我不确定我更喜欢哪种技术。一方面,用#ifdef 包装每个 PRINTF 语句会很丑陋。另一方面,如果我调用了一个在上下文中不能真正工作的函数,那么在编译时就知道会很好。

我认为决定因素将是 (void)0 宏是否会影响可执行文件的大小。

编译代码时,(void)0 会发生什么?如果 PRINTF 被定义为 (void)0,这是否意味着可执行文件将包含某种 (void)0 指令,还是会被完全忽略?

【问题讨论】:

  • (void)0 可能不会生成任何代码。
  • 如果您有疑问,为什么不直接忽略它,我的意思是,定义?您真的可以将#else 定义单独写为#define PRINTF(...),而您的行(希望我在这里)只是;s,即空语句。
  • #define PRINTF(...) 有什么问题(您将宏替换定义为空字符串)
  • 为什么你要问一个只有特定编译器才能回答的问题,而在任何时候都不说你的编译器是什么?
  • @PascalCuoq 我认为 (void)0 的处理将是跨 c 编译器的标准。我正在使用 Code Composer Studio IDE,这让我对我正在使用的编译器有些不知所措。

标签: c macros compiler-construction


【解决方案1】:
(void) 0;

是一个没有副作用的表达式语句。任何理智的实现都会优化这个语句(实现还能用这样的语句做什么?)。

如果定义了NDEBUG,则将(void) 0 作为宏定义得到C 标准的认可,因为它出现在(C11) 7.2p1 中的assert 宏定义中:

#define assert(ignore) ((void)0)

注意定义:

#define PRINTF(...) (void)0

而不是

#define PRINTF(...)

有一个优势。在第一种情况下,您有一个表达式(例如一个不返回值的函数),因此它可用于例如逗号表达式或条件表达式中。

例如:

// Comma expression
printf("test"), PRINTF("Hi Dennis");

// Conditional expression
test-expr ? perror("Hello") : PRINTF("world");

这两个表达式语句只对之前的PRINTF 定义有效(带有(void) 0)。

【讨论】:

    【解决方案2】:

    它将被完全忽略,您可以通过查看程序集输出来确认这一点(gcc -S 将生成 file.s,asm 输出),比较有无 (void)0 行,看看它是完全一样。

    【讨论】:

    • 是预处理器还是编译器完全忽略了它?
    • 也不是,我想我应该使用不同的措辞:预处理器留在(void)0 并且编译器看到它(例如,尝试将它分配给一个变量,gcc 会抱怨你没有像你应该的那样忽略 void 值)但它实际上并没有生成任何代码,因为它是一个无所事事的表达式。所以它在代码生成方面被忽略了,预处理器和编译器前端都看到了。
    【解决方案3】:

    一个不错的编译器将优化死(无法访问)代码,因此您可以:

      #ifdef DEBUG
      #define PRINTF(...) if (1) { printf(__VA_ARGS__) ; }
      #else
      #define PRINTF(...) if (0) { printf(__VA_ARGS__) ; }
      #endif
    

    它的一大优势是允许编译器检查调试代码,无论您是否打开/未打开DEBUG - 这降低了在您的背部留下痛苦牙印的风险。

    【讨论】:

    • 这是一个很大的优势,但请注意,它也会改变语义,PRINTF 不再是表达式的替代品,而是一个语句(因此不能再用于可以使用表达式的地方) )。
    • @ouah 在这种情况下,PRINTF 无论如何都不能在表达式中使用,因为它可能具有 void 类型。
    • 为什么if (0) 而不是什么都没有?编译器可能会删除它,但仍然...
    • @DrewMcGowen 查看我在 OP 问题中的回答,void 表达式可以用作逗号表达式或条件表达式的操作数。
    • OK...if 可以替换为 (1 ? printf(..) : (void)0) 吗?
    猜你喜欢
    • 2020-05-20
    • 2010-12-28
    • 1970-01-01
    • 1970-01-01
    • 2016-07-19
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2011-07-30
    相关资源
    最近更新 更多