#define ディレクティブ

プリプロセッサー定義ディレクティブ は、これ以降のマクロ の出現を、指定された置換トークンに置き換えるようプリプロセッサーに指示 します。

構文図を読む構文図をスキップする
#define ディレクティブの構文

>>-#--define--identifier---------------------------------------->

                                          .----------------.   
                                          V                |   
>--+-----------------------------------+----+------------+-+---><
   '-(--+-identifier-list---------+--)-'    +-identifier-+     
        +-identifier-list--,--...-+         '-character--'     
        '-...---------------------'                            

以下に、#defineconst 型修飾子との違いのいくつかを示 します。
  • #define ディレクティブは、数値定数、文字定数、およびス トリング定数を作成するために使用できますが、const オブジェクトは任意 の型を宣言できます。
  • const オブジェクトは変数のスコープ規則に従いますが、 #define を使用して作成される定数はそうではありません。
  • const オブジェクトと違って、マクロはインラインで展開されるの で、マクロの値は、コンパイラーが使用する中間ソース・コードには含まれません。 インライン展開をすると、デバッガーはマクロ値を使用できなくなります。
  • C のみ マクロは、ビット・フィールド長などの定数式の中で使用できますが、const オブジェクトは使用できません。

オブジェクト類似マクロ

オブジェクト類似マクロ定義 は、 単一の ID を指定された置換トークンに置き換えます。 以下のオブジェクト類似の定義を使用すると、プリプロセッサーは、ID COUNT のこれ以降のすべてのインスタンスを、定数 1000 に置き換えます。
#define COUNT 1000
次のステートメント
int arry[COUNT];
が、この定義の後、かつ定義と同じファイル内に現れると、プリプロセッサーは、 このステートメントをプリプロセッサーの出力で、以下のステートメントのように変更します。
int arry[1000];

他の定義が ID COUNT を参照することができます。
#define MAX_COUNT COUNT + 100

プリプロセッサーは、MAX_COUNT のこれ以降の出現を COUNT + 100 で置き換えます。 次に、プリプロセッサーは、これを 1000 + 100 で置き換えます。

マクロ展開によって部分的に構築された番号が作成された場合、プリプロセッサーは、 その結果を単一の値であるとは見なしません。 例えば、以下の結果は 10.2 という値にはならず、構文エラーになります。
#define a 10
a.2

関数類似マクロ

関数類似マクロは、オブジェクト類似マクロより複雑であって、その定義は、 仮パラメーターの名前をコンマで区切って、括弧で囲んで宣言します。 空の正式パラメーター・リストは有効です。そのようなマクロを使用して、引数を取らない関数をシミュレートすることができます。 C99 は、可変個の引数を持つ関数類似マクロのサポートを追加しました。

関数類似マクロの定義
小括弧に囲まれたパラメーター・リストおよび置換トークンが後ろに続く ID。 パラメーターを置換コード内に組み込みます。 空白文字で、ID (マクロの名前) とパラメーター・リストの左括弧とを分離することはできません。 コンマで各パラメーターを区切る必要があります。

移植性のため、1 つのマクロに は、パラメーターが 31 を超えないようにする必要があります。パラメーター・リストは、仮パラメーターとして、省略符号 (…) で終了することができます。 この場合には、置換リストに ID __VA_ARGS__ を使用できます。

関数類似マクロの呼び出し
小括弧に入れられた、コンマで区切られた引数のリストが後に続く ID。 引数の数は、マクロ定義内のパラメーター・リストが省略符号で終了 するのでなければ、定義内のパラメーターの数と一致しなければなりません。 定義内のパラメーター・リストが省略符号で終了する場合、マクロ呼び出しでの引数 の数は、定義内のパラメーターの数を超えるはずです。 この超過部分は、後続引数 と呼ばれます。 プリプロセッサーは、関数類似マクロの呼び出しを確認すると、引数の置換を行います。 置換コード内のパラメーターは、対応する引数に置き換えられます。 マクロ定義で後続引数が許可されている場合、その引数は 間にコンマが挿入されて、その引数全体が単一の引数であるかのように、 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. */
ID リストが省略符号で終了していない場合、マクロ呼 び出しの中の引数の数は、対応するマクロ定義の中のパラメーターの数と同じでなければな りません。 パラメーターの置換時、指定されたすべての引数 (区切り文字のコンマも 含む) が置換された後に残っている引数は、変数引数と呼ばれる 1 つの引数にまとめら れます。 変数引数は、置換リストの中の ID __VA_ARGS__ のすべてのオカレン スを置き換えます。 次の例は、このことを例示しています。
#define debug(...)   fprintf(stderr, __VA_ARGS__)

debug("flag");     /*   Becomes fprintf(stderr, "flag");   */
マクロ呼び出し引数リストにおけるコンマは、以下の場合には、分離文字として作用しません。
  • 文字定数内にある。
  • ストリング・リテラル内にある。
  • 小括弧で囲まれている。
次の行は、ab という 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 を再定義で使用できるようにします。

プログラムのテキストの中については、プリプロセッサーはマクロ呼び出しのための 文字定数またはストリング定数のスキャンを行いません。

以下のプログラム例には、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

IBM 拡張
可変数引数マクロ拡張機能

可変数引数マクロ (variadic macro) 拡張機能は、可変個の引数を持つマクロに関連した、C99 の 2 つの拡張機能です。これらの拡張の 1 つは、変数引数 ID を __VA_ARGS__ からユーザー定義の ID に名前変更するためのメカニズムです。 もう 1 つの拡張機能は、変数引数が指定されていない場合に、可変数引数マクロ内のダングリング・コンマを除去するための手段を提供します。 どちらの拡張機能も、GNU C を使用して開発された プログラムの移植を容易にするためにインプリメントされています。

以下の例は、__VA_ARGS__ の代わりに ID を使用する方法を示しています。 マクロ debug の最初の定義は、__VA_ARGS__ の通常の使用方法の例を示しています。 2 番目の定義は、__VA_ARGS__ の代わりの ID として args を使用する方法を示しています。
#define debug1(format, ...)  printf(format, ## __VA_ARGS__)
#define debug2(format, args ...)  printf(format, ## args)
呼び出し マクロ展開の結果
debug1("Hello %s/n"."World"); printf("Hello %s/n"."World");
debug2("Hello %s/n"."World"); printf("Hello %s/n"."World");

プリプロセッサーは、関数マクロへの変数引数が省略されるかまたは空であり、関数マクロ定義内の変数引数 ID の前に ## を伴うコンマがある場合に、末尾のコンマを除去します。

IBM 拡張