ソースファイルを分割しよう
プログラムが巨大になってくると、ソースファイルも大きくなって、取り扱いが面倒になってきます。プログラムが1000行を越えるようになってきたら、ソースファイルを分割するようにしましょう。
コンパイルの仕方−再び
gcc [option] <source file name> [link option]
-o output: 出力ファイル名を output にする (デフォルトは a.out)。
-Wall: ウォーニングを厳しくする。
-O1 〜 -O3: 最適化する (1:遅い・安全〜3:速い・危険)。
-g: デバッグ情報を含める。最適化しない。
-lxxx: ライブラリ xxx を読み込む。
-lm: 数学ライブラリ
-lX11: X11 ライブラリ
-ljpeg: Jpeg 読み書きライブラリ
-c: コンパイルのみ。
※ -Wall 付けて、Warning
のでないプログラムを作ろう。
分割コンパイル
ソースファイルを source1.c source2.c
に分けた場合のコンパイルの仕方:
source1.c
int sub(int a)
{
return a*b;
}
source2.c
#include <stdio.h>
int b;
int main()
{
b = 10;
printf("%d\n", sub(5));
return 0;
}
コンパイルの際に、ソースファイルを並べて書いてやると、コンパイルできる。
gcc -o program source1.c source2.c
ソースファイルがたくさんになると、コンパイルに時間がかかる。ちょっとだけ修正して再コンパイルという場合でも、全部コンパイルしないといけない。
gcc -c source1.c
gcc -c source2.c
gcc -o program source1.o source2.o
-c オプションを付けると、source1.c
がコンパイルされ、オブジェクトファイル source1.o ができる。
source1.o と source2.o
をつなぎ合わせて、最終的な実行プログラムを作ることを、リンクと言う。
コンパイルとリンクを別にしておくと、source1.c
のみを修正したときは、source2.c はコンパイルしなくてもいい。
gcc -c source1.c
gcc -o program source1.o source2.o
make を活用しよう
ソースファイルの数が増えてくると、コンパイルを行うのが面倒になってくる。make
コマンドを使うと、複雑なコンパイル作業を自動的に行うことができる。
makeコマンドで、コンパイル作業を自動化できる。どのようにコンパイルするかは、Makefile
に書く。
Makefile
の基本は、依存関係と生成ルール。依存関係とは、どのファイルをコンパイルした結果、どのファイルができる、という関係。例えば、source1.c
source2.c をコンパイルして program を作るとき、依存関係は、
source1.c → source1.o → program
source2.c → source2.c →→↑
となっている。生成ルールとは、コンパイルの仕方である。この場合の
Makefile は、次のように書く。
program : source1.o source2.o
gcc -o program source1.o source2.o
source1.o : source1.c
gcc -c source1.c
source2.o : source2.c
gcc -c source2.c
Makefile
は、ソースファイルと同じディレクトリに置き、次のように実行する。
make program
program の依存存関係・生成ルールが Makefile
中でに最初に出てくるので、この場合は、単に
make
としても良い。
実行すると、コンパイルが行われ、source1.o source2.o
が作られ、続いて program が作られる。
ここで、source1.c を書き換えて、再び make を実行する。make
は、ファイルのタイムスタンプ (ファイルを最後に書き換えた日付・時刻)
を調べて、コンパイルする必要のあるものだけをコンパイルしてくれる。この場合は、
gcc -c source1.c
gcc -o program source1.o source2.o
だけが実行される。source2.c → source2.o
のコンパイルは実行されない。
ソースファイルの数が増えてくると、Makefile
を書くのも結構大変になってくる。そこで、Makefile
を簡略化する暗黙のルールというものがある。
***.c から ***.o
を作る生成ルールは、ソースファイルの名前が違う以外は、どれも同じである。このような場合、次のように、生成ルールをまとめて書くことができる。これを、暗黙のルールという。
program : source1.o source2.o
gcc -o program source1.o source2.o
.c.o :
gcc -c $<
Makefile
のメンテナンスを容易にするため、変数を用いることができる。変数定義は、
CFLAGS = -Wall -O2
の様にし、これを参照するときは、
gcc ${CFLAGS} -c source1.c
の様にする。
以下に、一般的な Makefile の例を示す。make all (または、単に make)
とすると、コンパイルが行われ program が作成される。また、make
clean すると、source1.o source2.o program が削除される。
#**********************************************************************
# メイクファイルのサンプル
#
# '98/7/2 by K.Miura
#**********************************************************************
DEBUG = -g
CC = gcc
CFLAGS = -Wall ${DEBUG}
LDFLAGS = -Wall ${DEBUG}
LIBS = -lm
OBJS = source1.o source2.o source3.o
RM = /bin/rm -f
all : sample
sample : ${OBJS}
${CC} ${LDFLAGS} -o sample ${OBJS} ${LIBS}
.c.o :
${CC} ${CFLAGS} -c $
分割コンパイル時の extern 宣言
分割コンパイル時は、extern 宣言が非常に重要な意味を持つ。
次の二つのソースファイルをコンパイルして実行してみよう。先程の例からの変更点は、関数
sub の型が double 型になり、それにともなって printf の書式指定が %f
になっている点、およびグローバル変数 b が double
型になっている点である。
source1.c
double sub(int a)
{
return a*b;
}
source2.c
#include <stdio.h>
double b;
int main()
{
b = 10.0;
printf("%f\n", sub(5));
return 0;
}
このプログラムは正しく動作しない。これは、main の側で、関数
sub の型が判らず、int 型に解釈されてしまうため、sub
の側でグローバル変数 b の型が判らず、int
型に解釈されてしまうためである。このように、C言語では、型の分からない関数や変数があると、勝手に
int
型と解釈してしまう。他のソースファイルで定義されている関数やグローバル変数を利用するときは、利用する行よりも前で
extern 宣言をしなければならない。
正しいプログラムは、次のようである。
source1.c
extern double b;
double sub(int a)
{
return a*b;
}
source2.c
#include <stdio.h>
extern double sub(int a);
double b;
int main()
{
b = 10.0;
printf("%f\n", sub(5));
return 0;
}
※ 分割コンパイルする時は、必ず、extern
宣言すること。
ヘッダファイル (インクルードファイル) を活用しよう
プログラム中に
#include "header.h"
と書いておくと、その場所に、header.h が読み込まれる。
#include <stdio.h>
は、システムのディレクトリ (/usr/include など)
からヘッダファイルを探して、読み込む。
ヘッダファイルには、以下のような情報を書く。
#define
型定義 (typedef)
extern 宣言
ヘッダファイルには、関数本体書いてはいけない。
ヘッダファイルを作ったら、Makefiel
にヘッダファイルの依存関係も入れておく。
入れておかないと、ヘッダファイルを書き換えた時、コンパイルが行われない。
ヘッダファイルの依存関係を調べ、Makefile
に自動的に依存関係を追加してくれるコマンド。
makedepend