C语言之宏定义命名导致的问题
昨天,我在Code Review的时候看到一个代码。这个又得牵扯到我之前为了将stm32工程从keil迁移到cmake的时候的事情了。
一个星期以前,我尝试了一下st最新推出的stm32 for vscode这个插件。这个插件出奇的方便,它会自己下载依赖,而且支持cmake,clangd这种先进工具。这都不是最重要的,最重要的是,它提供了一个分析器,也就是基于编译器的数据,分析ram、rom的占用情况,包括编译好后的二进制文件里面,他的每个全局变量的内存占用的字节数。这个就让我有了极大的兴趣。在此之前,keil顶多告诉我们,整体的使用情况。
然后我就尝试把之前的一个有点大的stm32工程移植到cmake。keil里面有一个特点是,允许循环头文件包含。但是似乎换了gcc以后就直接不让循环包含了。所谓循环包含就是,假设我有一个总的头文件headfile.h,里面我包含了全部的头文件,比如头文件a.h,但是我a.c里面包含了a.h以后,为了方便用过其他的头文件,我就偷懒包含以下headfile.h解决。这个在keil里面是完全可以的,但是gcc会告诉你,这个循环包含,然后直接就报错了。解决方法,我就只能每个按需包含,而且让这个bsp层的万能头文件只能在app层使用,bsp层禁止循环包含了。
到现在为止,问题还没有出现。问题就出现在了这个headfile.h里面,当我尝试把一个头文件a.h提到最前面(比st的hal库还前面)包含的时候,问题就发生了。
这个头文件里面定义了一个宏,大致内容如下。
1 |
然后碰巧的是,stm32的hal库的头文件里面,有个别结构体的代码如下。
1 | typedef xxx{ |
于是就被替换变成了uint32_t 0x88,然后编译器直接到处报错。那上百个错误真是触目惊心了。我当时看了好一会,一开始怎么也想不到,这hal库内部怎么出错呢。然后我对着重构时候的git历史里面的差异看,结果就是因为这个头文件调换的问题。然后我的解决方案就是把这个外部不用的宏,塞到他自己的源文件里面。这个宏的名字的问题,后面再重构解决。
这个问题的本质是,有一个常量宏,它定义为一个很常用的名称,而且不是一个全大写的情况。于是就导致了宏把变量名都替换了的问题。
所以就总结出来两条规则。
- 常量宏应该全部大写,而且尽可能具体而详细的描述。避免单个单词的这种命名与常用的变量名一样。
- 尽可能少的在头文件暴露宏,除非必须,要不然应该定义在源文件内。
