为啥我的简单 C 宏不起作用?

Posted

技术标签:

【中文标题】为啥我的简单 C 宏不起作用?【英文标题】:Why doesn't my simple C macro work?为什么我的简单 C 宏不起作用? 【发布时间】:2016-04-02 21:23:34 【问题描述】:

我想做一个简单的宏,像这样调用 printf() 两次

#ifdef ENABLE_DEBUGPRINTF
#define DEBUGPRINTF(msg) printf("At sim_time = %f:", sim_time); printf(msg);
#else
#define DEBUGPRINTF(msg)  //evalutes to nothing
#endif

现在当我打电话时

DEBUGPRINTF("Processed event type: %d with value %f\n", id, data)

它正确地打印了第一部分“At sime_time = ...”,但后面的部分“Processed events ...”错误地打印了 id 和 data 的值。

同时

printf("Processed event type: %d with value %f\n", id, data);

正确打印值。

当我尝试通过准确写出我认为宏将评估为的内容来执行它时,我做到了。

printf("At sim_time = %f:", sim_time); printf("Processed event type: %d with value %f\n", id, data);

这将正确打印所有内容!那么为什么我的宏没有对此进行评估?

【问题讨论】:

@PaulGriffiths D'oh 当然。抱歉,我不习惯预处理器指令。出于某种原因,我认为括号内的所有内容都将包含在“msg”中。 不相关,但最好在多指令宏周围加上大括号:#define DEBUGPRINTF(msg, id, data) etc... 。否则,当宏在if 之后使用时,您可能会感到意外。 @user1320881:这仍然会导致错误的代码,例如在if 声明中。正确执行此操作的常用方法是包装到 do .. while ( 0 ) 虚拟循环中。每个现代编译器都会识别这种模式并优化循环。 @Olaf:在这种情况下,由于printf 调用是表达式,最好用逗号分隔它们并将整个内容括在括号中(并去掉分号)。 do ... while(0) 技巧让您可以在需要声明的任何地方使用宏,但如果扩展是表达式,则更加灵活。 【参考方案1】:

因为您想要并且正在使用常规 printf 的全部灵活性,所以您想要的是带有 variadic 参数的宏:

#ifdef ENABLE_DEBUGPRINTF
#define DEBUGPRINTF(msg...) \
    printf("At sim_time = %f:", sim_time); printf(msg);
#else
#define DEBUGPRINTF(msg...)  /*evalutes to nothing*/
#endif

我以前做过很多次,我建议用do while (0)封装:

#ifdef ENABLE_DEBUGPRINTF
#define DEBUGPRINTF(msg...) \
    do  \
        printf("At sim_time = %f:", sim_time); \
        printf(msg); \
     while (0)
#else
#define DEBUGPRINTF(msg...)  //evalutes to nothing
#endif

这使您可以执行以下操作:

if (showit)
    DEBUGPRINTF("hit the showit point -- showit=%d\n",showit);

因此,使用宏的代码不必知道它实际上是两个语句[或没有]


更新:

DEBUGPRINTF(msg...) 不符合标准,而是一些旧版编译器扩展。您在省略号之前漏掉了一个逗号。

也许,但就我个人而言,我仍然更喜欢它,并且已经在生产代码中使用它 10 多年了。

但是,对于那些可能希望使用替代方法的人来说,这里有一些资源:

    https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html https://en.wikipedia.org/wiki/Variadic_macro

【讨论】:

DEBUGPRINTF(msg...) 不符合标准,而是一些旧版编译器扩展。您在省略号之前漏掉了一个逗号。【参考方案2】:

您声明 DEBUGPRINTF 接受一个参数,但随后您传递了三个参数,所以它当然不会像您期望的那样工作。

msg 在你的第一个例子中只是"Processed event type: %d with value %f\n",你的第二个printf() 调用只是为%d%f 拉垃圾,因为你的宏从来没有告诉它任何关于iddata,所以他们永远不会被传递给printf()

你想要这样的东西:

#define DEBUGPRINTF(msg, id, data) printf("At sim_time = %f:", sim_time); printf(msg, id, data);

或者,如果您需要更灵活的东西,可以使用可变参数宏。

【讨论】:

如果用作像if这样的语句的主体,这仍然会引起问题。【参考方案3】:

使用双(嵌套)定义:

#define FIRST         printf("…")
#define DEBUGMSG(msg) FIRST;printf(msg)

这在定义中有一个参数,在实现中有一个参数。

【讨论】:

这只不过是 OP 已经拥有的。它根本无法解决 OP 的实际问题。如果您按照 OP 的要求尝试 DEBUGPRINTF("Processed event type: %d with value %f\n", id, data),那么您会发现您的解决方案将以与 OP 所经历的完全相同的方式失败。

以上是关于为啥我的简单 C 宏不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个在 Common Lisp 中漂亮地打印宏扩展的宏不起作用?都有哪些替代工具?

为 UIFont 定义宏不起作用

Word 2010 宏不起作用

保存excel文件后运行宏不起作用

电子生成器自定义 NSIS 包含脚本宏不起作用

如何在 VS 2010 中出现第一个错误时停止 C# 编译(VS 2008 宏不起作用)!