オブジェクト類似マクロよりも複雑な関数類似マクロ定義では、括弧内の仮パラメーターの名前を、 コンマで区切って宣言します。仮パラメーター・リストは空であってもかまいません。 そのようなマクロは引き数を取らない関数をシミュレートするために使用できます。C では、数が可変の引き数を持つ関数類似のマクロのサポートが追加されています。
C++ では、C との互換性のための言語拡張として、数が可変の引き数を持つ関数類似マクロがサポートされています。
移植性のため、1 つのマクロに は、パラメーターが 31 を超えないようにする必要があります。 このパラメーター・リストの最後には、省略符号 (...) を使用できます。 この場合、ID __VA_ARGS__ を置換リストに挿入することができます。
マクロ引き数は空 (ゼロ個のプリプロセス・トークンで構成) であってもかまいません。例えば、次のような場合です。
#define SUM(a,b,c) a + b + c SUM(1,,3) /* No error message. 1 is substituted for a, 3 is substituted for c. */この言語フィーチャーは、C++ の直交拡張です。
ID リストが省略符号で終了していない場合は、マクロ起動における引き数の数は、 対応するマクロ定義内のパラメーターの数と同じでなければなりません。指定された 引き数がすべて置換された後に残る引き数はすべて (分離するコンマを含む)、パラメーターの置換時に可変引き数と呼ばれる 1 つの引き数に結合されます。可変引き数は、パラメーター・リスト内に現れる すべての ID __VA_ARGS__ と置き換わります。 次の例は、このことを示したものです。
#define debug(...) fprintf(stderr, __VA_ARGS__) debug("flag"); /* Becomes fprintf(stderr, "flag"); */
マクロ起動引き数リストにおけるコンマは、以下の場合には、分離文字として作用しません。
以下の行は、a と b という 2 つのパラメーター と置換トークン (a + b) を持つものとして マクロ SUM を定義します。
#define SUM(a,b) (a + b)
この定義により、プリプロセッサーは以下のステートメントを変更すること になります (そのステートメントが前の定義の後に現れる場合)。
c = SUM(x,y); c = d * SUM(x,y);
プリプロセッサーの出力においては、これらのステートメントは次のように表示されます。
c = (x + y); c = d * (x + y);
置換テキストが正しく評価されるようにするためには、小括弧を使用してください。例えば、
#define SQR(c) ((c) * (c))
上記の定義では、式を正しく評価するため、定義内の各パラメーター c のまわりに小括弧を必要とします。
y = SQR(a + b);
プリプロセッサーはこのステートメントを次のように展開します。
y = ((a + b) * (a + b));
定義内に小括弧がないと、プリプロセッサーは評価の正しい順序を保てず、プリプロセッサーの出力は 次のようになります。
y = (a + b * a + b);
# および ## 演算子の引き数は、 関数類似マクロのパラメーターの置換の前に 変換されます。
プリプロセッサー ID は、いったん定義されると定義されたままとなり、 言語のスコープ決定規則とは関係なく、有効となります。 マクロ定義のスコープは定義から始まり、 対応する #undef ディレクティブに遭遇するまで終了しません。 対応する #undef ディレクティブがない場合、そのマクロ定義の スコープは、変換単位の終わりまで続きます。
再帰マクロは、完全には展開されません。 例えば、以下の定義
#define x(a,b) x(a+1,b+1) + 4
は、
x(20,10)
を、以下のように展開します。
x(20+1,10+1) + 4
マクロ x を、それ自体の中で繰り返し展開しようとするよりも、 上述の展開を行います。 マクロ x が展開された後で、そのマクロは、関数 x() の呼び出しとなります。
置換トークンを指定するのに、定義は必須ではありません。 以下の定義は、現在のファイル内のこれ以降の行から、 トークン debug のすべてのインスタンスを除去します。
#define debug
2 番目のプリプロセッサー #define ディレクティブを用いて、 定義済みの ID またはマクロの定義を変更することができます。 ただし、2 番目のプリプロセッサー #define ディレクティブの前に、 プリプロセッサー #undef ディレクティブがある場合に限ります。 #undef ディレクティブは、最初の定義を無効にして、 同じ ID を再定義で使用できるようにします。
プログラムのテキストの中については、プリプロセッサーはマクロ起動のための 文字定数またはストリング定数のスキャンを行いません。
#define ディレクティブの例
以下のプログラムには、2 つのマクロ定義と、その定義されている両方のマクロを 参照するマクロ起動が含まれています。
/** ** This example illustrates #define directives. **/ #include <stdio.h> #define SQR(s) ((s) * (s)) #define PRNT(a,b) ¥ printf("value 1 = %d¥n", a); ¥ printf("value 2 = %d¥n", b) ; int main(void) { int x = 2; int y = 3; PRNT(SQR(x),y); return(0); }
プリプロセッサーによって解釈された後、このプログラムは、 以下のものに等価のコードによって置き換えられます。
#include <stdio.h> int main(void) { int x = 2; int y = 3; printf("value 1 = %d¥n", ( (x) * (x) ) ); printf("value 2 = %d¥n", y); return(0); }
このプログラムの出力は次のようになります。
value 1 = 4 value 2 = 3
関連参照