题 #include 和#include“filename”有什么区别?


在C和C ++编程语言中,使用尖括号和使用引号之间的区别是什么 include 声明如下?

  1. #include <filename> 
  2. #include "filename"

1813
2017-08-22 01:40


起源


gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC6 - legends2k
GCC在哪里寻找其头文件 - theoretisch


答案:


实际上,区别在于预处理器搜索包含文件的位置。

对于 #include <filename> 预处理器以依赖于实现的方式搜索,通常在编译器/ IDE预先指定的搜索目录中。此方法通常用于包括标准库头文件。

对于 #include "filename" 预处理器首先在与包含该指令的文件相同的目录中进行搜索,然后按照用于该指令的搜索路径进行搜索 #include <filename> 形成。此方法通常用于包括程序员定义的头文件。

GCC提供了更完整的描述 搜索路径上的文档


1046
2017-08-22 01:40



声明:“预处理器在同一目录中搜索...”在实践中可能是正确的,但标准规定命名源文件是“以实现定义的方式搜索”。请参阅piCookie的回答。 - Richard Corden
虽然你的答案似乎是“真实的”,因为这是按惯例工作的实现数量,你应该仔细看看aib和piCookie的答案。他们都指出(由C标准的措辞支持)真正的区别是包含“标题”而不是包含“源文件”(不,这并不意味着“.h”与“。”。 C”)。在此上下文中的“源文件”可以(并且通常是,并且几乎总是应该是)“.h”文件。标头不一定需要是文件(编译器可以例如包括静态编码的标头,而不是文件中的标头)。 - Dan Moulding
“...预处理器在与要编译的文件相同的目录中搜索要包含的文件。”这句话并不完全正确。我对这个问题感兴趣,因为我很好奇实际的答案是什么,但我知道这不是真的,因为至少用gcc指定一个额外的包含路径-I,它将搜索用#include“filename指定的文件。 H” - Gabriel Southern
那些不喜欢答案的人,请举一个实际的例子,哪里错了。 - 0kcats
果然,我最近在包含来自“相同”库的标题时混合了这些语法,最终出现了重新定义错误。如果我理解正确, #include <...> 使用了系统上安装的软件包 #include "..." 使用附近的存储库版本。我可能会倒退。无论哪种方式,打包头中的include guard都以下划线为前缀。 (它可能是包装的惯例,也可能是故意防止两者混合的方法,尽管版本限定符对我来说更有意义。) - John P


唯一的方法是阅读您的实现文档。

