第4回:分割コンパイル

第4回:分割コンパイル

ホーム ] 上へ ] 第1回:基本作法 ] 第2回:データ型 ] 第3回:関数 ] [ 第4回:分割コンパイル ] 第5回:GDBでデバッグ ] 第6回:ポインタ初級編 ] 第7回:配列とポインタ ] 第8回:構造体とポインタ ] 第9回:文字列とポインタ ] 番外編:配列の様に使えるリスト ]

最終更新日: 03/02/21     藤岡研外部からのアクセス数: 

 

ソースファイルを分割しよう

  プログラムが巨大になってくると、ソースファイルも大きくなって、取り扱いが面倒になってきます。プログラムが1000行を越えるようになってきたら、ソースファイルを分割するようにしましょう。

 

コンパイルの仕方−再び

gcc [option] <source file name> [link option]

bullet-o output: 出力ファイル名を output にする (デフォルトは a.out)。
bullet-Wall: ウォーニングを厳しくする。
bullet-O1 〜 -O3: 最適化する (1:遅い・安全〜3:速い・危険)。
bullet-g: デバッグ情報を含める。最適化しない。
bullet-lxxx: ライブラリ xxx を読み込む。
bullet-lm: 数学ライブラリ
bullet-lX11: X11 ライブラリ
bullet-ljpeg: Jpeg 読み書きライブラリ
bullet-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;
}

 

bulletコンパイルの際に、ソースファイルを並べて書いてやると、コンパイルできる。

gcc -o program source1.c source2.c

bulletソースファイルがたくさんになると、コンパイルに時間がかかる。ちょっとだけ修正して再コンパイルという場合でも、全部コンパイルしないといけない。

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 コマンドを使うと、複雑なコンパイル作業を自動的に行うことができる。

bulletmakeコマンドで、コンパイル作業を自動化できる。どのようにコンパイルするかは、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

bulletMakefile は、ソースファイルと同じディレクトリに置き、次のように実行する。

make program

program の依存存関係・生成ルールが Makefile 中でに最初に出てくるので、この場合は、単に

make

としても良い。

実行すると、コンパイルが行われ、source1.o source2.o が作られ、続いて program が作られる。

bulletここで、source1.c を書き換えて、再び make を実行する。make は、ファイルのタイムスタンプ (ファイルを最後に書き換えた日付・時刻) を調べて、コンパイルする必要のあるものだけをコンパイルしてくれる。この場合は、

gcc -c source1.c

gcc -o program source1.o source2.o

だけが実行される。source2.c → source2.o のコンパイルは実行されない。

bulletソースファイルの数が増えてくると、Makefile を書くのも結構大変になってくる。そこで、Makefile を簡略化する暗黙のルールというものがある。

 ***.c から ***.o を作る生成ルールは、ソースファイルの名前が違う以外は、どれも同じである。このような場合、次のように、生成ルールをまとめて書くことができる。これを、暗黙のルールという。

program : source1.o source2.o
	gcc -o program source1.o source2.o

.c.o :
	gcc -c $<

bulletMakefile のメンテナンスを容易にするため、変数を用いることができる。変数定義は、
CFLAGS = -Wall -O2

の様にし、これを参照するときは、

gcc ${CFLAGS} -c source1.c      

の様にする。

bullet以下に、一般的な 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 宣言が非常に重要な意味を持つ。

bullet次の二つのソースファイルをコンパイルして実行してみよう。先程の例からの変更点は、関数 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;
}

 

bulletこのプログラムは正しく動作しない。これは、main の側で、関数 sub の型が判らず、int 型に解釈されてしまうため、sub の側でグローバル変数 b の型が判らず、int 型に解釈されてしまうためである。このように、C言語では、型の分からない関数や変数があると、勝手に int 型と解釈してしまう。他のソースファイルで定義されている関数やグローバル変数を利用するときは、利用する行よりも前で extern 宣言をしなければならない。

bullet正しいプログラムは、次のようである。

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 宣言すること。

 

ヘッダファイル (インクルードファイル) を活用しよう

bulletプログラム中に

#include "header.h"

と書いておくと、その場所に、header.h が読み込まれる。

#include <stdio.h>

は、システムのディレクトリ (/usr/include など) からヘッダファイルを探して、読み込む。

bulletヘッダファイルには、以下のような情報を書く。
bullet#define
bullet型定義 (typedef)
bulletextern 宣言

bulletヘッダファイルには、関数本体書いてはいけない。

bulletヘッダファイルを作ったら、Makefiel にヘッダファイルの依存関係も入れておく。

入れておかないと、ヘッダファイルを書き換えた時、コンパイルが行われない。

bulletヘッダファイルの依存関係を調べ、Makefile に自動的に依存関係を追加してくれるコマンド。

makedepend

 

Mail to: < miura@ise.eng.osaka-u.ac.jp >