あるディレクトリ(~/srcとします)に、foo.cとbar.hという二つのソースファイルがあったとします。bar.hは、foo.cから次のようにインクルードしています。

ここで、別のディレクトリ(~/workとします)でfoo.cをコンパイルしたとしましょう。このとき、foo.cからインクルードしているbar.hは、期待通り~/src/bar.hを探し当てることができるでしょうか?

今度は、~/src/bar.hとは別に、~/work/bar.hというファイルを用意してみましょう。そして、先ほどと同じように、~/workで~/src/foo.cをコンパイルしてみます。このときに~/src/foo.cから取り込むbar.hは、~/src/bar.hでしょうか? それとも、~/work/bar.hでしょうか?

もし、引用符で囲まれたヘッダ名はカレントディレクトリから探索するのであれば、~/work/bar.hが取り込まれるはずですが、本当にそうなるでしょうか?

結論は、処理系によります。コンパイラが何であるかだけでなく、コンパイルオプションや環境変数等の設定まで特定しなければ、どうなるかはわかりません。JIS X3010:2003の6.10.2 ソースファイル取り込みから引用すると、

 次の形式の前処理指令
    # include  ”q文字列” 改行
は,二つの”区切り記号の間で指定した文字列で一意に決まるソースファイルの内容全体で,この指令を置き換える。指定したソースファイルの探索手順は処理系定義とする。この探索をサポートしていない場合,又は探索が失敗した場合,同じ文字列(もしあれば>文字を含めて)を含む次の指令に読み替えたのと同じ規則で再処理する。

C++に関してもほぼ同じと考えてかまいません。ちなみに、< >で囲まれたヘッダ名の探索手順も処理系定義です。もっといえば、< >で囲まれたヘッダ名がファイルであるかどうかも処理系定義になります。

ところで、現実の処理系ではどうかというと、引用符で囲んだヘッダ名をカレントディレクトリからだけ探索し、見つからなければ< >で囲んだ場合と同じ手順に移行する処理系も実在します。ただ、もっともよく見かけるのは、次のようなものです。

a.c → b.h → c.hの順に取り込もうとしている場合、

  1. a.cがあるディレクトリからb.hを探します。見つからなければカレントディレクトリを探します。
  2. b.hがあるディレクトリからc.hを探します。見つからなければa.cがあるディレクトリからc.hを探します。それでも見つからなければカレントディレクトリを探します。

多くのコンパイラでは、< >で取り込むヘッダはファイルとして実装されており、コンパイルオプション等で探索ディレクトリを追加することができるようになっています。コンパイラによっては、” “で囲まれたヘッダファイルの探索ディレクトリを追加する手段を別途用意しているものもあります。