不幸的是,你不能假设别的程序员总是能够避免这种你认为“相当白痴”的事情。例如,最近有人报告我,他们遇到了一个包含goto的宏。我也见过这种情况,而且听到过一些——在很脆弱的时候——看起来确实有理的意见。例如:
#define prefix get_ready(); int ret__
#define Return(i) ret__=i; do_something(); goto exit
#define suffix exit: cleanup(); return ret__
void f()
{
prefix;
// ...
Return(10);
// ...
Return(x++);
//...
suffix;
}
作为一个维护的程序员,就会产生这种印象;将宏“隐藏”到一个头文件中——这并不罕见——使得这种“魔法”更难以被辨别。
一个常见的微妙问题是,一个函数风格的宏并不遵守函数参数传递的规则。例如:
#define square(x) (x*x)
void f(double d, int i)
{
square(d); // 好
square(i++); // 糟糕:这表示 (i++*i++)
square(d+1); //糟糕:这表示(d+1*d+1); 也就是 (d+d+1)
// ...
}
“d+1”的问题,可以通过在“调用”时或宏定义时添加一对圆括号来解决:
#define square(x) ((x)*(x)) /*这样更好 */
但是, i++被执行了两次(可能并不是有意要这么做)的问题仍然存在。
是的,我确实知道有些特殊的宏并不会导致C/C++预处理宏这样的问题。但是,我无心去发展C++中的宏。作为替代,我推荐使用C++语言中合适的工具,例如内联函数,模板,构造函数(用来初始化),析构函数(用来清除),异常(用来退出上下文环境),等等。
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/