43

这是同一预处理指令的多个问题。

1 - <> 或 "" ?

除了在 MSDN 中找到的信息:

#include 指令 (C-C++)

1.a:这两种符号有什么区别?
1.b:所有编译器都以相同的方式实现它们吗?
1.c:您什么时候使用<>,什么时候使用“”(即,您使用其中一个或另一个作为标题包含的标准是什么)?

2 - #include {TheProject/TheHeader.hpp} 或 {TheHeader.hpp} ?

我已经看到至少有两种写作方式包括一个项目的标题。考虑到您至少有 4 种类型的标头,即:

  • 您项目的私有标头?
  • 项目的标头,但正在导出符号(因此,“公共”)
  • 您的模块链接的另一个项目的标题
  • 编译器或标准库的头文件

对于每种标题:

2.a:你会使用 <> 还是 "" ?
2.b:您会包含在 {TheProject/TheHeader.hpp} 中,还是仅包含在 {TheHeader.hpp} 中?

3 - 奖金

3.a:您是否在树状组织中使用源代码和/或标头进行项目(即目录中的目录,而不是“一个目录中的每个文件”),优点/缺点是什么?

4

10 回答 10

35

在阅读了所有答案以及编译器文档后,我决定遵循以下标准。

对于所有文件,无论是项目标头还是外部标头,始终使用以下模式:

#include <namespace/header.hpp>

命名空间至少有一个目录深,以避免冲突。

当然,这意味着项目头所在的项目目录也应该作为“默认包含头”添加到makefile中。

之所以选择这个,是因为我找到了以下信息:

1. include "" 模式依赖于编译器

我会在下面给出答案

1.a 标准

资源:

在第 16.2 节源文件包含中,我们可以读到:

形式的预处理指令

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

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

这意味着 #include <...> 将以实现定义的方式搜索文件。

然后,下一段:

形式的预处理指令

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

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

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

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

这意味着 #include "..." 将以实现定义的方式搜索文件,然后,如果未找到该文件,将进行另一次搜索,就好像它是一个 #include <...>

结论是我们必须阅读编译器文档。

请注意,由于某种原因,标准中的“系统”或“库”标头或其他标头之间没有任何区别。唯一的区别似乎是 #include <...> 似乎针对标头,而 #include "..." 似乎针对源(至少,在英文措辞中)。

1.b 视觉 C++:

资源:

#include "我的文件.hpp"

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

  1. 在与包含#include 语句的文件相同的目录中。
  2. 在任何先前打开的包含文件的目录中,以打开它们的相反顺序。搜索从最后打开的包含文件的目录开始,一直到最先打开的包含文件的目录。
  3. 沿着每个 /I 编译器选项指定的路径。
  4. (*) 沿着 INCLUDE 环境变量指定的路径或开发环境默认包含的路径。

#include <我的文件.hpp>

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

  1. 沿着每个 /I 编译器选项指定的路径。
  2. (*) 沿着 INCLUDE 环境变量指定的路径或开发环境默认包含的路径。

注意最后一步

该文档并不清楚两者的“沿包含环境变量指定的路径”部分<...>"..."包含。以下引用使其符合标准:

对于指定为#include "path-spec" 的包含文件,目录搜索从父文件的目录开始,然后通过任何祖父文件的目录进行。也就是说,搜索相对于包含正在处理的#include 指令的源文件的目录开始。如果没有祖父文件且未找到该文件,则继续搜索,就像文件名包含在尖括号中一样。

因此,最后一步(用星号标记)是阅读整个文档的解释。

1.c g++

资源:

以下引用总结了该过程:

<file>GCC [...] 将在 [系统目录] [...] 中查找使用 #include 请求的标头

GCC 首先在包含当前文件的目录中查找使用 #include "file" 请求的头文件,然后在 -iquote 选项指定的目录中查找,然后在相同的位置查找使用尖括号请求的头文件。

#include "我的文件.hpp"

此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

  1. 在与包含#include 语句的文件相同的目录中。
  2. 沿着每个 -iquote 编译器选项指定的路径。
  3. 至于#include<MyFile.hpp>

#include <我的文件.hpp>

