分割コンパイル
煩雑な説明は後述致しますが,一度作った関数を再利用するなら,分割コンパイルが
便利です.
[作るべきファイル]
これらだけ作れば,分割コンパイルにより,プログラムの移植性,再利用性が上げられる
のです.もっとも,makefileに関する予備知識がないと全くできないので本ページで説明
します.
makefikeについて
複数のソースファイルを組み合わせてプログラムを作る場合、コマンドラインで一つ一つ
命令を打ち込むのは大変面倒なのと、必要な情報を忘れる可能性があります。
そのため、makefileというマクロ集を作っておき、開発しているプログラムに関連するような
ファイルを明らかにしておく必要があります。
情報処理演習1ですでに学んでいるかもしれませんが、もう一度説明しておきます。
一見面倒そうですし,難しく感じると思いますが,大変便利なものでもあるので,
用意したmakefikeは以下のものがあります。書き方はいくらでもあるので参考程度に見て
ください。
太字は自身のソースファイルやヘッダファイルの名前に合わせて変えるところです.
CC = bcc32 [1] LINK = bcc32 [2] TARGET = kadai15fft [3] OBJ = .obj fft_test.obj fft.obj lpf.obj [4] .SUFFIXES: .obj .c .h [5] $(TARGET).exe:$(OBJ) [6] $(LINK) -e $@ $^ [7] .c.obj: [8] $(CC) -c $< [9] $(TARGET).obj: $(OBJ:%.obj=%.h) [10] fft_test.obj: [11] fft.obj: fft.h:[11] lpf.obj: lpf.h [11] clean: [12] $(RM) $(TARGET) $(OBJ) *.tds *.bak [13] |
[1]はコンパイラとしてbcc32を使う。(このままで良い)
[2]はリンカとしてbcc32を使う。(このままで良い)
{3}は実行ファイル名としてkadai15fftとする。(名前を変えて下さい)
[4]は生成されるオブジェクトファイル群を示している。(名前を変えて下さい)
[5]はコンパイルに関連する拡張子を明言している.(このままで良い)
[6]はターゲットのファイル名とそれに関するファイル名を示す。(このままで良い)
[7]は実際のコマンドを示す。$@はターゲットの名前、$^はすべての関連するファイルの名前。(このままで良い)
[8]はサフィックスルールにより、*.cから*.objファイルを生成することを示す。(このままで良い)
[9]は実際のコマンドを示す。$<は関連するファイルを最初のファイルの名前。(このままで良い)
[10]は実行のヘッダファイルの関連性を示す。(書かなくてもいいが,確実なリンクにはあった方がいい)(このままで良い)
[11]はヘッダファイルの関連性を示す。(書かなくてもいいが,確実なリンクにはあった方がいい)(名前を変えて下さい)
[12]は不要ファイルを消去することを示す。(書かなくてもいい)(このままで良い)
[13]は実際のコマンドを示す。RMはすでに用意されたマクロ。(書かなくてもいい)(このままで良い)
ソースファイルから実行ファイルができるまでを考えてみましょう。
ソースファイル一つだけであれば、ソースファイルがコンパイルされてオブジェクトファイルが
生成され、オブジェクトファイルとライブラリがリンクされて実行ファイルが生成されます。
これだけであれば、例えば,
bcc32 main.c
だけでもコンパイルからリンク、実行ファイル生成までやってくれますが、分割コンパイルには
対応できません.そこでmakefileに関連するファイルをすべて記述し,複数のファイルから構成
されるファイル群(プロジェクトといっても良い)から実行ファイルを作成します.これさえ作れば
make
とタイプすれば,実行ファイルを生成することが可能です.もしmakefile以外のファイル名にした
ければ,例えばmake_prg.mを作ったとしたら,
make -f make_prg.m
などと-fとファイル名を示せば,同様の処理が可能です.同一のディレクトリにいくつも開発する
プログラムを置いてしまう時には,こちらの方がいいかも知れません.
なお,前回説明したような,プリプロセッサ,プロトタイプ宣言との整合性には注意して下さい.
ソースファイルとしてmain.c sub1.c sub2.cがあるとして,また,sub1の関数をsub2で使うとしたら
以下のように関連付けをすれば,ソースファイル間でのやりとりができることも注目しておいて
ください.
sub1.c | sub1.h |
#include <math.h> #include "sub1.h" double fnc1(double x){ } |
#ifndef _SUB1_H #define _SUB1_H double fnc1(double) #endif |
sub2.c | sub2.h |
#include <math.h> #include "sub1.h" #include "sub2.h" double fnc2(double x){ double y; y=fnc1(x) } |
#ifndef _SUB2_H #define _SUB2_H double fnc2(double) #endif |
main.c |
#include <math.h> #include "sub1.h" #include "sub2.h" main(){ double・・・. int i; } |
ヘッダファイル内のプリプロセッサの初めの2行は,一度コンパイルしたコードについては,もう
一度コンパイルしないように,という意味合いです.sub1をコンパイルしてsub2のコンパイルを
始めたとき,#include "sub1.h"があるからといってもう一度コンパイルする意味はないので,
#ifndef _SUB2_Hという既に_SUB2_Hが定義されているなら,#endifに飛んで処理を止める,という
意味合いの行を設けています.
上記の例を基にMakefileを作るとすれば,以下のようになります.
CC = bcc32 LINK = bcc32 TARGET = main OBJ = .obj main.obj sub1.obj sub2.obj .SUFFIXES: .obj .c .h $(TARGET).exe:$(OBJ) $(LINK) -e $@ $ .c.obj: $(CC) -c $< $(TARGET).obj: $(OBJ:%.obj=%.h) main.obj: sub1.obj: sub2.h: sub2.obj: sub2.h: clean: $(RM) $(TARGET) $(OBJ) *.tds *.bak |