C.ヘッダファイルの取り込み
今までの例ではソースファイルは1つだけでしたが、複数個ソースファイルがあっても構いません。複数個のソースファイルの場合、例えばソースファイルAからソースファイルBに記述された関数 f を呼び出したい時があるかも知れません。これを正しくコンパイルするにはソースファイルAに関数 f のプロトタイプ宣言をしてあげないとコンパイラは関数を見つけられずエラーになります。そうするとソースファイルAとBの両方に関数 f のプロトタイプ宣言をしないといけないことになり修正するときに面倒ですし、第一分かりづらくなります。
このようなときに「ヘッダファイル」を使います。ヘッダファイルには一般的によくつかうマクロや他のソースファイルから呼び出される関数のプロトタイプ宣言などを記述します。実行文も記述できますが、極力ソースファイルにした方が良いでしょう。
ヘッダファイルは特別な形式もなくソースファイルと同じようにコーディングします。ただし、保存するときにヘッダファイルであることが分かるように拡張子を「 . h」にして保存します。
ただこのままではヘッダファイルがあってもソースファイルとの関係性がに認識されないので、「#include 命令」を使ってヘッダファイルの取り込みを指示します。

 1. #include <ファイル名>
 2. #include "ファイル名" 

・#include が記述された場所に指定のファイルがそのまま挿入される
・形式1 はシステムディレクトリ(あらかじめコンパイラで定められたディレクトリ(=フォルダ))からファイルを探す
・形式2 はカレントディレクトリ(現在のディレクトリ)からファイルを探し、見つからない場合はシステムディレクトリから探す
・通常はソースファイルの先頭に記述する

形式1 はコンパイラに最初から用意されている標準ライブラリ用のヘッダファイル、標準ヘッダファイルを取り込むときに記述します。今までのサンプルの一番最初は呪文のように「#include <stdio.h>」となっていたのですが、これは基本的な入出力を扱う標準ライブラリを使いたかったからなのです。ほとんどのプログラムはこの標準ライブラリの力を借りてプログラムされるので、何はともあれ stdio.h はインクルードするようにしましょう。
ちなみにLSI-Cコンパイラでは include で検索するシステムディレクトリを変えられます。変えたいときは第1章の「コンパイラの設定」で_lccの中の -I(ディレクトリ) のところを設定したいディレクトリに変更して下さい。
形式2 はユーザーが作ったヘッダファイルを取り込みたい時に使います。ファイル名はソースファイルと同じにしておくと分かりやすく良いでしょう。
なお、ヘッダファイル中で他のヘッダファイルをインクルードしたり、定義マクロを使ったりしても構いません。

/* ヘッダファイル */
/*   defs.h    */


#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#define BUF_SIZE 256
#define ITEM_MAX 1000
 
/* ソースファイル */
/*   prog.c    */

#include "defs.h"


void main (void)
{
 〜
}

右と左の
ソースは
同等
/* ソースファイル */
/*   prog.c    */

#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0
#define BUF_SIZE 256
#define ITEM_MAX 1000


void main (void)
{
 〜
}

D.条件付きコンパイル


ある条件によってプログラムの一部をコンパイルしたりしなかったりすることができます。条件に定数を使うものとマクロを使うものがあります。

* #if 〜 #else 〜 #endif
定数式の真偽によってコンパイルを制御します。

 #if 定数式
  (定数式が真の場合ここをコンパイル)
 #else
  (定数式が偽の場合ここをコンパイル)
 #endif

定数式には関係式や論理式も記述できます。ただし、プリプロセッサで処理するので変数は使えません。
必要なければ #else 部は省略しても構いません。また、複数条件の場合は #elif を使います。

 #if 定数式1
  (定数式1 が真の場合ここをコンパイル)
 #elif 定数式2
  (定数式2 が真の場合ここをコンパイル)
 #elif 定数式3
  (定数式3 が真の場合ここをコンパイル)
 ・・・・
 #endif

