プログラミング言語 C

(C の プリプロセッサで) 最もよく使われる 2つの機能は、コンパイル中に ファイルの内容を 読み込むための #include と、token (= 文字の綴り) を 任意の文字列に 置き換えるための #define である。

#include を 使えば 全てのソースファイルに 同じ定義と 変数宣言を 入れることができるので、悪質なバグが 紛れ込みにくくなることが (ある程度) 保証されることになる。

もちろん、読み込むファイルの内容を 変更した場合には、それを適用する 全てのファイルは コンパイルし直さなければならない。

マクロは 次の形式で 定義される。

#define NAME text

#define 中の NAME は 変数名と 同じ形をもち、置き換える text のほうは 任意 (の文字列等) である。

通常、この text の部分は 行の最後に置く。

(また) 定義が 長くなる場合には、行の最後に バックスラッシュ (= '\') を置くと、次の行へ 続けることができる。

#define で 定義された NAME の 有効範囲は、定義の書かれた行から ソースファイルの最後までとなる。

(マクロは) 引用符で囲まれた文字列には 適用されない。(また、NAME の文字が ある文字列の一部である場合にも 適用されることはない)

例えば YES が マクロで定義された NAME だとして、
printf("YES") や
YESMAN
では マクロによる 置き換えは 実行されない。

NAME および text (の中身) は 任意 (= 自由) である。

例えば
#define FOREVER for ( ; ; )
の場合には、無限ループを表す 新しい NAME、FOREVER が 定義されている。(for ( ; ; ) で 1つの text を 表す)

(仮)引数つきの マクロの定義も 可能である。

#define MAX(A, B) ((A) > (B) ? (A) : (B))

このとき
x = MAX(p + q, r + s);
は (マクロによって) 次のように 置き換えられる。

x = ((p + q) > (r + s) ? (p + q) : (r + s));

関数の場合と違い、データ型が異なっていても、別の NAME で 定義する必要はない。このマクロでは どんなデータ型であっても 適用が可能となる。

(ただし このように 仮引数を使うと) 式が 二重に評価されるので、インクリメント演算子や 入出力の場合には 副作用がでて よくない (ときがある)。

(また) カッコを用いる場合、どのような順序で 評価されていくかを、注意深く 考えておく必要がある。

実用例として、ヘッダファイル では、getchar と putchar は マクロとして 定義されている。(なぜなら) 文字を処理するたびに 関数呼び出しを行なうという 実行時のオーバーヘッドを 避ける (ためにである)。

(他にも) の中の関数群は 普通、マクロとして 定義されている。

NAME は #undef を用いて (コードの途中で) 未定義にすることもできる。

(こんなことは やらないと思うが) getchar を 未定義にして、同じ名前で 別の関数 getchar を 使いたい場合には、
#undef getchar
int getchar(void) { ... }
というふうに すればいい。(p107-110)

(追記) 一部訂正 05/2/26