C言語 | プリプロセス | 関数形式マクロ - #define

関数形式マクロは、#define ディレクティブによって引数を受け取り、任意のテキストへ展開する。コンパイル前に展開されるため、関数のように使えても関数呼び出しのオーバーヘッドは発生しない。

引数を持つマクロ展開

関数は再利用性を高めるが、小さな関数でも引数の受け渡しと呼び出し元への復帰が必要になる。単純な計算では、マクロを使って式をコンパイル時に直接展開できる。

関数形式マクロの #define ディレクティブ

#define 識別子(引数1, 引数2 ...) トークン列

たとえば、次のマクロを考える。

#define ADD(a , b) a + b

ADD(10, 20) はコンパイル前に 10 + 20 へ置換される。

コード1

#include <stdio.h>
#define MUL(multiplicand , multiplier) multiplicand * multiplier

int main() {
 printf("5 * 5 = %d\n" , MUL(5 , 5));
 return 0;
}

マクロ展開はテキスト置換なので、演算子の優先順位によって予期しない結果になることがある。

コード2

#include <stdio.h>

#define MUL(multiplicand , multiplier) multiplicand * multiplier
int mul(int multiplicand , int multiplier) {
 return multiplicand * multiplier;
}

int main() {
 printf("macro: MUL(3 + 2 , 5) = %d\n" , MUL(3 + 2 , 5));
 printf("function: mul(3 + 2 , 5) = %d\n" , mul(3 + 2 , 5));
 return 0;
}

通常の関数は 25 を返すが、マクロは 3 + 2 * 5 に展開されるため 13 を返す。各引数と式全体を括弧で保護する必要がある。

#define MUL(multiplicand , multiplier) ((multiplicand) * (multiplier))

マクロは簡潔なビット演算にも役立つ。

コード3

#include <stdio.h>

typedef unsigned char BYTE;
#define RGB(r , g , b) ((BYTE)(r) << 16) | ((BYTE)(g) << 8) | (BYTE)(b)
#define RED(color) (BYTE)((color) >> 16)
#define GREEN(color) (BYTE)((color) >> 8)
#define BLUE(color) (BYTE)(color)

int main() {
 int color = RGB(0xFF , 0xEE , 0xAA);
 printf("R = %X : G = %X : B = %X\n" ,
   RED(color) , GREEN(color) , BLUE(color));
 return 0;
}

この例では、赤、緑、青の成分を1つの整数に格納し、マクロで各バイトを取り出す。関数形式マクロは小さな変換や繰り返し呼び出しの簡略化に便利だが、常にテキスト展開であることを意識する必要がある。