点击上方蓝色字体了解更多的嵌入式编程实用技能。
如果你觉得该文章对你有帮助,欢迎点赞+关注
对于C/C++语言来说,头文件的设计体现了大部分的系统设计。不合理的头文件布局是编译时间过长的根因,不合理的头文件实际上是不合理的设计。
在本篇文章中,我们将讨论C语言中头文件包含的一些最佳实践和原则,即头文件地包含顺序。
当在编写程序时,头文件的包含顺序非常重要。正确的头文件包含顺序可以确保代码的正确性、可读性和可维护性。
头文件大概可以分以下几类:
系统头文件:例如stdio.h、stdlib.h等。这些头文件包含了C语言的基本函数和类型定义。
第三方库头文件:这些头文件通常提供了非系统头文件库中定义的函数和数据结构的声明,即大部分开源代码。
自定义头文件:这些头文件应该包含您自己编写的函数、结构和宏的声明。
关于头文件包含顺序有以下方面的讨论:
《Google C++ 编程风格指南》中关于头文件的包含顺序规则
C标准库 –> C++标准库 –> 第3方库的头文件 –> 自己工程的头文件。如果是cpp文件最先包含的是首选的头文件,即例如a.cpp文件中应该优先包含a.h,首选的头文件是为了减少隐藏依赖。
其目的是:为了减少隐藏依赖,同时头文件和其实现文件匹配,应该先包含其首选项(即其对应的头文件)。
《C++编程思想》中关于头文件的包含顺序规则
头文件被包含的顺序是从“最特殊到最一般”。顺序应该是这样的:
在本地目录的任何头文件 –> 所有“工具”头文件 –> 第3方库头文件 ->标准C++/C库头文件
其目的是:保证.h文件的组成部分不被它自身解析(parse),这可以避免潜在的使用错误。因为被自身解析缺乏明确提供的声明或定义。在.c文件的第一行包含.h 文件能确保所有对于构件的物理界面重要的内部信息块都在.h中,如果的确是缺少了某些信息块,一旦编译这个.c文件时就可以发现这个问题。
他们的的不同观点在于:头文件的包含顺序不同
// Google C++ 编程风格指南
#include "math.h" // 标准库
#include "xxx/log.h" // 三方库
#include "xxxx.h" // 模块库
// C++编程思想
#include "xxxx.h" // 模块库
#include "xxx/log.h" // 三方库
#include "math.h" // 标准库
《Google C++ 编程风格指南》和《C++编程思想》倡导的包含头文件的顺序各有优点,《Google C++ 编程风格指南》应该能大量减少隐藏的头文件依赖,而《C++编程思想》则很容易让你清楚知道你所定义的接口是否和系统库及第三方库发生冲突。
《Google 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语言编程规范”的头文件规范内容的整理,参考该规范,如何合理地规划头文件。
代码编程规范-扩展(头文件)