4

当我在禁用宏时使用以下代码时,我正在使用宏来启用/禁用跟踪:

int main()
{

  ("Hello world");

}

这段代码是有效的,我得到了想要的效果(禁用宏时没有任何反应),但我无法弄清楚到底发生了什么。编译器是否将括号视为“无名”方法声明?

为了更清楚,代码是:

 #ifdef TRACE

    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
 else

    #define trace
 #endif

int main()
{

  trace("Hello world");

}

提前致谢。

4

5 回答 5

18

如果缺少函数名称,如您的第一个示例所示,则它不是“括号运算符”。它只是改变运算符和操作数之间关联的表达式的句法元素。在这种情况下,它根本什么都不做。你所拥有的只是一种表达

"Hello world";

它评估为char *类型的值,并且该值被忽略。您可以将该表达式包含在一对冗余的()

("Hello world");

这不会改变任何事情。

以完全相同的方式编写

(5 + 3);

在您的代码中间并获得一个计算结果为 value 的表达式,该表达式8立即被丢弃。

通常编译器不会为没有副作用的表达式语句生成代码。事实上,在 C 语言中,每个表达式语句的结果都会被丢弃,因此唯一“有意义”的表达式语句是具有副作用的表达式语句。编译器通常非常擅长检测无效语句并丢弃它们(有时带有警告)。

警告可能很烦人,因此编写无效的表达式语句,例如

"Hello world";

可能不是一个好主意。通常,编译器将强制转换识别void为不生成此警告的请求

(void) "Hello world";

所以你可能会考虑相应地重新定义你的宏。

当然,使用上述trace技术,你必须记住,如果你把一些确实有副作用的东西作为你的宏的参数

trace("%d\n", i++);

然后在“禁用”形式下,它将如下所示

("%d\n", i++);

(两个子表达式,由逗号运算符链接成一个表达式)。在这种情况下,递增的副作用i仍然存在,它不会被禁用。整个就相当于plain

i++;

此外,如果您使用函数调用作为参数

trace(get_trace_name());

“禁用”表格将如下所示

(get_trace_name());

并且编译器可能不够聪明,无法意识到get_trace_name()应该丢弃对的调用。因此,在使用宏时要小心。避免带有副作用的参数,避免带有函数调用的参数,当然,除非您打算在禁用实际跟踪时保留副作用。

于 2010-04-13T14:14:27.337 回答
3

它是否有效可能取决于您作为参数传递给宏的确切内容(请参阅 AndreyT 提到的副作用问题)。在这种情况下,它是良性的。但是,以下内容可能更安全,因为在处理宏且未定义 TRACE 时,不会插入任何文本

#ifdef TRACE
    #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
    #define trace( ... )
#endif

假设您的编译器支持可变参数宏。如果这样做,以下可能是一个更好的定义:

#ifdef TRACE
    #define trace( fmt, ...) printf("%s %d -> " fmt, __FILE__, __LINE__, __VA_ARGS__ ) ;
#else
    #define trace( ... )
#endif

请注意,和之间没有逗号"%s %d -> "fmt故意和必需的。另请注意,该fmt参数必须是文字字符串常量,以便发生相邻的字符串文字连接 - 任何类型的变量都会产生错误,但在任何情况下使用变量作为格式说明符都是不好的做法。

于 2010-04-13T14:30:46.327 回答
2
("Hello world");

是一个表达式,返回一个指向字符串的常量指针。该值未被消耗。

括号没有特定的作用,您可以省略它们:

"Hello world";
于 2010-04-13T14:15:09.420 回答
0

如果您的编译器支持 C99(或者您使用的是之前具有此功能的 gcc),您可以使用可变参数宏:

#ifdef TRACE
#define trace(...) printf("%s %d -> ",__FILE__, __LINE__);printf(__VA_ARGS__)
#else
#define trace(...)
#endif

这避免了参数中的副作用可能带来的问题。如果你有一个严格的 C89 编译器,你必须避免副作用......

于 2010-04-13T14:45:06.877 回答
0
#ifdef TRACE
   #define trace printf("%s %d -> ",__FILE__, __LINE__);printf
#else
   #define trace
#endif

int main {
  trace("Hello world");
}

宏在 C 中的工作方式是编译器将(基本上)* 对标识符进行字面替换。

因此,在您的情况下,有两个选项取决于#IFDEF

trace("Hello world");

可以变成

  1. printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");

或者

  1. ("Hello world");

第一个选项是由两个printf语句组成的有效 C 代码序列。第二个选项是一系列有效的 C 代码,它由不必要的大括号内的字符串 (char *) 组成。

于 2010-04-13T14:21:47.750 回答