此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

  1. 沿着每个 -I 编译器选项指定的路径。
  2. 在系统目录里面。

1.d 甲骨文/Sun Studio CC

资源:

请注意,文本有些自相矛盾(请参阅示例以理解)。关键短语是:“区别在于当前目录只搜索名称用引号括起来的头文件。

#include "我的文件.hpp"

此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

  1. 当前目录(即包含“包含”文件的目录)
  2. 使用 -I 选项命名的目录(如果有)
  3. 系统目录(例如 /usr/include 目录)

#include <我的文件.hpp>

此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

  1. 使用 -I 选项命名的目录(如果有)
  2. 系统目录(例如 /usr/include 目录)

1.e XL C/C++ 编译器参考 - IBM/AIX

资源:

两个文档的标题为“XL C/C++ Compiler Reference” 第一个文档较旧 (8.0),但更易于理解。第二个是较新的(12.1),但更难解密。

#include "我的文件.hpp"

此变体用于您自己程序的头文件。预处理器按以下顺序搜索包含文件:

  1. 当前目录(即包含“包含”文件的目录)
  2. 使用 -I 选项命名的目录(如果有)
  3. 系统目录(例如 /usr/vac[cpp]/include 或 /usr/include 目录)

#include <我的文件.hpp>

此变体用于系统头文件。预处理器按以下顺序搜索包含文件:

  1. 使用 -I 选项命名的目录(如果有)
  2. 系统目录(例如 /usr/vac[cpp]/include 或 /usr/include 目录)

1.e 结论

模式 "" 可能会导致编译器之间出现细微的编译错误,并且由于我目前在 Windows Visual C++、Linux g++、Oracle/Solaris CC 和 AIX XL 上工作,因此这是不可接受的。

无论如何,“”描述的功能的优势无论如何都不是有趣的,所以......

2. 使用 {namespace}/header.hpp 模式

我在工作中看到(即这不是理论,这是现实生活中痛苦的职业经历)两个同名的标题,一个在本地项目目录中,另一个在全局包含中。

由于我们使用的是 "" 模式,并且该文件同时包含在本地头文件和全局头文件中,当出现奇怪的错误时,我们无法理解到底发生了什么。

使用包含中的目录可以节省我们的时间,因为用户必须写:

#include <MyLocalProject/Header.hpp>

或者

#include <GlobalInclude/Header.hpp>

你会注意到,虽然

#include "Header.hpp"

会成功编译,因此,仍然隐藏问题,而

#include <Header.hpp>

在正常情况下不会编译。

因此,坚持 <> 符号将强制开发人员使用正确的目录作为包含前缀,这是首选 <> 而不是“”的另一个原因。

三、结论

同时使用 <> 表示法和命名空间表示法从预编译器中消除了猜测文件的可能性,而不是只搜索默认的包含目录。

当然,标准库仍然照常包含,即:

#include <cstdlib>
#include <vector>
于 2009-08-09T12:37:50.090 回答
7

我通常将 <> 用于系统标头,将 "" 用于项目标头。至于路径,仅当您想要的文件位于包含路径的子目录中时才需要这样做。

例如,如果您需要 /usr/include/SDL/ 中的文件,但只有 /usr/include/ 在您的包含路径中,那么您可以使用:

#include <SDL/whatever.h>

另外,请记住,除非您放置的路径以 / 开头,否则它是相对于当前工作目录的。

编辑回答评论:这取决于,如果一个库只有几个包含,我只会在包含路径中包含它的子目录,但如果库有很多标题(比如几十个),那么我宁愿只拥有它在我指定的子目录中。Linux 的系统头文件就是一个很好的例子。你像这样使用它们:

#include <sys/io.h>
#include <linux/limits.h>

等等

编辑以包含另一个好答案:另外,如果可以想象两个或多个库提供相同名称的标头,那么子目录解决方案基本上为每个标头提供了一个名称空间。

于 2008-10-07T16:15:20.680 回答
5

引用 C99 标准(乍一看,C90 标准中的措辞似乎是相同的,但我不能从中剪切粘贴):

形式的预处理指令

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

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

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

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

