在include指令中使用尖括号和引号有什么区别?
include
#include <filename>
#include "filename"
不同的是预处理器搜索要包含的文件的位置。
#include <filename>预处理器以实现定义的方式搜索,通常是在编译器/IDE预先指定的目录中。此方法通常用于包含C标准库的头文件和与目标平台关联的其他头文件。
#include "filename"预处理器也以实现定义的方式进行搜索,但通常用于包含程序员定义的头文件,并且通常包含与包含指令的文件相同的目录(除非给出绝对路径)。
对于GCC,GCC在搜索路径上留档中提供了更完整的描述。
<file>包含告诉预处理器在-I目录和预定义目录第一中搜索,然后在. c文件的目录中搜索。"file"包含告诉预处理器搜索源文件的目录第一,然后恢复到-I和预定义。无论如何,搜索所有目的地,只有搜索顺序不同。
<file>
-I
"file"
2011年标准主要讨论“16.2源文件包含”中的包含文件。
2表单的预处理指令 # include <h-char-sequence> new-line 搜索实现定义的位置序列,以查找由<和>分隔符之间的指定序列,并导致将该指令替换为标头的全部内容。如何指定位置或标头标识实现-定义。 3表单的预处理指令 # include "q-char-sequence" new-line 导致将该指令替换为源文件的全部内容,该文件由分隔符之间的指定序列。命名的源文件是以实现定义的方式搜索。如果此搜索是不支持,或者如果搜索失败,则将指令重新处理为如果读到 # include <h-char-sequence> new-line 与原始指令中相同的包含序列(包括>字符,如果有)。
2表单的预处理指令
# include <h-char-sequence> new-line
搜索实现定义的位置序列,以查找由<和>分隔符之间的指定序列,并导致将该指令替换为标头的全部内容。如何指定位置或标头标识实现-定义。
3表单的预处理指令
# include "q-char-sequence" new-line
导致将该指令替换为源文件的全部内容,该文件由分隔符之间的指定序列。命名的源文件是以实现定义的方式搜索。如果此搜索是不支持,或者如果搜索失败,则将指令重新处理为如果读到
与原始指令中相同的包含序列(包括>字符,如果有)。
请注意,如果找不到文件,"xxx"表单将降级为<xxx>表单。其余的是实现定义的。
"xxx"
<xxx>
<和>之间的字符序列唯一引用标头,标头不一定是文件。实现可以根据需要自由使用字符序列。(但是,大多数情况下,只需将其视为文件名并在包括路径中进行搜索,就像其他帖子所述。)
如果使用#include "file"表单,如果支持,实现首先查找给定名称的文件。如果不支持(支持),或者如果搜索失败,实现的行为就像使用了另一个(#include <file>)表单一样。
#include "file"
#include <file>
此外,当#include指令与上述任何一种形式都不匹配时,存在第三种形式并使用。在这种形式下,对#include指令的“操作数”进行一些基本预处理(例如宏扩展),结果预计将匹配其他两种形式之一。
#include
了解的唯一方法是阅读实现的留档。
在的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 <h-char-sequence> new-line
在实现定义的位置序列中搜索由<和>分隔符之间的指定序列唯一标识的报头,并导致该指令被报头的全部内容替换。指定位置或标识的标头的方式是实现定义的。
<
>
#include "q-char-sequence" new-line
导致用"分隔符之间指定序列标识的源文件的全部内容替换该指令。以实现定义的方式搜索命名的源文件。如果不支持此搜索,或者搜索失败,则重新处理该指令,就好像它读取
"
与原始包含的相同序列(包括>个字符,如果有的话)指令。
#include pp-tokens new-line
指令中include之后的预处理令牌与普通文本中的预处理令牌相同。(当前定义为宏名称的每个标识符都被其预处理令牌替换列表替换。)所有替换后生成的指令应与前两个表单之一匹配。实现定义了将<和>预处理令牌对或"字符对之间的一系列预处理令牌组合成单个标头名称预处理令牌的方法。
h-char:源字符集的任何成员,除了换行符和>
q-char:源字符集的任何成员,除了换行符和"
它确实:
"mypath/myfile" is short for ./mypath/myfile
.是包含#include的文件的目录,和/或编译器的当前工作目录,和/或default_include_paths
.
default_include_paths
和
<mypath/myfile> is short for <defaultincludepaths>/mypath/myfile
如果./在<default_include_paths>中,那么它没有区别。
./
<default_include_paths>
如果mypath/myfile在另一个包含目录中,则行为未定义。
mypath/myfile
对于#include "",编译器通常搜索包含该包含的文件的文件夹,然后搜索其他文件夹。对于#include <>,编译器不搜索当前文件的文件夹。
#include ""
#include <>
带尖括号的#包含将搜索“与实现相关的位置列表”(这是一种非常复杂的“系统标头”说法)以包含文件。
带有引号的#包含只会搜索一个文件(并且,“以依赖于实现的方式”,bleh)。这意味着,在正常的英语中,它将尝试应用您抛出的路径/文件名,并且不会在系统路径之前添加或篡改它。
此外,如果#support""失败,则标准将其重新读取为#support<>。
gcc留档有一个(编译器特定的)描述,尽管它特定于gcc而不是标准,但比ISO标准的律师式谈话更容易理解。
#include "filename" // User defined header#include <filename> // Standard library header.
示例:
这里的文件名是Seller.h:
Seller.h
#ifndef SELLER_H // Header guard#define SELLER_H // Header guard #include <string>#include <iostream>#include <iomanip> class Seller{private:char name[31];double sales_total; public:Seller();Seller(char[], double);char*getName(); #endif
在类实现中(例如,Seller.cpp和将使用文件Seller.h的其他文件),现在应该包含用户定义的标头,如下所示:
Seller.cpp
#include "Seller.h"
至少对于GCC版本<=3.0,角括号形式不会在包含文件和包含文件之间生成依赖关系。
因此,如果您想生成依赖规则(使用GCC-M选项作为示例),您必须为应该包含在依赖树中的文件使用引用的形式。
(见http://gcc.gnu.org/onlinedocs/cpp/Invocation.html)
这里的一些好答案引用了C标准,但忘记了POSIX标准,特别是c99(例如C编译器)命令的特定行为。
根据开放组基础规范第7期
我目录 更改搜索名称不是绝对路径名的标头的算法,在查找通常位置之前先在目录路径名命名的目录中查找。因此,名称用双引号括起来的标头 ( "" ) 应首先在带有#包括行的文件目录中搜索,然后在我选项中命名的目录中搜索,最后在通常位置中搜索。对于名称用尖括号括起来的标头(“<;>" ), 只能在我选项中命名的目录中搜索标头,然后在通常位置中搜索。应按指定顺序搜索我选项中命名的目录。实现应在单个c99命令调用中支持至少十个此选项的实例。
我目录
更改搜索名称不是绝对路径名的标头的算法,在查找通常位置之前先在目录路径名命名的目录中查找。因此,名称用双引号括起来的标头 ( "" ) 应首先在带有#包括行的文件目录中搜索,然后在我选项中命名的目录中搜索,最后在通常位置中搜索。对于名称用尖括号括起来的标头(“<;>" ), 只能在我选项中命名的目录中搜索标头,然后在通常位置中搜索。应按指定顺序搜索我选项中命名的目录。实现应在单个c99命令调用中支持至少十个此选项的实例。
因此,在符合POSIX的环境中,使用符合POSIX的C编译器,#include "file.h"可能会首先搜索./file.h,其中.是包含#include语句的文件的目录,而#include <file.h>可能会首先搜索/usr/include/file.h,其中/usr/include是您的系统为标头定义的通常的地方(它似乎不是由POSIX定义的)。
#include "file.h"
./file.h
#include <file.h>
/usr/include/file.h
/usr/include
如果头文件是预定义的,那么您只需将头文件名写在尖括号中,它看起来像这样(假设我们有一个预定义的头文件名iostream):
#include <iostream>
#include " "
如果您(程序员)编写了自己的头文件,那么您将用引号编写头文件名称。因此,假设您编写了一个名为myfile.h的头文件,那么这是如何使用包括指令来包含该文件的示例:
myfile.h
#include "myfile.h"
简单的一般规则是使用有角度的括号来包含编译器附带的头文件。使用双引号来包含任何其他头文件。大多数编译器都是这样做的。
1.9-头文件更详细地解释了预处理器指令。如果你是一个新手程序员,该页面应该可以帮助你理解所有这些。我从这里学到了它,我一直在工作中遵循它。
这里的许多答案都集中在编译器为了找到文件而搜索的路径上。虽然这是大多数编译器所做的,但合格的编译器允许使用标准标头的效果进行预编程,并将#include <list>视为开关,它根本不需要作为文件存在。
#include <list>
这不是纯粹的假设。至少有一个编译器是这样工作的。建议仅在标准标头中使用#include <xxx>。
#include <xxx>
当引用系统文件时使用#include <filename>。这是一个可以在系统默认位置找到的头文件,例如/usr/include或/usr/local/include。对于需要包含在另一个程序中的自己的文件,您必须使用#include "filename"语法。
/usr/local/include
感谢您的精彩回答,特别是Adam Stelmaszczyk和piCookie以及aib。
像许多程序员一样,多年来,我一直使用"myApp.hpp"表单用于应用程序特定文件,<libHeader.hpp>表单用于库和编译器系统文件,即/I和INCLUDE环境变量中指定的文件,认为这是标准。
"myApp.hpp"
<libHeader.hpp>
/I
INCLUDE
然而,C标准规定搜索顺序是特定于实现的,这可能会使可移植性变得复杂。更糟糕的是,我们使用Jam,它会自动找出包含文件的位置。您可以为包含文件使用相对或绝对路径。
#include "../../MyProgDir/SourceDir1/someFile.hpp"
旧版本的MSVS需要双反斜杠(\\),但现在不需要了。我不知道它什么时候改变的。只需使用正斜杠与'nix兼容(Windows将接受这一点)。
如果您担心真的,请使用"./myHeader.h"作为与源代码相同目录中的包含文件(我目前的非常大的项目有一些重复的包含文件名-实际上是一个配置管理问题)。
"./myHeader.h"
这里的MSDN解释复制在这里为您的方便)。
报价形式预处理器按以下顺序搜索包含文件:在与包含#support语句的文件相同的目录中。在当前打开的包含文件的目录中,按相反的顺序它们已打开。搜索从父包含文件的目录开始,继续向上遍历任何祖父母包含文件的目录。沿着每个/I编译器选项指定的路径。沿着INCLUDE环境变量指定的路径。角括号形式预处理器按以下顺序搜索包含文件:沿着每个/I编译器选项指定的路径。当编译发生在命令行上时,沿着INCLUDE环境变量指定的路径。
报价形式
预处理器按以下顺序搜索包含文件:
角括号形式
当您想使用C/C++系统的头文件或编译器库时使用。这些库可以是stdio. h、string. h、ma. h等。
#include "path-to-file/filename"
当您想使用位于项目文件夹或其他位置的自定义头文件时使用。
有关预处理器和标头的更多信息。阅读C-预处理器。
根据标准-是的,它们是不同的:
表单的预处理指令 #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>)。
<stdio.h>
从形式上讲,您必须阅读编译器的手册,但是通常(按照传统)#include "..."表单搜索首先找到#include的文件的目录,然后是#include <...>表单搜索的目录(包含路径,例如系统标头)。
#include "..."
#include <...>
在C++中,以两种方式包含文件:
第一个是#support,它告诉预处理器在预定义的默认位置查找文件。此位置通常是一个INCLUDE环境变量,表示包含文件的路径。
第二种类型是#support“filename”,它告诉预处理器首先在当前目录中查找文件,然后在用户设置的预定义位置中查找它。
GCC留档说以下关于两者的区别:
使用预处理指令#0包含用户和系统头文件。它有两个变体: #include <file> 此变体用于系统头文件。它在标准系统目录列表中搜索名为file的文件。您可以使用-I选项将目录添加到此列表中(请参阅调用)。 #include "file" 此变体用于您自己程序的头文件。它首先在包含当前文件的目录中搜索名为file的文件,然后在引用目录中搜索,然后在用于<file>的相同目录中搜索。您可以使用-iquote选项将目录添加到引用目录列表中。‘#include’的参数,无论是用引号还是尖括号分隔,其行为都像一个字符串常量,因为无法识别注释,并且不会展开宏名称。因此,#include <x/*y>指定包含名为x/*y的系统头文件。 但是,如果反斜杠出现在文件中,则它们被视为普通文本字符,而不是转义字符。不处理适用于C中字符串常量的字符转义序列。因此,#include "x\n\\y"指定包含三个反斜杠的文件名。(一些系统将“\”解释为路径名分隔符。所有这些也以相同的方式解释‘/’。仅使用‘/’最可移植。) 如果文件名之后的行上有任何内容(注释除外),则为错误。
使用预处理指令#0包含用户和系统头文件。它有两个变体:
#include <file> 此变体用于系统头文件。它在标准系统目录列表中搜索名为file的文件。您可以使用-I选项将目录添加到此列表中(请参阅调用)。 #include "file" 此变体用于您自己程序的头文件。它首先在包含当前文件的目录中搜索名为file的文件,然后在引用目录中搜索,然后在用于<file>的相同目录中搜索。您可以使用-iquote选项将目录添加到引用目录列表中。‘#include’的参数,无论是用引号还是尖括号分隔,其行为都像一个字符串常量,因为无法识别注释,并且不会展开宏名称。因此,#include <x/*y>指定包含名为x/*y的系统头文件。 但是,如果反斜杠出现在文件中,则它们被视为普通文本字符,而不是转义字符。不处理适用于C中字符串常量的字符转义序列。因此,#include "x\n\\y"指定包含三个反斜杠的文件名。(一些系统将“\”解释为路径名分隔符。所有这些也以相同的方式解释‘/’。仅使用‘/’最可移植。) 如果文件名之后的行上有任何内容(注释除外),则为错误。
此变体用于系统头文件。它在标准系统目录列表中搜索名为file的文件。您可以使用-I选项将目录添加到此列表中(请参阅调用)。
此变体用于您自己程序的头文件。它首先在包含当前文件的目录中搜索名为file的文件,然后在引用目录中搜索,然后在用于<file>的相同目录中搜索。您可以使用-iquote选项将目录添加到引用目录列表中。‘#include’的参数,无论是用引号还是尖括号分隔,其行为都像一个字符串常量,因为无法识别注释,并且不会展开宏名称。因此,#include <x/*y>指定包含名为x/*y的系统头文件。
-iquote
‘#include’
#include <x/*y>
x/*y
但是,如果反斜杠出现在文件中,则它们被视为普通文本字符,而不是转义字符。不处理适用于C中字符串常量的字符转义序列。因此,#include "x\n\\y"指定包含三个反斜杠的文件名。(一些系统将“\”解释为路径名分隔符。所有这些也以相同的方式解释‘/’。仅使用‘/’最可移植。)
#include "x\n\\y"
‘/’
如果文件名之后的行上有任何内容(注释除外),则为错误。
#include <abc.h>
用于包含标准库文件。因此编译器将签入标准库标头所在的位置。
#include "xyz.h"
将告诉编译器包含用户定义的头文件。因此编译器将检查当前文件夹或-I定义的文件夹中的这些头文件。
预处理器的确切行为因编译器而异。以下答案适用于GCC和其他几个编译器。
#include <file.h>告诉编译器在其“包含”目录中搜索标头,例如,对于MinGW,编译器将在C:\MinGW\包括\或安装编译器的任何地方搜索file.h。
file.h
#include "file"告诉编译器在当前目录(即源文件所在的目录)中搜索file。
file
您可以使用GCC的-I标志来告诉它,当它遇到带有斜角括号的包含时,它还应该在-I之后的目录中搜索标头。GCC将把标志后的目录视为includes目录。
includes
例如,如果您在自己的目录中有一个名为myheader.h的文件,如果您使用标志-I .调用GCC(表示它应该在当前目录中搜索包含),您可以说#include <myheader.h>。
myheader.h
-I .
#include <myheader.h>
如果没有-I标志,则必须使用#include "myheader.h"来包含文件,或将myheader.h移动到编译器的include目录。
#include "myheader.h"
“”在标准C库位置中搜索 而“文件名”也在当前目录中搜索。
“”在标准C库位置中搜索
而“文件名”也在当前目录中搜索。
理想情况下,您将使用<…>作为标准C库,并使用“…”作为您编写并存在于当前目录中的库。
当您使用#include <filename>时,预处理器会在C\C++头文件(stdio. h\cstdio、string、ector等)的目录中查找文件。但是,当您首先使用#include "filename"时,预处理器会在当前目录中查找文件,如果没有,预处理器会在C\C++头文件的目录中查找它。
首先,查找调用指令的当前目录中是否存在头文件。如果没有找到,则在标准系统目录的预配置列表中搜索。
这将查找调用指令的当前目录中是否存在头文件。
确切的搜索目录列表取决于目标系统、GCC的配置方式以及安装位置。您可以通过使用-v选项运行它来找到GCC编译器的搜索目录列表。
您可以使用-Idir将其他目录添加到搜索路径中,这会导致在当前目录之后(对于指令的引号形式)和标准系统目录之前搜索dir。
基本上,表单“xxx”只是在当前目录中搜索;如果没有找到,则返回表单
有两种方法可以编写#support语句。它们是:
#include"filename"#include<filename>
每种形式的含义是
#include"mylib.h"
此命令将查找当前目录中的文件mylib.h以及可能已设置的包含搜索路径中提到的指定目录列表。
mylib.h
#include<mylib.h>
此命令将仅在指定的目录列表中查找文件mylib.h。
包含搜索路径只不过是要搜索包含文件的目录列表。不同的C编译器允许您以不同的方式设置搜索路径。
要使用gcc查看系统上的搜索顺序,根据当前配置,您可以执行以下命令。您可以在此命令这里中找到更多详细信息
cpp -v /dev/null -o /dev/null
苹果LLVM版本10.0.0(clang-1000.10.44.2)目标:x86_64-appe-darwin18.0.0线程模型:posx InstalledDir: Library/Developer/Command dLineTools/usr/bin"/Library/Developer/Command dLineTools/usr/bin/clang"-cc1x86_64使用方法使用-E-disable-free-disable-llvm编译器-校验器-丢弃值-名称-主文件名null-mreporting-model pic-pic级别2-mthread-模型位置-mdisable-fp-elim-fno严格返回-masm详细-munose-munwin-table-目标cpu penryn-dwarf-列信息-调试器-调整=lldb-目标链接器版本409.12-v-resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/10.0.0-isysroot/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk-我/usr/本地/包括-fdebug编译目录 /Users/hogstrom-ferror限制19-f消息长度80-堆栈保护1-fblock-fencode扩展块签名-fobic-runtime=macosx-10.14.0-fmax-type-的对齐=16-f诊断-显示-选项-fColor-诊断-传统-cpp-o-x c /dev/nullclang-cc1 version 10.0.0(clang-1000.10.44.2)默认目标x86_64不存在目录"/Library/Developer/Command dLineTools/SDKs/MacOSX10.14.sdk/usr/local/include"忽略不存在的目录"/Library/Developer/Command dLineTools/SDKs/MacOSX10.14.sdk/Library/Frameworks"#包括“…”搜索从这里开始:搜索从这里开始:/usr/local/include/Library/Developer/CommandLineTools/usr/lib/clang/10.0.0/include/Library/Developer/CommandLineTools/usr/include/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/System/Library/Frameworks(框架目录)搜索列表结束。
#0
包含默认包含目录所在的文件。
在编译它的当前目录中包含一个文件。双引号也可以指定到不同位置的完整文件路径。
一般来说,区别在于预处理器搜索头文件的位置:
#包含是包含头文件的预处理器指令。两个#包含都用于在程序中添加或包含头文件,但首先是包含系统头文件,然后是包含用户定义的头文件。
查看gcc文档gcc包含文件
""将首先搜索./。然后搜索默认包含路径。您可以使用这样的命令打印默认的包含路径:
""
gcc -v -o a a.c
以下是一些例子,以使事情更清楚:代码a. c有效
// a.c#include "stdio.h"int main() {int a = 3;printf("a = %d\n", a);return 0; }
B. C的代码也有效
// b.c#include <stdio.h>int main() {int a = 3;printf("a = %d\n", a);return 0; }
但是当我在当前目录中创建一个名为stdio.h的新文件时
stdio.h
// stdio.hinline int foo(){return 10;}
a.c会产生编译错误,但b.c仍然有效
a.c
b.c
和"",<>可以与相同的文件名一起使用。因为搜索路径优先级不同。所以d.c也有效
d.c
// d.c#include <stdio.h>#include "stdio.h"int main(){int a = 0; a = foo(); printf("a=%d\n", a); return 0;}
编译器生成的实现定义警告可以(并且将)以不同于程序库的方式处理系统库。
所以
#include <myFilename>
-实际上声明myFilename位于系统库位置-很可能(并且可能会)隐藏死代码和未使用的变量警告等,当您使用时会显示:
#include "myFilename"