问编写多语句宏的最佳方法是什么?
答通常的目标是能够像调用一个由函数调用组成的表达式语句一样调用该宏。
MACRO(arg1, arg2);这意味着“调用者”会提供最后的分号,所以宏体不应该提供。因此,宏体不能是一个简单的由花括号括起来的复合语句,因为宏可能被用作带有显式 else 子句的 if/else 语句的 if 分支。
if(cond) MACRO(arg1, arg2); else /* some other code */如果宏扩展为一个简单的复合语句,那么由调用者提供的最终分号将是一个语法错误。
if(cond) {stmt1; stmt2;}; else /* some other code */
因此,传统的解决方案是使用:
#define MACRO(arg1, arg2) do { \ /* declarations */ \ stmt1; \ stmt2; \ /* ... */ \ } while(0) /* (no trailing ; ) */当调用者附加分号时,无论上下文如何,此展开都成为单个语句。(优化编译器将删除常数条件 0 的任何“死”测试或分支,尽管lint可能会抱怨。)
(另一种可能性可能是
#define MACRO(arg1, arg2) if(1) { \ stmt1; \ stmt2; \ } else但它不太好,因为它会在调用者恰好忘记在调用时附加分号时悄悄破坏周围的代码。)
如果预期的宏中的所有语句都是简单的表达式,没有声明或循环,则另一种技术是使用一个或多个逗号运算符编写一个简单的、带括号的表达式。
#define FUNC(arg1, arg2) (expr1, expr2, expr3)(例如,请参阅第一个DEBUG()宏,在第 10.26 题中。)此技术还允许一个值(在这种情况下是expr3)被“返回”。
一些编译器,例如gcc,也能够内联展开紧凑函数,无论是自动的还是程序员要求的(可能带有非标准的“inline”关键字或其他扩展)。
参考文献:H&S 第 3.3.2 节,第 45 页
CT&P 第 6.3 节,第 82-3 页