所以搜索到的位置是搜索到的位置#include "whatever"的超集#include <whatever>。目的是第一种样式将用于通常“属于”您的标题,而第二种方法将用于“属于”编译器/环境的标题。当然,通常有一些灰色区域 - 例如,您应该将哪个区域用于 Boost 标头?我会使用#include <>,但如果我团队中的其他人想要,我不会争论太多#include ""

在实践中,只要构建不中断,我认为没有人会过多关注使用哪种形式。我当然不记得它曾在代码审查(或其他方式)中提到过。

于 2008-10-07T17:01:56.670 回答
3

我将解决您问题的第二部分:

我通常<project/libHeader.h>在包含来自第 3 方的标头时使用。当在"myHeader.h"项目中包含标题时。

我使用<project/libHeader.h>代替的原因<libHeader.h>是因为可能不止一个库有一个“libHeader.h”文件。为了将它们都包含在内,您需要将库名称作为包含文件名的一部分。

于 2008-10-07T16:17:33.687 回答
2

1.a:这两种符号有什么区别?

"" 开始在 C/C++ 文件所在的目录中搜索。<> 在 -I 目录和默认位置(例如 /usr/include)中开始搜索。两者最终都搜索到相同的一组位置,只是顺序不同。

1.b:所有编译器都以相同的方式实现它们吗?

我希望如此,但我不确定。

1.c:您什么时候使用<>,什么时候使用“”(即,您使用其中一个或另一个作为标题包含的标准是什么)?

当包含文件应该在 C 文件旁边时,我使用 "",在所有其他情况下使用 <>。特别是,在我们的项目中,所有“公共”包含文件都在 project/include 目录中,所以我对它们使用 <>。

2 - #include {TheProject/TheHeader.hpp} 或 {TheHeader.hpp} ?

正如已经指出的, xxx/filename.h 允许您执行诸如 diskio/ErrorCodes.h 和 netio/ErrorCodes.h 之类的操作

* 您项目的私有标头?

我的子系统在项目中的私有标头。在项目中使用我的子系统的“filename.h”公共标头(在项目外不可见,但其他子系统可以访问)。使用 或 ,具体取决于适用于项目的约定。我宁愿用

* 项目的标头,但它们正在导出符号(因此,“公共”)

include 就像你的图书馆的用户会包括他们一样。大概

*您的模块链接的另一个项目的标题

由项目决定,但肯定使用编译器或标准库的 <> * 头文件 绝对 <>,根据标准。

3.a:您是否在树状组织中使用源代码和/或标头进行项目(即目录中的目录,而不是“一个目录中的每个文件”),优点/缺点是什么?

我确实在一个结构化的项目上工作。一旦你有超过分数的文件,一些划分就会变得明显。你应该走代码拉你的路。

于 2008-10-07T17:55:20.510 回答
1

如果我没记错的话。

您将菱形用于可以在“路径”中找到的所有库。因此,STL 中的任何库或您已安装的库。在 Linux 中,您的路径通常是“/usr/include”,在 Windows 中我不确定,但我猜它在“C:\windows”下。

然后使用“”来指定其他所有内容。没有起始目录信息的“my_bla.cpp”将解析为您的代码所在/编译的目录。或者您也可以使用它指定包含的确切位置。像这样“c:\myproj\some_code.cpp”

标题的类型无关紧要,只是位置。

于 2008-10-07T16:14:06.753 回答
1

<>和之间有两个主要区别""。第一个是哪个字符将结束名称 - 标题名称中没有转义序列,因此您可能被迫使用#include <bla"file.cpp>or "bla>file.cpp"。不过,这可能不会经常出现。另一个区别是系统包含不应该发生在"",只是<>。所以#include "iostream"不能保证工作;#include <iostream>是。我个人的偏好是用于""作为项目一部分的<>文件,以及不用于项目的文件。有些人仅<>用于标准库头文件和""其他所有内容。有些人甚至<>只用于 Boost 和 std;这取决于项目。像所有风格方面一样,最重要的是保持一致。

