在 C 和 C++ 编程语言中,在语句中使用尖括号和使用引号有什么区别include
,如下所示?
#include <filename>
#include "filename"
在 C 和 C++ 编程语言中,在语句中使用尖括号和使用引号有什么区别include
,如下所示?
#include <filename>
#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 "file"
表单,则实现首先查找给定名称的文件(如果支持)。如果不(支持),或者如果搜索失败,则实现的行为就像使用了其他 ( #include <file>
) 形式一样。
此外,存在第三种形式,当#include
指令与上述任何一种形式都不匹配时使用。在这种形式中,对指令的“操作数”进行了一些基本的预处理(例如宏扩展)#include
,并且预期结果与其他两种形式中的一种相匹配。
这里的一些好的答案引用了 C 标准,但忘记了 POSIX 标准,尤其是c99(例如 C 编译器)命令的特定行为。
根据开放组基本规范第 7 期,
-I 目录
将搜索名称不是绝对路径名的标头的算法更改为先在目录路径名命名的目录中查找,然后再查找通常的位置。因此,名称包含在双引号 ( "" ) 中的标题应首先在带有#include行的文件目录中搜索,然后在-I选项命名的目录中搜索,最后在通常的位置搜索。对于名称包含在尖括号 ( "<>" ) 中的标头,应仅在以-I选项命名的目录中搜索标头,然后在通常的位置进行搜索。-I选项中命名的目录应按指定的顺序进行搜索。c99命令调用。
因此,在符合 POSIX 的环境中,使用符合 POSIX 的 C 编译器,#include "file.h"
可能会首先搜索带有语句的文件./file.h
所在.
的目录,而, 可能会首先搜索,您的系统定义在哪里标题的常用位置(POSIX 似乎没有定义)。#include
#include <file.h>
/usr/include/file.h
/usr/include
预处理器的确切行为因编译器而异。以下答案适用于 GCC 和其他几个编译器。
#include <file.h>
告诉编译器在其“includes”目录中搜索头文件,例如对于 MinGW,编译器将file.h
在 C:\MinGW\include\ 或安装编译器的任何位置搜索。
#include "file"
告诉编译器在当前目录(即源文件所在的目录)中搜索file
.
您可以使用-I
GCC 的标志告诉它,当它遇到带尖括号的包含时,它还应该在-I
. GCC 会将标志后面的目录视为includes
目录。
例如,如果您myheader.h
在自己的目录中调用了一个文件,您可以说#include <myheader.h>
是否使用标志调用 GCC -I .
(表明它应该在当前目录中搜索包含。)
如果没有该-I
标志,您将不得不使用#include "myheader.h"
来包含文件,或者移动myheader.h
到include
编译器的目录。
它确实:
"mypath/myfile" is short for ./mypath/myfile
是包含.
的文件的目录#include
,和/或编译器的当前工作目录,和/或default_include_paths
和
<mypath/myfile> is short for <defaultincludepaths>/mypath/myfile
如果./
是 in <default_include_paths>
,那么它没有任何区别。
如果mypath/myfile
在另一个包含目录中,则行为未定义。
<file>
include 告诉预处理器首先在目录和预定义目录中搜索,-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 <...>
表单搜索的目录(包含路径,例如系统头文件)。
至少对于 GCC 版本 <= 3.0,尖括号形式不会在包含文件和包含文件之间生成依赖关系。
因此,如果要生成依赖关系规则(例如使用 GCC -M 选项),则必须对应包含在依赖关系树中的文件使用带引号的形式。
感谢您的精彩回答,尤其是。Adam Stelmaszczyk 和 piCookie 和 aib。
"myApp.hpp"
像许多程序员一样,多年来我一直认为应用程序特定文件的<libHeader.hpp>
格式以及库和编译器系统文件(即在/I
和环境变量中指定的文件)的格式是非正式的约定,INCLUDE
多年来我一直认为这是标准。
但是,C 标准规定搜索顺序是特定于实现的,这会使可移植性变得复杂。更糟糕的是,我们使用 jam,它会自动找出包含文件的位置。您可以对包含文件使用相对或绝对路径。IE
#include "../../MyProgDir/SourceDir1/someFile.hpp"
旧版本的 MSVS 需要双反斜杠 (\\),但现在不需要了。不知道什么时候变了 只需使用正斜杠来与 'nix 兼容(Windows 会接受)。
如果您真的担心它,请使用"./myHeader.h"
与源代码位于同一目录中的包含文件(我当前的非常大的项目有一些重复的包含文件名分散在各处 - 这确实是一个配置管理问题)。
为了方便起见,这里复制了MSDN 说明)。
报价单
预处理器按以下顺序搜索包含文件:
- 在与包含#include 语句的文件相同的目录中。
- 在当前打开的包含文件的目录中,按照打开
它们的相反顺序。搜索从父包含文件的目录开始,并
继续向上通过任何祖父包含文件的目录。/I
沿着每个编译器选项指定的路径。- 沿着
INCLUDE
环境变量指定的路径。尖括号形式
预处理器按以下顺序搜索包含文件:
/I
沿着每个编译器选项指定的路径。- 在命令行上进行编译时,沿
INCLUDE
环境变量指定的路径进行。
对于#include ""
编译器,通常搜索包含该包含的文件的文件夹,然后搜索其他文件夹。因为#include <>
编译器不搜索当前文件的文件夹。
当您使用#include <filename> 时,预处理器会在 C\C++ 头文件(stdio.h\cstdio、字符串、向量等)的目录中查找文件。但是,当您使用 #include "filename" 时:首先,预处理器会在当前目录中查找文件,如果它不在这里 - 他会在 C\C++ 头文件的目录中查找它。
带有尖括号的#include 将搜索要包含的文件的“依赖于实现的位置列表”(这是一种非常复杂的“系统头文件”的说法)。
带引号的#include 只会搜索文件(并且,“以实现依赖的方式”,bleh)。这意味着,在普通的英语中,它会尝试应用你扔给它的路径/文件名,并且不会预先设置系统路径或篡改它。
此外,如果 #include "" 失败,则按照标准将其重新读取为 #include <>。
gcc 文档有一个(特定于编译器的)描述,虽然它是特定于 gcc 而不是标准,但比 ISO 标准的律师式谈话更容易理解。
#include <>
用于预定义的头文件如果头文件是预定义的,那么您只需将头文件名称写在尖括号中,它看起来像这样(假设我们有一个预定义的头文件名称 iostream):
#include <iostream>
#include " "
用于程序员定义的头文件如果您(程序员)编写了自己的头文件,那么您将在引号中写入头文件名。因此,假设您编写了一个名为 的头文件myfile.h
,那么这是一个如何使用 include 指令包含该文件的示例:
#include "myfile.h"
这里的许多答案都集中在编译器为了找到文件而搜索的路径上。虽然这是大多数编译器所做的,但允许符合标准的编译器使用标准头文件的效果进行预编程,并将其#include <list>
视为一个开关,它根本不需要作为文件存在。
这不是纯粹的假设。至少有一个编译器以这种方式工作。#include <xxx>
建议仅使用标准标题。
#include "filename" // User defined header
#include <filename> // Standard library header.
例子:
这里的文件名是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
),现在应该包含用户定义的标头,如下所示:
#include "Seller.h"
#include <abc.h>
用于包含标准库文件。因此编译器将检查标准库头文件所在的位置。
#include "xyz.h"
将告诉编译器包含用户定义的头文件。-I
所以编译器会在当前文件夹或定义的文件夹中检查这些头文件。
在 C++ 中,通过两种方式包含文件:
第一个是#include,它告诉预处理器在预定义的默认位置查找文件。此位置通常是一个 INCLUDE 环境变量,表示包含文件的路径。
第二种类型是#include "filename",它告诉预处理器首先在当前目录中查找文件,然后在用户设置的预定义位置中查找它。
#include <filename>
引用系统文件时使用。这是一个头文件,可以在系统默认位置找到,例如/usr/include
或/usr/local/include
。对于需要包含在另一个程序中的您自己的文件,您必须使用#include "filename"
语法。
首先,在调用指令的当前目录中查找头文件的存在。如果未找到,则在标准系统目录的预配置列表中进行搜索。
这会在调用指令的当前目录中查找头文件的存在。
确切的搜索目录列表取决于目标系统、GCC 的配置方式以及安装位置。您可以通过使用 -v 选项运行 GCC 编译器来找到搜索目录列表。
您可以使用 -I dir将其他目录添加到搜索路径,这会导致在当前目录之后(对于指令的引用形式)和标准系统目录之前搜索 dir。
基本上,“xxx”形式只是在当前目录中搜索;如果没有找到回退表格
#include <filename>
当您要使用 C/C++ 系统或编译器库的头文件时使用。这些库可以是 stdio.h、string.h、math.h 等。
#include "path-to-file/filename"
当您想使用您自己的自定义头文件时使用,该文件位于您的项目文件夹或其他地方。
有关预处理器和标头的更多信息。阅读C 预处理器。
简单的一般规则是使用尖括号来包含编译器附带的头文件。使用双引号包含任何其他头文件。大多数编译器都是这样做的。
1.9 — 头文件更详细地解释了预处理器指令。如果您是新手程序员,该页面应该可以帮助您理解所有这些。我是从这里学到的,我在工作中一直在关注它。
#include <filename>
#include "filename"
#include <filename>
并在系统头文件存储的位置搜索该头文件。#include <filename>
.“<filename>”在标准 C 库位置中搜索
而“文件名”也在当前目录中搜索。
理想情况下,您可以将 <...> 用于标准 C 库,将“...”用于您编写并存在于当前目录中的库。
一般来说,区别在于预处理器在哪里搜索头文件:
#include 是包含头文件的预处理器指令。#include 都用于在程序中添加或包含头文件,但首先是包含系统头文件,然后是用户定义的头文件。
检查 gcc 文档gcc 包含文件
""
会./
先搜索。然后搜索默认包含路径。您可以使用这样的命令来打印默认包含路径:
gcc -v -o a a.c
这里有一些例子可以让事情更清楚:代码 ac 有效
// a.c
#include "stdio.h"
int main() {
int a = 3;
printf("a = %d\n", a);
return 0;
}
bc 的代码也有效
// b.c
#include <stdio.h>
int main() {
int a = 3;
printf("a = %d\n", a);
return 0;
}
stdio.h
但是当我在当前目录中创建一个名为的新文件时
// stdio.h
inline int foo()
{
return 10;
}
a.c
会产生编译错误,但b.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 <file>
包含默认包含目录所在的文件。
#include "file"
在编译它的当前目录中包含一个文件。双引号也可以指定到不同位置的完整文件路径。
要使用 gcc 在您的系统上查看搜索顺序,基于当前配置,您可以执行以下命令。您可以在此处找到有关此命令的更多详细信息
cpp -v /dev/null -o /dev/null
Apple LLVM 版本 10.0.0 (clang-1000.10.44.2)
目标:x86_64-apple-darwin18.0.0
线程模型:posix InstalledDir:Library/Developer/CommandLineTools/usr/bin
"/Library/Developer/CommandLineTools/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.14.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -E -disable-free - disable-llvm-verifier -discard-value-names -main-file-name null -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fno-strict-return -masm-verbose - munwind-tables -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 409.12 -v -resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/10.0.0 - isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk -I/usr/local/include -fdebug-compilation-dir /Users/hogstrom -ferror-limit 19 -fmessage-length 80 -stack-protector 1 -fblocks -fencode-extended-block-signature -fobjc-runtime=macosx-10.14。0 -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -traditional-cpp -o - -xc /dev/null
clang -cc1 版本 10.0.0 (clang-1000.10.44.2) 默认目标 x86_64-apple-darwin18.0.0 忽略不存在的目录 "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/local/include" 忽略不存在目录 "/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/Library/Frameworks"
#include "..." 搜索从这里开始:
#include <...> 搜索从这里开始:
/usr/local/include
/库/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(框架目录)
搜索列表结束。
编译器生成的实现定义的警告可以(并且将)以不同于程序库的方式对待系统库。
所以
#include <myFilename>
- 实际上声明 myFilename 位于系统库位置 - 很可能(并且可能会)隐藏死代码和未使用的变量警告等,当您使用时会显示:
#include "myFilename"
有两种编写#include 语句的方法。它们是:
#include"filename"
#include<filename>
每种形式的含义是
#include"mylib.h"
该命令将mylib.h
在当前目录中查找文件以及在可能已设置的包含搜索路径中提到的指定目录列表。
#include<mylib.h>
此命令将mylib.h
仅在指定的目录列表中查找文件。
包含搜索路径只不过是一个目录列表,用于搜索被包含的文件。不同的 C 编译器允许您以不同的方式设置搜索路径。