#define プリプロセッサディレクティブには、単純な文字列を置き換えのほかに、関数形式のマクロ(function-like macro)、すなわちマクロ関数という使い方もある。
マクロ関数を使うと、プリプロセッサによって置き換えられる文字列に引数を渡すことができる。
#incluce #define SUM(i, j) i + j int main(void) { int sum; sum = SUM(10, 20); printf("%d\n", sum); return 0; }
11行目の
sum = SUM(10, 20);
はプリプロセッサによって
sum = 10 + 20;
に置き換えられる。この例は単純だけど、プログラム中に同じ処理が何度も出てくるときには、マクロ関数が使える。もちろん普通の関数にしてもいいけど、関数呼び出しのオーバーヘッドがないぶん有利。ただ、マクロ関数が出てくる場所全部に同様のコードが複製されるので、プログラムが大きくなる。
……と、こう書くとマクロ関数にしたほうがいいのか、普通の関数のほうがいいのかよくわかんないなあ。
可能な限り関数にする方が型をキッチリと明記できるので安全で綺麗だというのが現代的な認識だと思います。 関数の呼出しコストが気になる場合はインライン関数にすることも出来ますし、賢いコンパイラなら効率の良いように勝手にインライン化してくれることもありますので呼出し効率を理由にマクロを選ぶ必要はほぼ無いです。
とはいうものの、あまり賢くないコンパイラも世の中には多いのでそこらはケースバイケースですが。
マクロにするのは評価順序を制御したいときなどが考えられます。 例えば関数 f, g, h があるとき f(g(),h()) という式は g() と h() の両方が呼出された後に f が呼出されることが保証されますが、 g() の返す結果によっては h() を呼出さなくてよいような場面ではマクロにする意味があるでしょう。 (ちなみに、 f が関数であるとき f(g(),h()) という式では g() と h() のどちらが先に呼出されるかは保証されません)
具体例を挙げます。
#include
#define logand_macro(x,y) ((x)&&(y))
inline int logand_func(int x, int y) { return x&&y; }
int foo(int x) { printf(“call foo with %d\n”, x); return x; }
int main(void) {
printf(“%d\n”, logand_macro(0, foo(1)));
printf(“%d\n”, logand_func(0, foo(1)));
return 0;
}
このとき、マクロ版も関数版もやっていることは論理和 (&&) をとっていることにかわりないですが、マクロ版では foo(1) が呼出されないことがわかります。
コメントありがとうございます。
マクロよりも関数のほうが分かりやすい、とぼくは感じました。通常は普通の関数にしておけば良さそうですね。