C标准,第6.10.2节,第2至4段规定:

  • 表单的预处理指令

    #include <h-char-sequence> new-line
    

    搜索一系列实现定义的位置,以查找由指定序列之间唯一标识的标头 < 和 > 分隔符,并导致该标题的整个内容替换该指令。如何指定位置或标识的标头是实现定义的。

  • 表单的预处理指令

    #include "q-char-sequence" new-line
    

    导致由指定序列之间标识的源文件的全部内容替换该指令 " 分隔符。以实现定义的方式搜索指定的源文件。如果不支持此搜索,或者搜索失败,则会重新处理该指令,就像它读取一样

    #include <h-char-sequence> new-line
    

    具有相同的包含序列(包括 > 来自原文的字符(如果有的话)   指示。

  • 表单的预处理指令

    #include pp-tokens new-line
    

    (允许与前两种形式中的一种不匹配)。之后的预处理令牌 include 在指令中处理就像在普通文本中一样。 (当前定义为宏名称的每个标识符将替换为其预处理标记的替换列表。)所有替换后生成的指令应与前两个表单中的一个匹配。一种预处理令牌序列的方法 < 和a > 预处理令牌对或一对 " 字符组合成单个标题名称预处理标记是实现定义的。

定义:

  • h-char:除了换行符之外的源字符集的任何成员 >

  • q-char:源字符集的任何成员,除了换行符和 "


594
2017-09-16 21:06



相关:实施 克++ 并在 visual c ++ - Alexander Malakhov
@piCookie <filename>和“filename”搜索实现定义的位置。那么区别是什么呢 ? - onmyway133
@Stefan,我只是引用了一个没有说明INCLUDE_PATH的标准。您的实施可能会这样做,而我的可能不会。最初的问题通常是C,而不是gcc(我认为不使用INCLUDE_PATH)或Microsoft C(我认为可以)或任何其他问题,所以它不能一般地回答,而是必须引用每个实现的文档。 - piCookie
与所有这些情况一样,具体示例(尤其是常见情景)非常有用并且同样受到重视。不必要的钝性通用答案没有那么多的实际用途。 - vargonian
“这是C标准如何冗长而不回答你的问题” - anatolyg


<和>之间的字符序列唯一地引用标题,该标题不一定是文件。实现几乎可以随意使用字符序列。 (但是,大多数情况下,只需将其视为文件名并在中进行搜索 包括路径,正如其他帖子所述。)

如果 #include "file" 如果支持,则实现首先查找给定名称的文件。如果不是(支持),或者如果搜索失败,则实现的行为就像另一个(#include <file>)形式被使用。

此外,存在第三种形式,并在使用时使用 #include 指令与上述任何一种形式都不匹配。在这种形式中,一些基本的预处理(如宏扩展)是在“操作数”上完成的 #include 指令,结果预计与其他两种形式中的一种相匹配。


215
2017-09-08 17:43



+1,这可能是这里最简洁和正确的答案。根据标准(piCookie在他的回答中引用),唯一 真实 差异是“标题”与“源文件”。搜索机制是以任一方式实现定义的。使用双引号意味着您打算包含“源文件”,而尖括号意味着您打算包含一个“标题”,正如您所说,它可能根本不是文件。 - Dan Moulding
请参阅Dan Molding对quest49的回答的评论;标准头文件不必是文件形式,它们可以是内置的。 - aib
十年来我一直在阅读“标准标题不一定是文件形式”。关心提供一个真实世界的例子? - Maxim Egorushkin
@Maxim Yegorushkin:我也想不出任何现有的现实世界的例子;但是,除非标题不必是文件,否则MS-DOS不能存在完整的C11编译器。这是因为某些C11标头名称与“8.3”MS-DOS文件名限制不兼容。 - Dan Moulding
@MaximEgorushkin:VAX / VMS C编译器将所有C运行时库头保存在单个文本库文件中(类似于unix存档),并使用了之间的字符串 < 和 >作为索引到图书馆的关键。 - Adrian McCarthy


这里的一些好的答案引用了C标准,但忘记了POSIX标准,特别是该标准的具体行为 c99(例如C编译器) 命令。

根据 公开集团基本规格问题7

-一世  目录

更改搜索名称不是绝对路径名的标头的算法,以查找由其命名的目录 目录 在通常的地方看之前的路径名。因此,名称以双引号(“”)括起来的标题应首先在文件的目录中搜索 #包括 行,然后在名为的目录中 -一世 选项,并在通常的地方持续。对于名称用尖括号(“<>”)括起来的标题,只能在名为的目录中搜索标题 -一世 选项,然后在通常的地方。目录命名为 -一世 应按指定的顺序搜索选项。实现应在单个中支持至少十个此选项的实例 C99 命令调用。

因此,在符合POSIX标准的环境中,使用符合POSIX标准的C编译器, #include "file.h" 可能会搜索 ./file.h 首先,在哪里 . 是带有文件的目录 #include 声明,而 #include <file.h>,很可能会去搜索 /usr/include/file.h 首先,在哪里 /usr/include 是你的系统定义 平常的地方 对于标题(它似乎没有被POSIX定义)。


92
2017-07-20 09:29



文本的确切来源是什么?它来自IEEE Std 1003.1,2013的规范部分吗? - osgx
@osgx:在POSIX规范中找到了这个措辞(或类似的东西) c99  - 这是C编译器的POSIX名称。 (POSIX 2008标准很难提及C11; 2013年对POSIX 2008的更新没有改变它所提到的C标准。) - Jonathan Leffler


它确实:

"mypath/myfile" is short for ./mypath/myfile

. 是文件的目录所在的文件 #include 包含在和/或编译器的当前工作目录中,和/或 default_include_paths

<mypath/myfile> is short for <defaultincludepaths>/mypath/myfile

如果 ./ 在... <default_include_paths>那么它没有什么区别。

如果 mypath/myfile 在另一个包含目录中,行为未定义。


36
2018-02-08 11:45



没有, #include "mypath/myfile" 不等于 #include "./mypath/myfile"。正如piCookie的回答所说,双引号告诉编译器以实现定义的方式搜索 - 包括在指定的位置搜索 #include <...>。 (实际上,它可能是等价的,但仅仅是因为,例如, /usr/include/mypath/myfile 可以称为 /usr/include/./mypath/myfile  - 至少在类Unix系统上。) - Keith Thompson
@Keith Thompson:没错,我在想我的Linux机箱。显然它可能会有所不同。虽然在实践中,Windows作为非Posix操作系统也会解释/作为路径分隔符,而./也存在。 - Stefan Steiger
-L dirpath 选项然后添加 dirpath 到了 defaultincludepaths而不是赋予其他意义 . (如上所述)。这具有两者的预期后果 #include "..." 和 #include <...> 搜索 dirpath - Protongun
我认为这个答案是不正确的,因为这意味着在当前工作目录中总是会查找包含双引号的标题。搜索机制更加详细;这个答案是不完整的。我没有将此评论添加到抱怨或抱怨中,但是因为系统要求我添加评论以解释我为什么选择这个答案。 - Carlo Wood


GCC文件说 以下是关于两者的区别:

使用预处理指令包含用户和系统头文件 ‘#include’。它有两个变种:

#include <file>

此变体用于系统头文件。它在标准的系统目录列表中搜索名为file的文件。您可以使用以下目录将目录添加到此列表中 -I 选项(见 调用)。

#include "file"

此变体用于您自己的程序的头文件。它首先在包含当前文件的目录中搜索名为file的文件,然后在quote目录中搜索,然后使用相同的目录 <file>。您可以使用以下命令将目录添加到引用目录列表中 -iquote 选项。     的论点 ‘#include’无论是用引号还是尖括号分隔,行为都像字符串常量,因为无法识别注释,并且不会扩展宏名称。从而, #include <x/*y> 指定包含名为的系统头文件 x/*y

但是,如果文件中出现反斜杠,则它们被视为普通文本字符,而不是转义字符。没有处理适合C中字符串常量的字符转义序列。从而,#include "x\n\\y"指定包含三个反斜杠的文件名。 (有些系统将'\'解释为路径名分隔符。所有这些都解释了 ‘/’ 一样的方法。它最便于使用 ‘/’。)

如果文件名后面的行上有任何内容(注释除外),则会出错。


30
2018-01-14 04:52





<file> include告诉预处理器搜索 -I 目录和预定义目录 第一,然后在.c文件的目录中。该 "file" include告诉预处理器搜索源文件的目录 第一,然后恢复 -I 和预定义的。无论如何都要搜索所有目的地,只有搜索顺序不同。

2011标准主要讨论“16.2源文件包含”中的包含文件。

2表单的预处理指令

# include <h-char-sequence> new-line

搜索一系列实现定义的位置,以查找由唯一标识的标头   <和>分隔符之间的指定序列,并导致   用标题的全部内容替换该指令。   如何指定地点或标识的标题是   实现定义。

3表单的预处理指令

# include "q-char-sequence" new-line

导致由该标识的源文件的全部内容替换该指令   “分隔符”之间的指定序列。命名的源文件是   以实现定义的方式搜索。如果这个搜索是   不支持,或者如果搜索失败,则将指令重新处理为   如果它读

# include <h-char-sequence> new-line

使用原始指令中相同的包含序列(包括>字符,如果有的话)。

注意 "xxx" 形式降级为 <xxx> 如果找不到文件,请填写表单。其余的是实现定义的。


23
2017-09-03 12:17



你能提供一个参考C标准的地方吗? -I 业务是指定的? - juanchopanza
@juanchopanza完了。另见最佳答案:) - Arkadiy
我看不到任何参考 -I。 - juanchopanza
那是“实现定义的”部分。 - Arkadiy
叹。我的答案,你听起来像是语言中指定的东西。 - juanchopanza


