ショートカット
ファシリテーター × あり方
コーディングの向こう側
Hello, ANOTHER world!
オブジェクト指向のはなし
プログラミングのはなし
C言語実力診断クイズ
eSkillBooks
Hello, ANOTHER world!

分割コンパイル

関数 x から関数 f を呼び出す場合、f の型(プロトタイプ宣言)を知っている必要があります。型とは、つまりインターフェイスです。インターフェイスを知らなければ、その関数を使うことはできません。
呼び出す側と呼び出される側が別のモジュール(ライブラリファイルや別のソースファイル)に収められているとしたら、つまり、分割コンパイルするようにできているとしたら、インターフェイスはどこにあるでしょう。簡単な例として、標準関数 printf を使う場合を考えてみましょう。printf の実体はライブラリファイルの中にあります。ですが、実体が必要になるのはリンクの時で、呼び出す側(ここでは関数 x)をコンパイルしてそのオブジェクトができるまではインターフェイスしか使いません。
関数のインターフェイスはプロトタイプ宣言のことですから、それはヘッダーファイルの中にあります。printf の宣言が書かれているヘッダーファイルは "stdio.h" なので、呼び出す側ではこれをインクルードすることになります。
一般的な言い方をすると、「別のモジュールにある関数を使う必要がある場合は、必要なヘッダーファイルをインクルードしておいてコンパイルする」ということです。これは、呼び出す関数がライブラリ内にあろうと自分で作った別のソースファイル内にあろうと同じことです。関数 x から別のモジュールにある関数 f を呼び出す場合は、次の図のようになります。
さて、別のソースファイルにある関数を呼ぶには、そのインターフェイスを記述したヘッダーファイルをインクルードしなければならないわけですが、ということは、自分でサブルーチンのモジュールを作るときには、ソースファイルとヘッダーファイルのセットを作らなければならないということです。つまり、モジュールを1つ作るときには、同じ名前で拡張子が違う2つのファイル *.c(または *.cpp など)と *.h を作ることになります。
厳密には拡張子以外を同じ名前にしなければならないというルールはないのですが、同じにすることをおすすめします。モジュールが増えてきたときに、その方が整理しやすいからです。
練習として、少し複雑なケースを考えてみましょう。
  • 関数 main は関数 a と b を使っている
  • 関数 a も関数 b を使っている
  • 関数 b は標準関数 printf を使っている
  • main, a, b はそれぞれ別のソースファイルにある
関数 main, a, b は別々のソースファイルにあるので、別々にコンパイルされます(分割コンパイル)。そのとき、使う必要がある関数のインターフェイスが書かれたヘッダーファイルをインクルードしなければなりません。
  • main のソースファイルからは a と b のヘッダーファイルをインクルード
  • a のソースファイルからは b のヘッダーファイルをインクルード
  • b のソースファイルからは printf のヘッダーファイルをインクルード
別々にコンパイルするので、その結果別々のオブジェクトファイルができます。
最後に、できたオブジェクトファイルとライブラリファイルをリンクして実行形式のファイルを作ります。
図中では a と b のソースファイルから自分自身のヘッダーファイルをインクルードしていますが、これには特別な理由があります。
説明が後回しになってしまいましたが、分割コンパイルには、コンパイルにかかる時間を節約できるというメリットがあります。プログラムを修正したとき、変更があったソースファイルだけをコンパイルしなおせばいいからです。ヘッダーファイルを修正したときも、それをインクルードしているソースファイルだけをコンパイルすれば済みます。つまり、どのオブジェクトファイルがどのソースファイルとヘッダーファイルに依存しているかを把握していさえすれば、必要最小限の時間でコンパイルできるようになるわけです。
しかし、そのような依存関係を把握するのは面倒だし、モジュールが増えてくると間違いやすくなってきます。そこで、makefile というファイルを作ります。これは分割コンパイルの際の各パーツ間の依存関係を記述したテキストファイルです。これを make というツールに渡せば必要な部分だけコンパイルして、リンクまで行うことができます。
少し古い開発環境では、自分で makefile を書いていました。しかし最近の統合開発環境では自動的に依存関係まで把握して最小限のコンパイルだけするようになっています。たとえば VC++ にある「ビルド」というのがそれです(ちなみに「リビルド」というのは全部コンパイルしなおす機能です)。意識する必要がなくなったとはいえ、原理は以前と変わっていません。
開発環境が全自動でやってくれるとはいえ、やはり中身の動作を知っておくことは大事です。makefile についてもっと詳しく知りたければ「MAKE の達人」という本を読んでみると良いでしょう。ちょっと古くさい入門書ですが、いまさら makefile を完全マスターしてもあまり役に立たないので、こういう入門書で原理だけきちんと押さえておくのが現実的でしょう。

("Hello, ANOTHER world!" は2001年11月から2002年11月にかけて作成されたコンテンツです。)