法大奥山研究室

 previous  contents

15. プリプロセッサ (#directive) とマクロ


 ソースファイルに書き込む字句(トークン,Token)は,

まとりとなります。この内,整数型や浮動小数点型の実数型配列型ポインタ型構造体型関数型宣言,各種演算子と,そして,関数の定義を見ました。ここでは「プリプロセッサ」について見ます。

 前処理(プリプロセッシング,Preprocessing)とは,コンパイラがアセンブリ言語に翻訳する前に,ソースファイルの一部を条件的にスキップしたり,他のソースファイル(ヘッダファイル)を読み込んだり,マクロを置き換えるための処理で,「プリプロセッサ」(前処理指令,プリプロセッシング・ディレクティブ,Preprocessing Directive)はそのための命令を指します。

 プリプロセッサは,字句要素の中の区切り子 # で始まり,改行で終わります。プリプロセッサで定義された定数や関数をマクロ(Macro)と呼びます。

■プリプロセッサ
#include <file>備え付けのヘッダファイル file の読み込み。/usr/include 内のファイル。
#include "myheader"自らが作成したヘッダファイル myheader の読み込み。
#if 条件if 文の始まり。#endif とペア。
#endifif 文の終わり。#if とペア。
#elseif 文における分岐。
#elif 条件if 文における分岐。
#define マクロマクロ を定義。
#undef マクロ#define マクロ で定義された マクロ を無効にする。
#ifdef マクロマクロ が定義されていれば」という意味。#if defined と同じ。
#ifndef マクロマクロ が定義されていなければ」という意味。#if !defined と同じ。

ヘッダファイルの読み込みについては,コンパイル時に I オプションを付してヘッダのあるディレクトリを追加することができます。

cc -I /path/to/directory1/ -I /path/to/directory2/ ...

この場合,自らが作成したヘッダファイルのあるディレクトリを追加すれば,#include "myheader" ではなく #include <myheader> としても,myheader を読み込めます。

/* Example 15.1 */

#include <stdio.h>
#include "def.h"

#ifndef PI
#define PI 3.14159
#endif

#ifndef E
#define E  2.71828
#endif

int main(void)
{
       printf("PI = %f\n", PI);
       printf("E = %f\n", E);
       printf("max{PI,E} = %f\n", MAX(PI,E));
       printf("min{PI,E} = %f\n", -MAX(-PI,-E));

       return 0;
}

2つ目の #include で独自に作成したヘッダファイル def.h を読み込んでいます。その def.h のソースは次の通りです。

/* def.h */

#include <math.h>

#define PI 4*atan(1.0)
#define E  exp(1.0)
#define MAX(x,y) (x>y?x:y)

def.h の1つ目の #include でヘッダファイル math.h を読み込んでいます。ヘッダファイル math.h には,関数 expatan が入っています。最初の #define では円周率を意味するマクロ PI4*atan(1.0) と定義しています。2つ目の #define では,マクロ E を自然対数の底と定義しています。3つ目の #define では MAX というマクロを定義しています。2つの変数の最大値を出す関数です。def.h で定義された PIE が定義されていない場合を想定して,元のソースファイルには #ifndef で再定義しています。実行結果は,次の通りです。

PI = 3.141593
E = 2.718282
max{PI,E} = 3.141593
min{PI,E} = 2.718282

def.h で定義された定数や関数は,正しく読み込まれているのが確認できます。(ソースを分割せずに,def.h の中身を元のソースの main 関数の上に入れ,#ifndef 部分を削除しても良いです。ここでは,例証のために分割しました。上のようなヘッダファイルの利用法とは別に,プログラムをモジュール化して行く段階で作成するヘッダファイルもあります。これについては「16. プログラムの分割とリンク」を参照して下さい。)

 上では,PIEMAX というマクロを定義しましたが,マクロはコンパイラによって既に定義済みのものがあります。それらを変更することはできません。

■定義済みマクロ[C99, 6.10.8, 1の一部]
__LINE__     行番号。
__FILE__ソースファイル名。
__DATE__ソースファイルの変更日。mmm dd yyyy の形式で出力。
__TIME__ソースファイルの変更時間。hh:mm:ss の形式で出力。
__STDC__C99準拠のコンパイラならば 1 を出力。
/* Example 15.2 */

#include <stdio.h>

int main(void)
{
       printf("LINE = %d\n", __LINE__);
       printf("FILE = %s\n", __FILE__);
       printf("DATE = %s\n", __DATE__);
       printf("TIME = %s\n", __TIME__);
       printf("STDC = %d\n", __STDC__);

       return 0;
}

実行結果です。(機械に依存する部分があります。)

LINE = 7
FILE = ex15.2.c
DATE = 09/24/02
TIME = 13:47:17
STDC = 1

 previous  contents