按标准 - 是的,它们是不同的:

  • 表单的预处理指令

    #include <h-char-sequence> new-line
    

    搜索一系列实现定义的位置,以查找由指定序列之间唯一标识的标头 < 和 > 分隔符,并导致该标题的整个内容替换该指令。如何指定位置或标识的标头是实现定义的。

  • 表单的预处理指令

    #include "q-char-sequence" new-line
    

    导致由指定序列之间标识的源文件的全部内容替换该指令 " 分隔符。以实现定义的方式搜索指定的源文件。如果不支持此搜索,或者搜索失败,则会重新处理该指令,就像它读取一样

    #include <h-char-sequence> new-line
    

    具有相同的包含序列(包括 > 来自原文的字符(如果有的话)   指示。

  • 表单的预处理指令

    #include pp-tokens new-line
    

    (允许与前两种形式中的一种不匹配)。之后的预处理令牌 include 在指令中处理就像在普通文本中一样。 (当前定义为宏名称的每个标识符将替换为其预处理标记的替换列表。)所有替换后生成的指令应与前两个表单中的一个匹配。一种预处理令牌序列的方法 < 和a > 预处理令牌对或一对 " 字符组合成单个标题名称预处理标记是实现定义的。

定义:

  • h-char:除了换行符之外的源字符集的任何成员 >

  • q-char:源字符集的任何成员,除了换行符和 "

请注意,该标准没有说明实现定义方式之间的任何关系。第一种形式以一种实现定义的方式搜索,另一种以(可能是其他)实现定义的方式搜索。该标准还规定应存在某些包含文件(例如, <stdio.h>)。

正式地,您必须阅读编译器的手册,但通常(按传统) #include "..." form搜索文件的目录 #include 先发现,然后是目录 #include <...> 表单搜索(包含路径,例如系统标题)。


16
2017-08-18 06:21



这与piCookie的答案大致相同 7年 早。 - Kyle Strand
@KyleStrand那是因为同一文本是标准中相关部分的引用 - 该文本 应该 相同。实际答案不是相同的文本,并且有些不同 - 虽然我也认识到它将写在实现的文档中我还注意到还有一种传统方式这些被解释(我使用的大多数或所有编译器都尊重) 。 - skyking
IMO这是最好的答案,因为它涵盖了标准所说的内容和大多数编译器实际执行的内容。 - plugwash


谢谢你的回答,特别是。 Adam Stelmaszczyk和piCookie,以及aib。

像许多程序员一样,我使用了非正式的使用方法 "myApp.hpp" 应用程序特定文件的表单,以及 <libHeader.hpp> 库和编译器系统文件的表单,即。中指定的文件 /I 和 INCLUDE 环境变量,多年来一直认为是标准。

但是,C标准规定搜索顺序是特定于实现的,这会使可移植性变得复杂。更糟糕的是,我们使用jam,它可以自动计算包含文件的位置。您可以为包含文件使用相对路径或绝对路径。即

#include "../../MyProgDir/SourceDir1/someFile.hpp"

较旧版本的MSVS需要双反斜杠(\\),但现在不需要。我不知道它什么时候改变了。只需使用正斜杠与'nix兼容(Windows会接受)。

如果你是  担心,使用 "./myHeader.h" 对于与源代码在同一目录中的包含文件(我当前的非常大的项目有一些重复的包含文件名分散 - 实际上是一个配置管理问题)。

这是 MSDN解释 复制到这里为了您的方便)。

引用形式

预处理器按以下顺序搜索包含文件:

  1. 与包含#include语句的文件位于同一目录中。
  2. 在当前打开的包含文件的目录中,按相反的顺序排列
        他们被打开了。搜索从父包含文件的目录开始
        继续向上通过任何祖父母包含文件的目录。
  3. 沿着每个人指定的路径 /I 编译选项。
  4. 沿着由路径指定的路径 INCLUDE 环境变量。

角括号形式

预处理器按以下顺序搜索包含文件:

  1. 沿着每个人指定的路径 /I 编译选项。
  2. 在命令行上进行编译时,沿着由...指定的路径进行编译 INCLUDE 环境变量。

12
2017-10-14 23:51





至少对于GCC版本<= 3.0,角括号形式不会在包含文件和包含文件之间产生依赖关系。

因此,如果要生成依赖性规则(使用GCC -M选项作为示例),则必须使用引用的表单来存储应该包含在依赖关系树中的文件。

(看到 http://gcc.gnu.org/onlinedocs/cpp/Invocation.html )


11
2017-10-25 12:35