C语言在宏定义中使用语句表达式和预处理器运算符
语句表达式的亮点在于定义复杂功能的宏。使用语句表达式来定义宏,不仅可以实现复杂的功能,而且还能避免宏定义带来的歧义和漏洞。下面以一个简单的最小值的宏为例子一步步说明。
1、灰常简单的么,使用条件运算符就能完成,不就是
2、通常为了避免这种展开错误,我们可以给宏的参数加一个小括号()来防止展开后,表达式的运算顺序发生变化。这样的宏才能算一个比较完善的宏:
在程序中,我们打印表达式 3 + MIN(1, 2) 的值,预期结果应该是5,但实际运行结果却是2。我们展开后,发现同样有问题:
释然了。
对于4 + (1!=2) > (2) ? (2) : (1!=2);
同样的道理,出现5>2?2:1,结果可想而知。
3、此时我们应该继续修改这个宏:
那么现在的这个写法已经解决上面的两种写法带来的意外,在进行一下简单的测试。
实际运行结果发现 min = 3,而不是预期结果 min = 2。这是因为
4、基于上述的问题,可以使用语句表达式来定义这个宏。
注意:宏延续运算符(\)的概念
一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用宏延续运算符(\)。
5、在上面这个宏定义的两个临时变量数据类型是 int 型。那对于其它类型的数据,就需要重新再定义一个宏,但是可以基于上面的宏继续修改,让它可以支持任意类型的数据比较大小:
在这个宏中,我们添加一个参数:type,用来指定临时变量 _x 和 _y 的类型。这样,我们在比较两个数的大小时,只要将2个数据的类型作为参数传给宏,就可以比较任意类型的数据了。
6、在内核中,尤其是在内核的宏定义中,被大量的使用。使用语句表达式定义宏,不仅可以实现复杂的功能,还可以避免宏定义带来的一些歧义和漏洞。比如在 Linux 内核中,max_t 和 min_t 的宏定义,就使用了语句表达式:
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。在宏中使用的该运算符有一个特定的参数或参数列表。例如:
8、宏定义中标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如: