第15回
関数の宣言~ライブラリとヘッダファイル

externとモジュール

複数のソースファイルを使って……と書きました。大規模なプログラムでは処理を複数のソースファイルに分割し、最終的に1つの実行形式ファイルに結合します。このとき必要になるのがextern宣言です。

外部の関数を参照する

Cでは、ソースファイルを機能や役割別に分けて記述し、それぞれをコンパイルして生成された.objファイルをリンカ(linker)で結合して、最終的に1つの実行形式ファイルを作れます。

1つの.cソースファイルをコンパイルして生成された.objファイルをモジュール(module)と呼び、関数や変数の参照関係はモジュール単位で閉ざされています。abc.cから生成されたabc.objとxyz.cから生成されたxyz.objがあるとします。この2つのオブジェクトファイルは、お互いにその内部で定義された関数や変数を参照できません。

他のモジュールで定義されている関数を参照するには、参照元のソースでその関数がextern宣言されていなければなりません。abc.c内で定義されたinputという関数をxyz.cで使用するには、xyz.cの中でinput関数を呼び出す前にexternを使ってプロトタイプ宣言されていなければなりません。

extern int input(char *);
のような形式です。

externは関数だけではなく、他のモジュールで宣言されたグローバル変数を参照したい場合にも使います。グローバル変数など変数の適用範囲については、回を追って紹介します。

なお、コラムの冒頭では「extern命令」と書きましたが、厳密には「記憶域指定子」と言います。

最後にリンカが解決する

他のモジュールで定義された関数や宣言された変数をextern宣言して参照できるようになっているのは、先述したようにCのプログラムが各ソースから一旦.objファイルを生成し、最後にそれらを結合する──という形になっているためです。

1つのソースをコンパイルしているとき、そのソースの外(他のモジュールやライブラリ)に実体のある関数や変数が『果たして本当に存在しているかどうか』を、コンパイラは判断できません。そのためextern宣言によって、『とりあえず他のモジュールまたはライブラリに存在していますよ』とコンパイラを納得させてあげる訳です。

そうしてコンパイルを済ませた後、リンカが他のモジュールやライブラリからextern宣言された関数や変数を探して結合します。これを『外部参照の解決』などと呼んでいます。

ライブラリ関数とヘッダファイル

Cのプログラムは『関数の集合』だと言われます。関数はプログラマーが独自に定義することもできますし、標準関数など処理系に予め備わっているものを使うこともできます。

この『予め備わっている関数』は、あくまで事前に作成されライブラリとして提供されているだけであって、本質的にはプログラマーが独自に作った関数と変わりありません。Cの基本命令は、これまでに紹介した制御構造を作るifやfor、呼び出し元に値を返すreturnなどごくわずかです。そして、残りの足りない機能を関数で補うことになります。

その「足りない機能」の一部が、標準入出力関数や文字列関数など処理系に用意されているライブラリ関数です。本来ならば、文字列を標準出力に送ったり、標準入力から文字列を受け取ったり、文字列をコピーしたり……といった基本的な処理も、プログラマーが関数として作らなければなりません。しかし、それではあまりに効率が悪いので、一般的なプログラムで用いる基本的な機能を関数として役割ごとにライブラリにまとめたものを、処理系が提供してくれているのです。

そして、stdio.hやstring.h、stdlib.hなどのヘッダファイルには、それら処理系に用意されている各種ライブラリ関数を独自のソース中で使用するために、externによるプロトタイプ宣言が記述されているのです。