C言語講座10回目。
C言語のプリプロセッサは、実に強力です。
今回は、プリプロセッサの応用編で、プリプロセッサいわゆるマクロで関数を作ってみる、という試みです。
ただし、副作用もあるので、利用には注意が必要です。
関数マクロの使い方
マクロは、極めると便利この上ありません。ただ使うには注意は必要です。
定数を示すためのマクロは一般的であり、どこのヘッダに定義されているかさえ分かれば特に問題はありません。
一方、関数マクロ(引数付きの処理を行うマクロ)は、凝りすぎには注意が必要です。
というのも、プログラムを書いた本人以外にはわからないものになってしまう危険性を秘めているからです。
その気になれば何でもできてしまいます。
例えば、階乗計算の関数をマクロで書くこともできてしまいます。
#define KaijyoKeisan(ans, n) \ do {\ int i, x;\ x = 1;\ for (i = 1; i <= n; i++) {\ x = x * i;\ }\ (ans) = x;\ } while (0)
使いかたも通常の関数とそう変わりは無く、2行以上にまたがるときは、¥で連結すれば、大きなプログラムも書けてしまいます。
また、do ~ while (0) は、最後に関数らしく、セミコロンを要求するためのテクニックであり、別に{}だけでも大差はない。
int main() { int n, x; char buf[128]; printf("N : "); gets(buf); if ((n = atoi(buf)) < 0) { printf("Input Error!\n"); return 1; } KaijyoKeisan(x, n); printf("%d! = %d\n", n, x); return 0; }
このようなことが、できるという例をしめしました。
ただし、マクロは使い方次第で問題がおきてしまうので、非常に便利なのですが、使い方をよく理解したうえで、特定のものに限定しておいた方が無難です。
(1)デバッグ関係
(2)共通関数の名称再定義
(3)共通の決まりきった定義
(4)簡単な処理
たとえば、
(1)の例は、
int main()
{
....
DEBUG_TRACE("trace point 1");
....
DEBUG_TRACE("trace point 2");
....
}
(2)の例は、
#define CheckTime(t, x) MyCheckDateTime((t),(x),CHECK_TIME)
として、プログラム中では、CheckTime を使用するなどです。これによって、わざわざ関数をかかずにすみます。
ただし、デバッガを利用する場合は、CheckTimeという関数はなく、MyCheckDataTimeという関数になるので、注意が必要です。
(3)の例は、たとえば、プログラムの先頭で、
DEFINE_APP(MyAppName)
とかやると、アプリケーション必要な定義を展開してくれるようなマクロを作成して、おまじないのように呼ぶような場合です。
(4)の例は、よく入門書等にものっているやつで、
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
など、工夫すればいくらでも使い道があります。
なお、関数マクロの引数は、引数で演算したりすることも考えて()を付けておく癖をつけておいたほうがいいです。
また、マクロはプリプロセッサが展開するだけなので、使う際はマクロで展開されることを意識して、引数でインクリメントする等は避けたほうが無難です。
例えば、
MAX(a++, b);
=> ((a++) > (b) ? (a++) : (b));
一度しかインクリメントしていないつもりが、展開されると2度インクリメントされています。
このように、関数マクロには副作用も多いため、C++からは、関数を内部展開してくれる、inline関数というのが実装されました。
inline int MAX(int a, int b) { return (a > b ? a : b); }
これは、関数のため、先ほど書いたような副作用は発生しません。
今回はここまでにします。
C言語の話は、この後も何回かに分けて投稿しますので、興味のある方は引き続きよろしくお願いします。