为啥在包含警卫之前放置#include

Posted

技术标签:

【中文标题】为啥在包含警卫之前放置#include【英文标题】:Why Placing #include BEFORE include guards为什么在包含警卫之前放置#include 【发布时间】:2014-05-19 10:37:05 【问题描述】:

在头文件中的包含保护之前放置#include指令是否有任何正当理由:

#include "jsarray.h"
#include "jsanalyze.h"
#include "jscompartment.h"
#include "jsgcmark.h"
#include "jsinfer.h"
#include "jsprf.h"
#include "vm/GlobalObject.h"

#include "vm/Stack-inl.h"

#ifndef jsinferinlines_h___
#define jsinferinlines_h___

//main body mostly inline functions 

#endif

注意,这个例子取自一个真实的高调开源项目,应该由经验丰富的程序员开发——Firefox 10 中使用的 Mozilla Spidermonkey 开源 javascript 引擎(最新版本中也存在相同的头文件) .

在备受瞩目的项目中,我认为他们的设计背后一定有一些正当的理由。在包含防护之前有 #include 的正当理由是什么?它是适用于仅内联函数头文件的模式吗?还要注意,这个头文件(jsinferinlines.h)实际上是通过最后一个#include "vm/Stack-inl.h"(这个头文件包括很多其他头文件,其中一个实际上又包括这个jsinferinlines.h)指令在include保护之前包含了自己,这个对我来说更没有意义。

【问题讨论】:

【参考方案1】:

因为您希望这些标头包含它们自己的保护,因此它并没有什么区别。

还要注意,这个头文件(jsinferinlines.h)实际上是通过最后一个#include“vm/Stack-inl.h”包含自己的(这个头文件包括很多其他头文件,其中一个实际上包括这个jsinferinlines .h 再次)在包含保护之前的指令,这对我来说更没有意义。

不会有任何不同,因为包含该文件时的工作流程将是:

包括所有标题到"vm/Stack-inl.h" 包括"vm/Stack-inl.h" 直到最后一个#include "jsinferinlines.h"(您的文件) 再次包含文件jsinferinlines.h(跳过所有以前的包含,因为包含保护) 过去#include "vm/Stack-inl.h" 终于从#ifndef jsinferinlines_h___开始处理

但总的来说,相互头递归是不好的,应该不惜一切代价避免。

【讨论】:

它对编译性能有相当大的影响,因为它必须先打开所有这些文件。 (当然,真正的编译器通常会缓存它们,但仍然如此。) 如果我们有两个包含彼此的文件怎么办? (我不是在这里讨论这种做法有多糟糕)。在包含守卫的正常使用中,当它们是文件中的第一件事时,什么都不会发生。如果包含在守卫之前,编译器将进入无限循环。 @WojtekSurowka,当然。不过,这是你的错误。 @Jeffrey - 如果文件包含自身,现代编译器会做什么? @WojtekSurowka:现代编译器将检测常见的“包含守卫”模式并相应地优化对fopen 的调用。包含的合法使用应该像优化没有发生一样工作(否则它是一个错误),并且应该检测到错误(无限循环等......)(否则它是一个错误)。当然,现代版本的 gcc 和 clang 应该可以很好地处理这些边缘情况。【参考方案2】:

当时 SpiderMonkey 标头中有很多包含循环,将标头保护置于顶部会导致难以理解的编译错误 - 我怀疑将标头保护置于包含之下只是清理包含的增量步骤。

不过,我无法告诉您导致它产生影响的确切包含顺序。

如今,SM 标头中不应该有任何包含循环,并且它们的样式是通过 Nicholas Nethercote 编写的 python 脚本强制执行的。如果您今天查看 jsinferinlines.h,您会看到标头保护位于其所属的顶部。

【讨论】:

以上是关于为啥在包含警卫之前放置#include的主要内容,如果未能解决你的问题,请参考以下文章

包含文件将自身包含在警卫中

Erlang,使用警卫之前的语法错误

includeinclude_oncerequirerequire_once其区别

犯错误:多个定义...尽管包括警卫

包括问题和包括警卫

Angular2 相对路径在警卫中导航