web-dev-qa-db-ja.com

なぜ危険なプロジェクトで-I includeスイッチを使用するのですか?

GCCの-Iスイッチの細かい印刷を読んで、コマンドラインでそれを使用するとシステムインクルードが上書きされることにかなりショックを受けました。 プリプロセッサドキュメント から

"これらのディレクトリは標準システムヘッダーファイルディレクトリの前に検索されるため、-Iを使用してシステムヘッダーファイルを上書きし、独自のバージョンに置き換えることができます。"

彼らは嘘をついていないようです。 GCC 7を使用する2つの異なる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>とだけ記述されています。私の明らかな誤解(そしておそらく他の誤解)は、山括弧がこれを防いでいたということでしたが、-私は思っていたよりも強いです。

強力ではありませんが、十分ではありません。これは、最初にコマンドラインに/ usr/includeを貼り付けても修正できないためです。

」標準システムのインクルードディレクトリ、または-isystemで指定されたディレクトリが-Iでも指定されている場合、-Iオプションは無視されます。ディレクトリはまだです。検索されますが、システムの通常の位置にあるシステムディレクトリとしてチェーンをインクルードします。」

実際、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を使用する正当な理由は何ですか?付随的な名前に基づいてシステム上の任意のヘッダーを上書きできます衝突。ほぼ全員が-iquoteを代わりに使用するべきではありませんか?

32
HostileFork

-I-iquoteよりも正当な理由は何ですか? -Iは標準化されています(少なくとも POSIX で)が、-iquoteは標準化されていません。 (実際、-Iを使用しているのは、tinycc(プロジェクトのコンパイルに使用するコンパイラの1つ)が-iquoteをサポートしていないためです。)

危険を考えると、プロジェクトはどのように-Iで管理しますか?インクルードをディレクトリにラップし、-Iを使用してそのディレクトリを含むディレクトリを追加します。

  • ファイルシステム:includes/mylib/endian.h
  • コマンドライン:-Iincludes
  • C/C++ファイル:#include "mylib/endian.h" //or <mylib/endian.h>

これにより、mylibの名前で衝突しない限り、衝突しません(少なくともヘッダー名に関しては)。

33
PSkocik

GCCのマニュアルを振り返ると、-iquoteおよびその他のオプションがGCC 4にのみ追加されたように見えます https://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Directory- Options.html#Directory%20Options

したがって、"-I"の使用は、おそらく、習慣、怠zy、後方互換性、新しいオプションの無知、他のコンパイラとの互換性のいくつかの組み合わせです。

解決策は、ヘッダーファイルをサブディレクトリに配置することで、ヘッダーファイルを「名前空間」にすることです。たとえば、エンディアンヘッダーを"include/mylib/endian.h"に配置し、"-Iinclude"をコマンドラインに追加すると、他のライブラリまたはシステムライブラリと競合しない#include "mylib/endian.h"を実行できます。

19
Alan Birtles

これは、-Iが危険だという前提は誤りです。この言語は、#includeのいずれかの形式で実装が定義されたヘッダーファイルの検索を残しているため、標準ヘッダーファイルの名前とまったく競合するヘッダーファイルを使用することは安全ではありません。単にこれを行うことを控えてください。

15
R..

明らかなケースは、クロスコンパイルです。 GCCは、常にローカルシステム用にコンパイルしているという歴史的なUNIXの前提、または少なくとも非常に近いものに苦しんでいます。そのため、コンパイラのヘッダーファイルはシステムルートにあります。クリーンなインターフェイスがありません。

これに対して、Windowsはコンパイラを想定しておらず、Windowsコンパイラは、ローカルシステムをターゲットにしていると想定していません。そのため、コンパイラのセットとSDKのセットをインストールできます。

現在、クロスコンパイルでは、GCCはWindows用のコンパイラのように動作します。ローカルシステムヘッダーを使用することを前提とはしていませんが、必要なヘッダーを正確に指定できます。そして明らかに、リンクしたライブラリにも同じことが言えます。

これを行うと、置換ヘッダーのセットは、ベースシステムのtopに進むように設計されていることに注意してください。実装が同一である場合、置換セットのヘッダーは省略できます。例えば。 <complex.h>が同じである可能性があります。複素数の実装にはそれほど大きな違いはありません。ただし、<endian.h>などの内部実装ビットをランダムに置き換えることはできません。

TL、DR:このオプションは、自分が何をしているかを知っている人向けです。 「安全でない」ということは、対象読者にとって議論ではありません。

11
MSalters