ディレクティブ
C言語でのディレクティブとは、プリプロセッサへの指示文#defineなどのことである。
プリプロセッサは、コンパイルを開始する前に、まずソースファイルからディレクティブを読み込み、展開する。 コンパイラは、展開後のソースを処理するわけである。 ディレクティブは開発環境によってまちまちだが、ANSI規格に準拠したものは各環境において共通であると考えてよい。
以下に、ANSI規格のディレクティブのうちよく使用するものをまとめておく。
#include
言わずと知れた「インクルード文」である。これを使わないとCもC++も一切書けない。
構文1 #include <ファイル名>
構文2 #include "ファイル名"
構文1と2の違いは、ヘッダファイルを探しに行くパスの違いである。構文1は、コンパイラ既定のインクルードパスを見に行く。 構文2は、コンパイラへのオプションで与えたサーチ・パスを見に行く。オプションがなければ、ソースファイルと同一ディレクトリだけを見る。
つまり、構文1は<stdio.h>などの標準ライブラリ用、構文2は自分で作ったヘッダファイル用と思えばよろしい。
さて、ファイル名には何が指定できるのかといえば、
#include "mydir/header.h"
という具合に、相対パスも書ける(絶対パスも書けるが決して書いてはならない)。もうひとつ、
#include "mysrc.c"
とも書けるが(昔はよく、共通部品などをこうやってインクルードしていた)、分割コンパイルが基本であるから、 ソースファイルをインクルードするのはいまやご法度である。理由は、自分で考えよう。 できるからといって、何でもしてよいというわけではない。
#define
#defineは変数を別のものに置換する(マクロ置換)。主な使い方は
という3つである。プログラム中で定数として使用する例をあげる。
#define PI 3.141592
意外なことだが、πは既定義ではない。そこでこうやって定数化してくとプログラムのあちこちで使用できる。 ヘッダファイルに書いておくことが多い。あるいは、
#define ERROR_MSG1 "ファイルがみつかりません"
エラーメッセージなど定型文を定義しておく。プリプロセッサはソースファイル中のERROR_MSGという文字列をすべて、 コンパイラに引き渡す前に置き換える。メッセージを変えたいとき、一箇所だけ直せばよいことになる。
2番目の使い方は、最後のマクロ関数の項を参照のこと。
3番目の使い方は、次に説明する#ifdefとの組み合わせになる。
#define OS_WINDOWS98
たとえばOSの種類によってコンパイルする部分を変えたい場合などに使用する。 いずれの場合にも、規格には書かれていないが暗黙のルールにより、#defineの変数はすべて大文字で書くことになっている。 理由は、通常の変数と区別するためだ。
#ifdef 〜 #else 〜 #endif
さきほどの#defineの有無に応じてコンパイル対象を制御する。
#ifdef OS_WINDOWS98 bit = 16; #else bit = 32; #endif
#elseはなくてもよい。
#ifdef OS_WINDOWS98 bit = 16; #endif
逆に、定義されていないときだけ実行したいときはこれ。
#ifndef OS_WINDOWS98 bit = 32; #endif
マクロ関数
マクロ関数は、ある機能を定義する。あたかも関数のようだが、厳密には関数ではない。勘違いするとハマるので注意。
#define ADD(a,b) (a+b)
上記は、足し算を行うマクロの例である。このようにマクロ置換では引数も書くことができる。
#define MAX(a,b) ((a)>(b)?(a):(b))
これは有名なマクロであり、ほとんどどの処理系にも定義されている。2つの数の大きいほうをあらわすマクロだが、 関数と異なり毎回コンパイルされる。また、引数に型がないので、整数にも実数にも使用できる。使う側ではこのように書く。
int a = 20, b = 30; int c; c = MAX(a, b);
もうすこし高度な例をみてみよう。
#define SWAP(a,b) {int c;c=a;a=b;b=c}
この例では、2つの変数を入れ替える。ブロックを使うと、引数以外の変数も使えてしまうのだ。 うまく利用すれば、大変便利なのがマクロ関数である。 マイマクロ集をヘッダファイルにまとめておいて、いつでも使えるようにしておくのが通だ。