プリプロセッサ

スポンサーリンク

プリプロセッサ

狭義のコンパイルが行われる前に,プリプロセッサ (preprocessor) によってソースコードに前処理が行われます。
このプリプロセッサ用の指令 (preprocessor directive) を,ソースコード中に埋め込むことができます。

プリプロセッサ用の指令は,#include や #define など,記号 # で始まります。
純粋な C のソースコードとは異なり,各命令は独立した 1 行に書く必要があります。

#include

#include 指令は,ヘッダファイルの内容をソースコード中に取り込む (インクルードする) 命令です。
標準ヘッダファイルのインクルードは山括弧を使って #include ,自作ヘッダファイルのインクルードはダブルクォーテーションを使って #include "hoge.h" と書き分けられます。

記号定数

次のプログラムは,配列要素数の指定に定数を利用した例です。

#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
スポンサーリンク