* #ifdef 〜 #else 〜 #endif
 #ifndef 〜 #else 〜 #endif


● #ifdef マクロ名
  (マクロ名が定義されている場合ここをコンパイル)
 #else
  (マクロ名が定義されていない場合ここをコンパイル)
 #endif
 
● #ifndef マクロ名
  (マクロ名が定義されていない場合ここをコンパイル)
 #else
  (マクロ名が定義されている場合ここをコンパイル)
 #endif

必要なければ #else 部は省略しても構いません。ただし、「#if 〜」とは違って #elif はありません。
#ifdef 〜 を上手く使えば効率よく作業できます。いくつか例を見てみましょう。

◇デバッグがしやすくなる
次のようにコーディングしておくとデバッグしやすくなります。
こうしておけば DEBUG を定義すれば変数の値などを追うことができ、プログラムの実行過程が把握できます。
DEBUG は #define で定義する方法の他にコンパイラの起動オプションを使っても定義できます。この場合、コンパイラの起動オプションを変えるだけでコンパイルする部分を変更できます。つまり、エディタでソースを変更しなくても簡単にデバッグしたりやめたりできます。
コンパイラの起動オプションはコンパイラによって違います。ここで使っているLSI-Cのコンパイラの場合はコンパイルするときに次のようにします。

lcc -DDEBUG test.c

この起動オプションを付けると「#define DEBUG 1」という文がコンパイル時に自動的に挿入されます。

 [ 例 ] 条件付きコンパイルでデバッグする例  ex7-1.cGet! ソースファイル

◇コメントとして利用できる
プログラムを修正していくときに修正する前のコードをコメントとして残しておくことがあります。「 /* 」と「 */ 」を使えばコメントにできますが、コメントの入れ子はできないのでコメントにしたい部分が長いと不便です。
この時に #ifdef を使ってコメントにすることが可能です。

#ifdef COMMENT
 ・・・・・ /* ・・・・・ */
 ・・・・・・ /* ・・ */
 ・・・・・
#endif

COMMENT が定義されていなければ、上記部分はコンパイルされずコメントと同じにことになるわけですね。余談ですが、LSI-Cのコンパイラではオプション -cn を指定すればコメントの入れ子も使えるようになります。

◇ヘッダの二重取り込みを避けられる
ヘッダファイルの中でさらに別のヘッダファイルを取り込むことが良くあります。そうすると同じヘッダファイルを1つのソースファイル中に何度も取り込んでしまうことになります。そうすると同じ定義が何度もされてしまいコンパイルエラーとなります。
◇ソースファイル

/* file.c */
#include "head1.h" 
#include "head2.h"

 ・・・・・

◇ヘッダファイル1

/* head1.h */
#include "defs.h" 
 ・・・・・

◇ヘッダファイル2

/* head2.h */
#include "defs.h"
 ・・・・・

これを避けるためにヘッダファイルの最初と最後に次のようなコーディングをします。

/* def.h */

#ifndef __DEF_H /* __DEF_H が定義されていなければ
             ここから下もコンパイル */

#define __DEF_H
 /* __DEF_H を定義。
             これで2回目以降の取り込みではコンパイルされない */

 ・・・・・・
 (ヘッダ本体)
 ・・・・・・
#endif

1回目のヘッダの取り込みでは __DEF_H は定義されていないので、#define 、本体部分と取り込まれます。この時に __DEF_H が定義されます。
よってこれ以降に def.h を再度取り込んでも既に __DEF_H が定義されているので、本体は取り込まれません。こうしてヘッダファイルの二重取り込みを防止しています。
マクロを定義するときに他のマクロ名と重なっていると1回目の取り込みもされない可能性があるので注意が必要です。できるだけヘッダファイルの名前を使うなどしてユニークなマクロ名にします。


Jump to Phase #07-1Phase #07-1: コンパイルの過程(1) へ
Phase #07-3: コンパイルの過程(3) へJump to Phase #07-3

△戻る
▲トップに戻る