プリプロセッサ
プリプロセッサ
狭義のコンパイルが行われる前に,プリプロセッサ (preprocessor) によってソースコードに前処理が行われます。
このプリプロセッサ用の指令 (preprocessor directive) を,ソースコード中に埋め込むことができます。
プリプロセッサ用の指令は,#include や #define など,記号 # で始まります。
純粋な C のソースコードとは異なり,各命令は独立した 1 行に書く必要があります。
#include
#include 指令は,ヘッダファイルの内容をソースコード中に取り込む (インクルードする) 命令です。
標準ヘッダファイルのインクルードは山括弧を使って #include
記号定数
次のプログラムは,配列要素数の指定に定数を利用した例です。
#include <stdio.h>
#define ARRSIZE 256 /* 定数を定義 */
int main(void)
{
int i, arr[ARRSIZE]; /* arr[256] と書いたのと同等 */
for (i = 0; i < ARRSIZE; i++) {
/* ... */
}
return 0;
}
#define 指令によって,ARRSIZE という記号定数を定義しています。
この記号定数はプリプロセッサによって処理され,ソースコード中のすべての ARRSIZE が 256 に置き換えられます。
マジックナンバーがあちこちにばら撒かれるのは好ましくないので,配列要素数の指定などには,すすんでこの記号定数を使うといいです。
条件付き取り込み
次のプログラムは,記号定数 DEBUG が定義されているか否かによって,ソースコードの一部をコンパイルするかを選択する例です。
#include <stdio.h>
#define DEBUG
int main(void)
{
#ifdef DEBUG /* DEBUG が定義されていればコンパイル */
/* デバッグ用の処理 */
#endif
return 0;
}
#define 指令では,値を与えずに DEBUG のようなシンボルを定義することも可能です。
#ifdef DEBUG から #endif までは,シンボル DEBUG が定義されている場合にのみコンパイルされます。
他には,#ifndef (#ifdef の否定), #elif (else if), #else などの指示が利用できます。
関数型マクロ
次のプログラムは,ある値の 2 乗を計算する SQUARE マクロを定義した例です。
#include <stdio.h>
#define SQUARE(x) (x * x)
int main(void)
{
int a = 5;
int s = SQUARE(a);
printf("%d\n", s); /* 出力: 25 */
return 0;
}
#define 指令によって,このような関数型マクロを定義することもできます。
SQUARE(a) の部分は,プリプロセッサによってコンパイル前に (a * a) と展開されます。
この SQUARE マクロは,例えば SQUARE(a + b) と書いたときに,(a + b * a + b) と展開されてしまうという問題があります。
これを回避するには,次のように括弧を多めに付けて定義します。
#define SQUARE(x) ((x) * (x))
定義済マクロ
次のマクロはコンパイラによって定義されます。
マクロ | 説明 |
---|---|
__LINE__ | 現在の行番号を表す値 |
__FILE__ | ファイル名を表す文字列 |
__DATE__ | コンパイル日付を表す文字列 |
__TIME__ | コンパイル時刻を表す文字列 |
__STDC__ | ANSI 準拠の処理系でのみ値 1 |