我有一个包含src
和include
目录的项目。编译时,我通过-I
选项(gcc -Iinclude ...
)传递include
目录。
我应该使用双引号("
)还是尖括号(<
)来包含我自己的头文件?
我试图寻找答案,发现这两种相互矛盾的说法:
- 通过双引号包含相对于c文件的头文件。所有其他内容(包含路径中的头文件)都带有尖括号。->因此使用尖括号
- 包括带尖括号的标准标题。其他都用双引号->因此使用双引号
在我看来,陈述2更清楚。当包含一个带有双引号的文件时,最明显的是它是我自己的头。
我应该使用引号或括号来包含我自己的头文件吗?C标准允许这两种可能性。那么,最佳实践是什么呢?
常见的约定是:
- 对于属于C实现或平台的头文件,请使用
< … >
——项目之外的头文件(如C标准库、Unix或Windows头文件),以及通常为开发环境安装的库的头文件 - 对于属于项目一部分的标题,请使用
" … "
这不是完全由C标准决定的;这是一般做法的问题。对于每个分隔符选择,编译器都有一个查找标头的位置列表(搜索路径)。这些搜索路径通常是为了方便上述使用而设计的,但它们可以通过命令行开关、环境变量、系统设置和/或构建编译器时的设置进行自定义(取决于您使用的编译器)。
以下是C标准在C 2018 6.10.2中对它们的说明。第2段说:
表单的预处理指令
#
include
<
h-char-序列>
new-line
在实现定义的位置序列中搜索由
<
和>
分隔符之间的指定序列唯一标识的报头,并使该指令被报头的整个内容替换。如何指定位置或标识标头是实现定义的。
第3段说:
表单的预处理指令
#
include
"
q-char-序列"
新品导致用源文件的全部内容替换该指令,该源文件由";分隔符。以实现定义的方式搜索命名的源文件。如果不支持此搜索,或者搜索失败,则指令将被重新处理,如同读取一样
#
include
<
h-char-序列>
新品具有与原始指令相同的包含序列(包括
>
字符,如果有的话)。
注意两者之间的一些差异:
- 括号形式的文本表示它搜索唯一标识为的页眉。引用表单的文本中不包含单词"unique"。这表明方括号形式引用的所有标题都应该彼此不同,如果它们是旨在避免歧义的设计系统的一部分,你可能会想到这一点
- 注意,它说第一个表单"搜索一系列实现定义的位置"。这与编译器有一个搜索标准头的位置列表相一致。对于第二种形式,它使用"由指定序列标识的源文件"。这与在文件系统中使用引号之间的文本作为路径一致
标准中的这段文本非常宽松,都允许使用实现定义的方法来识别文件,因此可以将其中一种扩展为与另一种相同(尽管看到编译器抱怨括号中的头不是唯一的会很有趣),并且编译器配置选项足够广泛,您可以在项目中使用每种方法。然而,一般来说,还是坚持惯例为好。
考虑这个例子:
+- include/
| |
| - header.h
|
+- src/
|
- main.c
声明中说,要么使用:
#include "../include/header.h"
gcc src/main.c
或:
#include <header.h>
gcc -Iinclude src/main.c
您可以决定使用哪种样式。我个人更喜欢第二个。但更重要的是在整个项目中使用一致的风格。
问题中的第一句话符合当前版本的C++核心指南的规则SF.12:
相对于include文件,文件更喜欢#include的引号形式,其他地方都是角括号形式
尽管它与C++23标准15.3注1(草案N4928)的建议相冲突:
使用
< >
形式作为实现提供的头,使用" "
形式作为实现控制之外的源,实现了更广泛的可移植性
我建议您应用SF.12,因为它可以降低包含错误文件的风险,正如他们所解释的:
如果不遵守这一点,将导致难以诊断错误,因为在包含文件时错误地指定了范围,从而拾取了错误的文件。例如,在典型的[编译器实现]中,
#include ""
搜索算法可能首先搜索存在于本地相对路径的文件,然后使用这种形式来引用非本地相对的文件可能意味着,如果文件在本地相对路径上出现(例如,包括的文件被移到新位置),它现在将在上一个include文件之前被找到,并且include集将以意外的方式被更改。
如果使用< >
,则不会搜索相对于包含文件的路径(对于典型的编译器实现),因此上述情况不会成为问题。
或者更一般地说:如果编译器实现找到具有两种形式的文件,请使用不太可能产生歧义的形式。由于< >
是" "
的后备(根据标准)," "
将搜索潜在的更多位置。因此,尽可能使用< >
。
尽管共同的惯例不同。SF.12提供了稍微偏离该公约的技术理由。