至于路径,外部库将指定标题的约定;例如<boost/preprocessor.hpp> <wx/window.h> <Magic++.h>。在本地项目中,我将编写相对于顶级 srcdir 的所有路径(或者在它们不同的库项目中,包括目录)。

在编写库时,您可能还会发现使用 <> 来区分私有和公共头文件很有帮助,或者不是-I源目录而是上面的目录,因此您#include "public_header.hpp""src/private_header.hpp". 这真的取决于你。

编辑:至于具有目录结构的项目,我强烈推荐它们。想象一下,如果所有 boost 都在一个目录中(并且没有子命名空间)!目录结构很好,因为它可以让您更轻松地找到文件,并且可以让您在命名方面更加灵活("module\_text\_processor.hpp"而不是"module/text\_processor.hpp")。后者更自然,更易于使用。

于 2008-10-07T16:17:14.360 回答
1

重新 <> 与“”。在我的店里,就“风格”问题而言,我非常不干涉。我需要的少数几个领域之一是在#include 语句中使用尖括号——规则是:如果您要#include 操作系统或编译器文件,则可以在适当的情况下使用尖括号。在所有其他情况下,它们是被禁止的。如果您要#include 此处某人或第 3 方库编写的文件,则禁止使用 <>。

原因是:#include "xh" 和 #include 不搜索相同的路径。#include 将仅搜索系统路径以及您在 -i 中添加的任何内容。重要的是,如果该目录未以其他方式包含在搜索路径中,它将不会搜索文件 xh 所在的路径。

例如,假设您有以下文件:

c:\dev\angles\main.cpp

#include "c:\utils\mylib\mylibrary.h"

int main()
{
    return 0;
}

c:\utils\mylib\mylibrary.h

#ifndef MYLIB_H
#define MYLIB_H

#include <speech.h>

namespace mylib
{
    void Speak(SpeechType speechType);  
};

#endif

c:\utils\mhlib\speech.h

#ifndef SPEECH_H
#define SPEECH_H

namespace mylib
{
    enum SpeechType {Bark, Growl};
};

#endif

如果不通过设置 PATH 环境变量或 -i'ing 在 c:\utils\mhlib\ 目录中更改路径,这将无法编译。即使#include <speech.h>该文件与mylibrary.h!

我们在代码中的#include 语句中广泛使用相对路径名和绝对路径名,原因有二。

1) 通过将库和组件保持在主源代码树之外(即,将实用程序库放在一个特殊的目录中),我们不会将库的生命周期与应用程序的生命周期耦合。当您有几个使用公共库的不同产品时,这一点尤其重要。

2)我们使用Junctions将硬盘上的物理位置映射到逻辑驱动器上的目录,然后在所有#includes 中使用逻辑驱动器上的完全限定路径。例如:

#include "x:\utils\mylib.h"-- 很好,x: 是替代驱动器,x:\utils 指向硬盘驱动器上的 c:\code\utils_1.0

#include "c:\utils_1.0\mylib.h" - 坏的!t#includes mylib.h 的应用程序现在与特定版本的 MYLIB 库耦合,所有开发人员都必须将它放在硬盘驱动器上的同一目录中,c:\utils_1.0

最后,我团队的一个广泛但难以实现的目标是能够支持一键编译。这包括能够通过从源代码控制获取代码然后点击“编译”来编译主源代码树。特别是,我讨厌必须设置路径和机器范围的#include 目录才能进行编译,因为您在构建开发机器的设置阶段添加的每一个小额外步骤只会让它变得更难、更容易搞砸起来,让新机器加速和生成代码需要更长的时间。

于 2008-10-07T18:38:08.163 回答
0

我使用系统头文件(stdio、iostreams、字符串等)中的 <...> ,并为特定于该项目的头文件使用“...”。

于 2008-10-07T16:15:29.993 回答
0

我们将#include "header.h" 用于项目本地的标头,将#include 用于系统包含、第三方包含和解决方案中的其他项目。我们使用Visual Studio,在头文件include中使用项目目录要容易得多,这样每当我们创建一个新项目时,我们只需要指定包含所有项目目录的目录的包含路径,而不是单独的路径每个项目。

于 2008-10-07T17:39:10.293 回答