#include <文件名> 和 #include“文件名” 有什么区别?

在 C 和 C ++ 编程语言中,在include语句中使用尖括号和引号有什么区别,如下所示?

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

答案

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

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

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

有关搜索路径的 GCC 文档中提供了更完整的描述。

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

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在指令中的预处理令牌与普通文本一样进行处理。 (当前定义为宏名的每个标识符都将替换为其预处理令牌的列表。)所有替换后产生的指令应与前两种形式之一匹配。实现定义了将<>预处理令牌对之间的一系列预处理令牌或一对"字符"组合为单个标头名称预处理令牌的方法。

定义:

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

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

<和> 之间的字符序列唯一地指的是标头,不一定是文件。实现几乎可以随意使用它们的字符序列。 (不过,大多数情况下,仅将其视为文件名,然后在其他帖子中声明在include 路径中进行搜索即可。)

如果使用#include "file"形式,则实现将首先查找具有给定名称的文件(如果支持)。如果不(不支持),或者搜索失败,则该实现的行为就像使用其他形式( #include <file> )。

另外,当#include指令与以上两种形式都不匹配时,将使用第三种形式。在这种形式中,对#include指令的 “操作数” 进行了一些基本的预处理(例如宏扩展),并且预期结果将与其他两种形式之一匹配。

这里有一些很好的答案引用了 C 标准,但忘记了 POSIX 标准,尤其是c99(例如 C 编译器)命令的特定行为。

根据开放团体基本规范第 7 期

-I 目录

更改搜索名称不是绝对路径名的标头的算法,以在查找普通位置之前先查找由目录路径名命名的目录 。因此,应首先在带有#include行的文件目录中搜索名称包含在双引号(“”)中的标题,然后在-I选项命名的目录中进行搜索,然后在通常的位置中进行搜索。对于名称括在尖括号(“<>”)中的标头,应仅在-I选项命名的目录中搜索标头,然后在通常的位置中搜索。 -I选项中命名的目录应按指定顺序搜索。实现应在单个c99命令调用中至少支持此选项的十个实例。

因此,在符合 POSIX 的环境中,使用符合 POSIX 的 C 编译器, #include "file.h"可能会首先搜索./file.h ,其中.是包含#include语句的文件所在的目录,而#include <file.h>可能会首先搜索/usr/include/file.h ,其中/usr/include是系统定义的常用位置标头(POSIX 似乎未定义)。

GCC 文档对两者之间的区别了以下几点:

使用预处理指令'#include'包括用户和系统头文件。它有两个变体:

#include <file>

此变体用于系统头文件。它在系统目录的标准列表中搜索名为 file 的文件。您可以使用-I选项在此列表之前添加目录(请参阅Invocation )。

#include "file"

此变体用于您自己程序的头文件。它首先在包含当前文件的目录中搜索一个名为 file 的文件,然后在引号目录中搜索,然后在<file>使用相同的目录。您可以使用-iquote选项将目录-iquote报价目录列表的前面。参数'#include'不管用引号引起来还是用尖括号分隔,其行为都类似于字符串常量,因为注释无法识别,宏名称也不会扩展。因此, #include <x/*y>指定包含名为x/*y的系统头文件。

但是,如果文件中出现反斜杠,则将其视为普通文本字符,而不是转义字符。没有适合于 C 中字符串常量的字符转义序列被处理。因此, #include "x\n\\y"指定包含三个反斜杠的文件名。 (某些系统将 '\' 解释为路径名分隔符。所有这些系统也以相同的方式解释'/' 。仅使用'/'最可移植。)

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

它确实:

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

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

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

如果./位于<default_include_paths> ,则没有任何区别。

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

<file>包含告诉预处理器搜索-I目录和在预定义的目录第一 ,然后在. c 文件的目录。在"file"包含告诉预处理器搜索源文件所在的目录,然后恢复到-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>格式。其余的是实现定义的。

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

  • 形式的预处理指令

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

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

  • 形式的预处理指令

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

    导致由与指定的序列标识的源文件的全部内容替换该指令的"分隔符。命名的源文件中搜索以实现定义的方式。如果不支持该搜索,如果搜索失败,伪指令将被重新处理,就像读取

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

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

  • 形式的预处理指令

    #include pp-tokens new-line

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

定义:

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

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

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

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

特别是感谢您的出色回答。 Adam Stelmaszczyk 和 piCookie,以及 aib。

像许多程序员一样,我使用了非正式约定,即对特定于应用程序的文件使用"myApp.hpp"形式,对库和编译器系统文件(即在/IINCLUDE环境变量中指定的文件)使用<libHeader.hpp>形式。 ,多年来一直以为这是标准。

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

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

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

如果您真的担心它, "./myHeader.h"用于与源代码位于同一目录中的包含文件(我当前的大型项目中有些重复的包含文件名分散存在,这确实是一个配置管理问题)。

为了方便起见,这里是MSDN 解释复制在这里。

引用形式

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

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

尖括号形式

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

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

#include <file.h>告诉编译器在其 “includes” 目录中搜索标头,例如,对于 MinGW,编译器将在 C:\ MinGW \ include \ 或安装了编译器的任何位置搜索file.h

#include "file"告诉编译器在当前目录(即源文件所在的目录)中搜索file

您可以对 GCC 使用-I标志来告诉它,当遇到带有尖括号的 include 时,它还应该在-I之后的目录中搜索标头。 GCC 会将标志后的目录视为includes目录。

例如,如果您在自己的目录中有一个名为myheader.h的文件,那么如果您使用标志-I .调用了 GCC,则可以说#include <myheader.h> -I . (指示它应搜索当前目录中的包含。)

如果没有-I标志,则必须使用#include "myheader.h"包含文件,或将myheader.h移至编译的include目录。