C语言-带参数宏定义易出现的隐藏bug和定义方式归纳
<p>宏定义尤其是带参数的宏定义,特别容易出现一些隐藏问题,因为宏定义在预处理阶段是按照定义原封不动的进行展开,此时如果展开之后涉及到运算符优先级的问题,那么隐藏bug就此出现。<br/>这里我先列举一个简单的例子,然后归纳下带参数宏定义对于括号使用的一些说明。<br/></p><p class="artical_littlestyle1">1.构造带有隐藏bug的宏定义</p><p>下面定义两个带参数宏,MUL_TWO是将两个数进行相乘,MUL_THREE是将三个数进行相乘。<br/><span style="color: rgb(0, 112, 192);">#define MUL_TWO(val1, val2) (val1 * val2)<br/>#define MUL_THREE(x, y, z) (MUL_TWO(x, y) * z)</span><br/>比如我这里计算2 * 3 * 4的运算结果,那么只需调用宏MUL_THREE(2, 3, 4)就可得到计算结果为:24,计算结果是正确的。但是如果将MUL_THREE(2, 3, 4)修改为MUL_THREE(1+1, 1+2, 1+3),此时的运算结果又是多少呢,很简单,我们将这个宏进行展开,展开的过程如下所示:<br/><span style="color: rgb(0, 112, 192);">MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * 1+3)<br/>(MUL_TWO(1+1, 1+2) * 1+3) => ((1+1 * 1+2) * 1+3)</span><br/>然后我们计算下,得出结果是7,是不是计算错误了。<br/></p><p class="artical_littlestyle2">2.改造上述宏定义</p><p>这里的宏定义还是比较简单的,并且大多数的小伙伴应该都知道在定义带参数的宏时,参数需要使用括号括起来,那么我们改造下上述的宏,改造结果如下所示:<br/><span style="color: rgb(0, 112, 192);">#define MUL_TWO(val1, val2) ((val1) * (val2))<br/>#define MUL_THREE(x, y, z) (MUL_TWO(x, y) * z)</span><br/>此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:<br/><span style="color: rgb(0, 112, 192);">MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * 1+3)<br/>(MUL_TWO(1+1, 1+2) * 1+3) => (((1+1) * (1+2)) * 1+3)</span><br/>然后我们计算下,得出结果是9,计算结果还是有问题。仔细检查下宏定义,原来是对MUL_THREE宏的z没有用括号括起来,这个问题也是比较容易犯的,修改好之后的宏如下所示:<br/><span style="color: rgb(0, 112, 192);">#define MUL_TWO(val1, val2) ((val1) * (val2))<br/>#define MUL_THREE(x, y, z) (MUL_TWO(x, y) * (z))</span><br/>此时再来对MUL_THREE(1+1, 1+2, 1+3)进行展开,展开的过程如下所示:<br/><span style="color: rgb(0, 112, 192);">MUL_THREE(1+1, 1+2, 1+3) => (MUL_TWO(1+1, 1+2) * (1+3))<br/>(MUL_TWO(1+1, 1+2) * (1+3)) => (((1+1) * (1+2)) * (1+3))</span><br/>此时的计算结果就是没问题的了。<br/>这里我再提个问题,为什么你在MUL_THREE宏中,只使用括号括起了z,为啥x和y你不同等对待,确实哈,如果对于不是很熟悉的小伙伴,可能看到我说的情况,会毫不犹豫的也对x和y进行同样的保护;也有的小伙伴看到我说的这个情况可能脑子里面就晕了。<br/></p><p class="artical_littlestyle3">3.带参数宏定义对于括号使用的一些说明</p><p>其实不对x和y做保护是有一个前提的,那就是你所定义的每一个宏定义都要确保对在当前宏中使用到的参数用括号进行保护。不知道各位明白我的意思不,不明白的话,看看我下面的总结吧。<br/><span style="color: rgb(255, 0, 0);">带参数宏定义,对于括号何时使用的总结:</span><br/><span style="color: rgb(0, 112, 192);">(1).带参数宏定义,如果参数在当前的宏中有进行运算,那么必须对该参数使用括号括起来(类似例子中MUL_THREE里面的z,MUL_TWO里面的val1和val2);<br/>(2).带参数宏定义,如果参数没有在当前的宏中有进行运算,而是直接当成参数传递给其他的宏,那么该参数是不用使用括号进行保护的(类似例子中MUL_THREE里面的x和y)。</span><br/>对于上面的总结第(2)点,能够对传递给其他宏的参数不进行括号保护是因为总结的第(1)点已经对宏做了一个规定,只要所有的宏定义都按照第(1)点进行书写,那么第(2)点自然也就不会出什么问题。<br/>各位小伙伴在定义带参数的宏时,按照我上面说的2点进行书写,什么隐藏bug的也就不会存在了。<br/>如果文中有什么问题欢迎指正,毕竟博主的水平有限。<br/></p>
暂无评论,要不要来个沙发
发表评论
JLink V9掉固件修复(灯不亮) 3Zephyr笔记2 - 在STM32F429上运行HelloWorld 2计算NandFlash要传入的行地址和列地址 1Linux MMC子系统 - 6.eMMC 5.1工作模式-设备识别模式 0Linux MMC子系统 - 5.eMMC 5.1工作模式-引导模式 0Linux MMC子系统 - 4.eMMC 5.1常用命令说明(2) 0
标签云
Linux嵌入式实用技巧ARM内核学习问题集合CC++编程语言阅读笔记汇编Linux内核完全注释Windows驱动开发计算机基础ARM11ARMv7-ASTM32IDESublimeLinux内核学习eMMCMMC子系统Ubuntu操作系统OfficeVMWareAPUEgccRTOS中断漫游世界随笔感悟开发工具软件应用编程VsCodearmccarmclang编译器ZephyrSPIJLink网卡驱动安装各种芯片库函数NFSμCOS内核sambaFlashUnix命令与脚本输入法Linux内核设计与实现gitRIFFWAVJATGFTPar8161安装centos有线上网μCGUI字库工程建立右键菜单网络文件系统Firefox百度NTFS文件系统CodeBlocksCentOS数据结构算法PhotoShop51KeilQTUltraEditscanfglibc宏定义UIDGID优先级娱乐天地SourceInsight磁盘扇区总线I2CPDFBComparePythonI2SFPUMakefileSWDCPUARP软件推荐FileZilla