C中的括号运算符。以下代码中的效果如何

时间:2010-04-13 14:11:09

标签: c parentheses

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

int main()
{

  ("Hello world");

}

此代码有效且我获得了预期的效果(禁用宏时没有任何反应)但我无法弄清楚究竟发生了什么。编译器是否将括号视为“无名”方法声明?

为了更清楚,代码是:

 #ifdef TRACE

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

    #define trace
 #endif

int main()
{

  trace("Hello world");

}

提前致谢。

5 个答案:

答案 0 :(得分:18)

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

"Hello world";

,其值为char *类型,并忽略该值。您可以将该表达式包含在()

的冗余对中
("Hello world");

不会改变任何东西。

以完全相同的方式编写

(5 + 3);

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

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

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

"Hello world";

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

(void) "Hello world";

因此,您可以考虑相应地重新定义宏。

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

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

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

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

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

i++;

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

trace(get_trace_name());

“已禁用”表单将显示为

(get_trace_name());

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

答案 1 :(得分: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参数必须是一个文字字符串常量,以便相邻的字符串文字串联发生 - 任何类型的变量都会产生错误,但它是{{3}在任何情况下都使用变量作为格式说明符。

答案 2 :(得分:2)

("Hello world");

是一个返回指向字符串的常量指针的表达式。不消耗此值。

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

"Hello world";

答案 3 :(得分:0)

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

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

宏在C中的工作方式是编译器(基本上)*执行 literal 替换标识符。

因此,在您的情况下,根据#IFDEF

的值,有两个选项

trace("Hello world");

可以成为

  1. printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");
    1. ("Hello world");
    2. 第一个选项是一系列有效的C代码,它由两个printf语句组成。第二个选项是一系列有效的C代码,它由不必要的大括号内的字符串(char *)组成。

答案 4 :(得分:0)

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

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

这可以避免在参数中出现副作用的问题。如果你有一个严格的C89编译器,你必须避免副作用......