改善C++ 程序的150个建议学习之建议4:小心宏#define使用中的陷阱

来源:互联网 发布:国家电网数据运维招人 编辑:程序博客网 时间:2024/04/27 21:11
建议4:小心宏#define使用中的陷阱
C语言宏因为缺少必要的类型检查,通常被C++程序员认为是“万恶之首”,但就像硬币的两面一样,任何事物都是利与弊的矛盾混合体,宏也不例外。宏的强大作用在于在编译期自动地为我们产生代码。如果说模板可以通过类型替换来为我们产生类型层面上多样的代码,那么宏就可以通过符号替换在符号层面上产生的多样代码。正确合理地使用宏,可以有效地提高代码的可读性,减少代码的维护成本。不过,宏的使用中存在着诸多的陷阱,如果不注意,宏就有可能真的变成C++代码的“万恶之首”。
(1)用宏定义表达式时,要使用完备的括号。
由于宏只是简单的字符替换,宏的参数如果是复合结构,那么替换之后要是不用括号保护各个宏参数,可能会由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,而产生意想不到的情形。但并不是使用了括号就一定能避免出错,我们需要完备的括号去完备地保护宏参数。如下代码片段所定义的宏要实现参数a和参数b的求和,但是这三种定义都存在一定风险:
#define ADD( a, b ) a + b
#define ADD( a, b ) (a + b)
#define ADD( a, b ) (a) + (b)
例如,ADD(a,b) * ADD(c,d)的本意是对(a+b)*(c+d)求值,在采用了上面定义的宏之后,代码展开却变成了如下形式,其中只有第2种方式“碰巧”实现了原本意图:
a + b * c + d
(a + b) * (c + d)
(a) + (b) * (c) + (d)
之所以说“碰巧”,是因为第2种方式中括号的使用也非完备的。例如:
#define MULTIPLE( a, b ) (a * b)
在计算(a+b)×c时,如果采用上述宏MULTIPLE(a+b,c),代码展开后,我们得到的却是a+b×c的结果。要避免这些问题,要做的就是:用完备的括号完备地保护各个宏参数。正确的定义应为:
#define ADD( a, b ) ((a)+(b))
#define MULTIPLE( a, b ) ((a)*(b))
(2)使用宏时,不允许参数发生变化。宏参数始终是一个比较敏感、容易引发错误的东西。有很多人认为,在某种程度上带参的宏定义与函数有几分类似。但是必须注意它们的区别,正如下面代码片段所示:
#define SQUARE( a ) ((a) * (a))
int Square(int a)
{
return a*a;
}
int nValue1 = 10, nValue2 = 10;
int nSquare1 = SQUARE(nValue1++); // nSquare1=110, nValue1=12
int nSquare2 = Square(nValue2++);// nSquare2=100, nValue2=11
类似的定义,却产生了不同的结果,究其原因还是宏的字符替换问题。正如上面的示例一样,两处的a都被参数nValue1++替换了,所以nValue1自增操作也就被执行了两回。这就是宏在展开时对其参数的多次取值替换所带来的副作用。为了避免出现这样的副作用,最简单有效的方法就是保证宏参数不发生变化,如下所示。
#define SQUARE( a ) ((a) * (a))
int nValue1 = 10;
int nSquare1 = SQUARE(nValue1); // nSquare1=100
nValue1++; // nValue1=11
(3)用大括号将宏所定义的多条表达式括起来。如果宏定义包含多条表达式,一定要用大括号将其括起来。如果没有这个大括号,宏定义中的多条表达式很有可能只有第一句会被执行,正如下面的代码片段:
#define CLEAR_CUBE_VALUE( l, w, h )\
l = 0;\
w = 0;\
h = 0;
int i = 0;
for (i = 0; i < CUBE_ACOUNT; i++)
CLEAR_CUBE_VALUE( Cubes[i].l, Cubes[i].w, Cubes[i].h );
简单的字符替代,并不能保证多条表达式都会放入for循环的循环体内,因为没有将它包围在循环体内的大括号中。正确的做法应该是用大括号将多条表达式括起来,这样就能保证多条表达式全部执行了,如下面的代码片段所示:
#define CLEAR_CUBE_VALUE( l, w, h )\
{\
l = 0;\
w = 0;\
h = 0;\

}


请记住:正确合理使用C语言中的宏,能有效地增强代码的可读性。但是也要遵守一定的规则,避免踏入其中的陷阱:(1)用宏定义表达式时,要使用完备的括号。(2)使用宏时,不允许参数发生变化。(3)用大括号将宏所定义的多条表达式包括起来。
原创粉丝点击