本物のC

C の知識:分割コンパイル


整理の初歩は分類です。

リンク

C の基本:プログラムの作成」で書いたのは単一のソースコードの場合ですが、C 言語のプログラムを複数のコードで構成する場合、実行可能ファイルは

の二段階を経て作られます。

ソースファイルは個別にコンパイルされてオブジェクトファイル(object file)になり、全てのオブジェクトファイルをリンクによって組み合わせる事で一つのプログラムとなります。

foo.c
foo.o
bar.c
bar.o
:

⎬⇒

foo.exe
コンパイルとリンク

(msvc の場合オブジェクトファイルの拡張子は .obj です。)

ライブラリを利用する場合も、リンクによってライブラリのコードがプログラムに統合されます。(但し動的リンクの場合、ライブラリのコードはプログラム本体に組み込まれません。)

ヘッダファイル

リンクが上手くいく為には、それぞれのコードが公開する機能、つまり関数の使い方などを約束しておく必要があります。

結合部さえ形が合っていれば後から組み合わせられる

それをコンパイラが理解して、コンパイル時に間違いがないか確認できる様にしたのがヘッダファイル(header file)です。

「main.c」にmain関数を書き、「stat.c」に統計処理のコードを分けて書くとします。

コーディング

ヘッダファイル「stat.h」に

double mean(int n, double *data);

などと stat.c の公開する関数の宣言を書きます。stat.c の方では

#include <stdlib.h> /* NULL の為に */
#include <math.h> /* nan の為に */
#include "stat.h" /* インクルード */
double mean(int n, double *data) /* mean を実装 */
{
int i;
double m = 0;
if (n <= 0)
return nan(NULL);
for (i = 0; i < n; i++)
m += data[i];
return m;
}

とヘッダファイルをインクルードしつつ関数本体を定義します。

利用する側は単にヘッダファイルをインクルードすればコンパイルが通ります。

#include <stdio.h>
#include "stat.h" /* インクルード */
int main(void)
{
double x[] = { 1.1, 2.2, 3.3 };
printf("mean : %f\n", mean(sizeof(x) / sizeof(x[0]), x));
return 0;
}

stat.h は stat.c と main.c の両方にインクルードされていますが、「C の基本:プログラムとメモリ」で説明した様に関数は定義によって初めてメモリ上に実体を得るので、定義さえ唯一ならば問題ありません。(宣言の形式が一致しないとリンクに失敗しますが。)

関数はデフォルトで他のファイルからも利用できる様になっています。

コンパイルとリンク

gcc の場合、-cオプションを使うとコンパイルした時点で処理を止める事ができ、

$ gcc -c stat.c $ gcc -c main.c

によりstat.omain.oが生成されます。これらを gcc に渡す事で

$ gcc main.o stat.o

リンクが行われ、一つのプログラムが出来上がります。

$ a mean : 6.600000

尚、gcc に .c を一括で渡せば

$ gcc main.c stat.c

コンパイルとリンクを全て同時に行えますが、これだと更新されていないソースファイルでも必ずコンパイルされるので、大規模のプログラムではコンパイル時間も馬鹿にならずかなり不便です。

かと言って個別にコンパイルするのも面倒なので、「GNU make」などのツールを使って作業を自動化すべきでしょう。