宏定义尤其是带参数的宏定义,特别容易出现一些隐藏问题,因为宏定义在预处理阶段是按照定义原封不动的进行展开,此时如果展开之后涉及到运算符优先级的问题,那么隐藏bug就此出现。
这里我先列举一个简单的例子,然后归纳下带参数宏定义对于括号使用的一些说明。
1.构造带有隐藏bug的宏定义
下面定义两个带参数宏,MUL_TWO是将两个数进行相乘,MUL_THREE是将三个数进行相乘。
#define MUL_TWO(val1, val2) (val1 * val2) #define MUL_THREE(x, y, z) (MUL_TWO(x, y) * z)
比如我这里计算2 * 3 * 4的运算结果,那么只需调用宏MUL_THREE(2, 3, 4)就可得到计算结果为:24,计算结果是正确的。但是如果将MUL_THREE(2, 3, 4)修改为MUL_THREE(1+1, 1+2, 1+3),此时的运算结果又是多少呢,很简单,我们将这个宏进行展开,展开的过程如下所示:
MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * 1+3) (MUL_TWO(1+1, 1+2) * 1+3) => ((1+1 * 1+2) * 1+3)
然后我们计算下,得出结果是7,是不是计算错误了。
2.改造上述宏定义
这里的宏定义还是比较简单的,并且大多数的小伙伴应该都知道在定义带参数的宏时,参数需要使用括号括起来,那么我们改造下上述的宏,改造结果如下所示:
#define MUL_TWO(val1, val2) ((val1) * (val2)) #define MUL_THREE(x, y, z) (MUL_TWO(x, y) * z)
此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:
MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * 1+3) (MUL_TWO(1+1, 1+2) * 1+3) => (((1+1) * (1+2)) * 1+3)
然后我们计算下,得出结果是9,计算结果还是有问题。仔细检查下宏定义,原来是对MUL_THREE宏的z没有用括号括起来,这个问题也是比较容易犯的,修改好之后的宏如下所示:
#define MUL_TWO(val1, val2) ((val1) * (val2)) #define MUL_THREE(x, y, z) (MUL_TWO(x, y) * (z))
此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:
MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * (1+3)) (MUL_TWO(1+1, 1+2) * (1+3)) => (((1+1) * (1+2)) * (1+3))
此时的计算结果就是没问题的了。
这里我再提个问题,为什么你在MUL_THREE宏中,只使用括号括起了z,为啥x和y你不同等对待,确实哈,如果对于不是很熟悉的小伙伴,可能看到我说的情况,会毫不犹豫的也对x和y进行同样的保护;也有的小伙伴看到我说的这个情况可能脑子里面就晕了。
3.带参数宏定义对于括号使用的一些说明
其实不对x和y做保护是有一个前提的,那就是你所定义的每一个宏定义都要确保对在当前宏中使用到的参数用括号进行保护。不知道各位明白我的意思不,不明白的话,看看我下面的总结吧。
带参数宏定义,对于括号何时使用的总结:
(1).带参数宏定义,如果参数在当前的宏中有进行运算,那么必须对该参数使用括号括起来(类似例子中MUL_THREE里面的z,MUL_TWO里面的val1和val2);
(2).带参数宏定义,如果参数没有在当前的宏中有进行运算,而是直接当成参数传递给其他的宏,那么该参数是不用使用括号进行保护的(类似例子中MUL_THREE里面的x和y)。
对于上面的总结第(2)点,能够对传递给其他宏的参数不进行括号保护是因为总结的第(1)点已经对宏做了一个规定,只要所有的宏定义都按照第(1)点进行书写,那么第(2)点自然也就不会出什么问题。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !