为什么项目使用-I include开关给出危险?

Hos*_*ork 32 c c++ gcc clang include-path

-I在GCC中读取开关的精细打印,我很震惊地发现在命令行上使用它会覆盖系统包含.来自预处理器文档

"您可以使用-I覆盖系统头文件,替换您自己的版本,因为这些目录是在标准系统头文件目录之前搜索的."

他们似乎并不撒谎.在具有GCC 7的两个不同的Ubuntu系统上,如果我创建一个文件endian.h:

#error "This endian.h shouldn't be included"
Run Code Online (Sandbox Code Playgroud)

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

#include <stdlib.h>
int main() {}
Run Code Online (Sandbox Code Playgroud)

然后编译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"
Run Code Online (Sandbox Code Playgroud)

所以stdlib.h包含了这个types.h文件,在194行就是这么说的#include <endian.h>.我明显的误解(也许是其他人的误解)是尖括号会阻止这一点,但是 - 我比我想象的更强.

虽然不强足够在命令行上,因为你甚至不能通过症结修复/ usr/include目录在第一位,这是因为:

"如果标准系统包括目录或指定的目录-isystem,也指定了-I,-I则忽略该选项.目录仍然被搜索,但作为系统目录在系统包含链中的正常位置."

实际上,g++ -v main.cpp -I/usr/include -I. -o mainleaves/usr/的详细输出包含在列表的底部:

#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
Run Code Online (Sandbox Code Playgroud)

让我惊讶的颜色.我想这是一个问题:

-I考虑到这个极其严重的问题,大多数项目使用的正当理由是什么? 您可以根据偶然名称冲突覆盖系统上的任意标头.不应该每个人都使用-iquote相反?

PSk*_*cik 33

有什么正当的理由有没有-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名称上发生冲突,你就不会发生冲突(至少与头名相关).


Ala*_*les 19

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

所以使用"-I"可能是一些组合:习惯,懒惰,向后兼容性,对新选项的无知,与其他编译器的兼容性.

解决方案是通过将头文件放在子目录中来"命名"头文件.例如,将您的endian标头"include/mylib/endian.h"添加"-Iinclude"到命令行,您可以将#include "mylib/endian.h"其与其他库或系统库冲突.


R..*_*R.. 15

我这是你的前提,那-I就是危险是假的.该语言使用任何形式的#include充分实现定义来搜索头文件,使用与标准头文件的名称冲突的头文件是不安全的.简单地避免这样做.

  • 它超越了与"标准"*头文件的冲突; 因为它似乎可以是任何内部实现文件(例如`/ usr/include/x86_64-linux-gnu/sys /`目录中的那些...),看起来这几乎可以是任何名称.`debugreg.h`?您如何知道避免所有可能包含的名称? (5认同)
  • `sys/*`中的标题不是"内部".它们是由POSIX或特定操作系统定义的扩展,您不需要关心使用`-I`来屏蔽它们,除非您的应用程序需要将它们用于某些东西.在任何情况下,命名你的标题sys/any似乎是一个非常糟糕的主意. (3认同)

MSa*_*ers 11

一个明显的例子是交叉编译.GCC在历史上的UNIX假设中遇到了一些问题,即您总是在为本地系统编译,或者至少是非常接近的东西.这就是编译器的头文件在系统根目录中的原因.缺少干净的界面.

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

现在在交叉编译中,GCC的行为更像是Windows的编译器.它不再假定您打算使用本地系统标头,而是允许您准确指定所需的标头.显然,您链接的库也是如此.

现在请注意,当您执行此操作时,替换标头集旨在置于基本系统之上.如果它们的实现相同,您可以在替换集中省略标题.例如,机会是<complex.h>相同的.复数实现没有那么多变化.但是,您不能随意替换内部实现位<endian.h>.

TL,DR:这个选项适用于那些知道自己在做什么的人."不安全"不是目标受众的论据.

  • 在交叉编译时,您通常会调用使用与/ usr/include不同的目录的其他编译器,因此-I不再是单个实例中的必需项. (2认同)