考虑到危险,为什么项目要使用 -I include 开关?

Why do projects use the -I include switch given the dangers?

阅读 GCC 中 -I 开关的细则,我很震惊地发现在命令行上使用它会覆盖系统包含。来自 preprocessor docs

"You can use -I to override a system header file, substituting your own version, since these directories are searched before the standard system header file directories."

他们好像没有说谎。在带有 GCC 7 的两个不同 Ubuntu 系统上,如果我创建一个文件 endian.h:

#error "This endian.h shouldn't be included"

...然后在同一目录下创建一个main.cpp(或main.c,区别相同):

#include <stdlib.h>
int main() {}

然后用g++ main.cpp -I. -o main(或clang,相同的区别)编译给我:

In file included from /usr/include/x86_64-linux-gnu/sys/types.h:194:0,
                 from /usr/include/stdlib.h:394,
                 from /usr/include/c++/7/cstdlib:75,
                 from /usr/include/c++/7/stdlib.h:36,
                 from main.cpp:1:
./endian.h:1:2: error: #error "This endian.h shouldn't be included"

所以 stdlib.h 包括这个 types.h 文件,它在第 194 行只是说 #include <endian.h>。我明显的误解(也许其他人的误解)是尖括号会阻止这种情况,但是 -I 比我想象的要强大。

虽然不够强,因为你甚至无法通过先在命令行上粘贴 /usr/include 来修复它,因为:

"If a standard system include directory, or a directory specified with -isystem, is also specified with -I, the -I option is ignored. The directory is still searched but as a system directory at its normal position in the system include chain."

确实,g++ -v main.cpp -I/usr/include -I. -o main 的详细输出将 /usr/include 留在列表底部:

#include "..." search starts here:
#include <...> search starts here:
 .
 /usr/include/c++/7
 /usr/include/x86_64-linux-gnu/c++/7
 /usr/include/c++/7/backward
 /usr/lib/gcc/x86_64-linux-gnu/7/include
 /usr/local/include
 /usr/lib/gcc/x86_64-linux-gnu/7/include-fixed
 /usr/include/x86_64-linux-gnu
 /usr/include

让我感到惊讶。我想提出这个问题:

考虑到这个极其严重的问题,大多数项目使用 -I 的正当理由是什么? 您可以根据偶然情况在系统上覆盖任意 headers名称冲突。几乎每个人不应该改用 -iquote 吗?

-I 超过 -iquote 有什么正当理由? -I 是标准化的(至少 POSIX 是),而 -iquote 不是。 (实际上,我使用 -I 因为 tinycc(我希望我的项目编译的编译器之一)不支持 -iquote。)

考虑到危险,项目如何管理 -I?您将包含包含在目录中并使用 -I 添加包含该目录的目录。

  • 文件系统:includes/mylib/endian.h
  • 命令行:-Iincludes
  • C/C++ 文件:#include "mylib/endian.h" //or <mylib/endian.h>

因此,只要不在 mylib 名称上发生冲突,就不会发生冲突(至少就 header 名称而言)。

回顾 GCC 手册,看起来 -iquote 和其他选项仅在 GCC 4 中添加:https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory-Options.html#Directory%20Options

所以 "-I" 的使用可能是以下因素的结合:习惯、懒惰、向后兼容性、对新选项的无知、与其他编译器的兼容性。

解决方案是 "namespace" 您的 header 文件,将它们放在子目录中。例如,将字节序 header 放在 "include/mylib/endian.h" 中,然后将 "-Iinclude" 添加到命令行,这样就可以 #include "mylib/endian.h" ,这不应与其他库或系统库冲突。

一个明显的例子是cross-compilation。 GCC 有点受历史 UNIX 假设的影响,即您总是为本地系统或至少是非常接近的系统进行编译。这就是编译器的 header 文件位于系统根目录中的原因。缺少干净的界面。

相比之下,Windows 假定没有编译器,并且 Windows 编译器不假定您的目标是本地系统。这就是为什么您可以安装一组编译器和一组 SDK。

现在 cross-compilation,GCC 的行为更像 Windows 的编译器。它不再假定您打算使用本地系统 headers,而是让您准确指定所需的 headers。显然,您 link 在其中的库也是如此。

现在请注意,当您执行此操作时,替换集 headers 旨在继续基本系统的 top。如果它们的实现相同,您可以在替换集中省略 headers。例如。 <complex.h> 很可能是相同的。复数实现没有太多变化。但是,您不能随意替换 <endian.h> 等内部实现位。

TL,DR :此选项适用于知道自己在做什么的人。 "Being unsafe" 不是针对目标受众的论点。

我认为你认为 -I 很危险的前提是错误的。该语言通过充分实现定义的 #include 形式来搜索头文件,因此使用与标准头文件名称冲突的头文件是不安全的。简单地避免这样做。