为啥在包含警卫之前放置#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的主要内容,如果未能解决你的问题,请参考以下文章