当我在禁用宏时出现以下代码时,我正在使用宏来启用/禁用跟踪:
int main()
{
("Hello world");
}
此代码有效且我获得了预期的效果(禁用宏时没有任何反应)但我无法弄清楚究竟发生了什么。编译器是否将括号视为“无名”方法声明?
为了更清楚,代码是:
#ifdef TRACE
#define trace printf("%s %d -> ",__FILE__, __LINE__);printf
else
#define trace
#endif
int main()
{
trace("Hello world");
}
提前致谢。
答案 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");
可以成为
printf("%s %d -> ",__FILE__, __LINE__);printf("Hello world");
或
("Hello world");
第一个选项是一系列有效的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编译器,你必须避免副作用......