很多事不深入以为自己懂了,但真正用到项目上,才会发现其中的问题。曾以为自己写C语言已经轻车熟路了,特别是对软件文件的工程管理上,因为心里对自己的代码编写风格还是有自信的。
本人曾经认为,一个.c文件对应一个.h文件,.c文件只包含它自身的.h文件就好,若.c文件中用到其他文件中的内容,则.h文件把用到的头文件包含进来就可以了。
这种思想在项目代码量小,工程文件少时貌似看不出问题,但随着工程文件数量越来越多,我发现了这种思想存在弊端:头文件互相包含,导致编译时自以为有些宏变量声明了,它就能起作用,但实际测试发现这种方式编码后,有些声明的宏没能起到作用。
经过领导及同事的指正,自己才明白原有的代码编写习惯不正确。应该秉承.c文件对应的.h文件只包含头文件里用到的其它文件的头文件,任何非必须的.h文件不要包含;而.c文件里面要包含用到的所有.h文件。这样写即使存在.c文件内头文件重复包含也不伤大雅。
语言描述有时太抽象,还是符号举例说明下:假如有两个.c文件分别为A.c和B.c,自然它们都有各自的A.h和B.h文件。
✍ 原来的思路
A.c里面只有一个#include "A.h",而A.h所包含的就是一大堆如B.h,C.h,D.h…..文件,因为A.c文件里面要用到B.h,C.h,D.h里面的内容。如图所示。
✍ 新思路
A.h里面只包含A.h所写内容要用到的.h文件,很多时候A.h里面无需任何.h文件.而在A.c文件内就要写成 #include "B.h" #include "C.h" #include "D.h"。而且两个文件的.c文件在头文件包含上可以互相包含。如图所示。
项目中遇到的这个头文件包含问题导致我重新搜索资料进行该问题的深入了解,故下文是通过网络资源的搜查及加上自己对它的理解,进行了相关内容的整理,希望对感兴趣的小伙伴有所帮助。
㉿ 背景
对于C语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因,不合理的头文件实际上不合理的设计。
㉿ 依赖
特指编译依赖。若x.h包含了y.h,则称作x依赖y。依赖关系会进行传导,如x.h包含y.h,而y.h又包含了z.h,则x通过y依赖了z。依赖将导致编译时间的上升。
虽然依赖是不可避免的,也是必须的,但是不良的设计会导致整个系统的依赖关系无比复杂,使得任意一个文件的修改都要重新编译整个系统,导致编译时间巨幅上升。
在一个设计良好的系统中, 修改一个文件,只需要重新编译数个,甚至是一个文件。
曾经做过一个实验,把所有函数的实现通过工具注释掉,其编译时间只减少了不到10%,究其原因,在于A包含B, B包含C, C包含D,最终几乎每一个源文件都包含了项目组所有的头文件,从而导致绝大部分编译时间都花在解析头文件上。
某产品更有一个“优秀实践”,用于将.c文件通过工具合并成一个比较大的.c文件,从而大幅度提高编译效率。
其根本原因还是在于