あなたは、配列の要素数や、for文の繰り返し回数などの数値を、直接プログラムに書いていませんか?
もし、そうなら今すぐ、それをやめましょう。
なぜなら、プログラムの意味もわかりにくくなりますし、修正するときに複数個所に及ぶとミスも増えるからです。
そんなときに、便利なのがマクロ定義です。
マクロ定義の仕方
マクロ定義の書き方ですが、例えば、以下のようにすればいいです。
#define MAX 10
これで、「MAX」という文字列は「10」という文字列で定義されたということになります。
したがって、この定義以降、同一ファイル内で、「MAX」と書かれた箇所は「10」に置き換えられます。
大抵の場合、このマクロ定義は大文字で書かれます。小文字でもいいのでしょうが、その場合、通常の変数とマクロ定義との区別がつきにくいでしょう。
MAXというのはあくまで固定データですので、
MAX = 10;
というように、代入することはできません。
また、関数のように宣言することもできます。
#define CALC(a, b) (a + b)
このように定義すると、CALCを記述した箇所は「(a + b)」の演算が行われます。ここで注目してほしいのは、関数と違って、型の指定が不要な点です。
通常の関数は、引数に型を宣言する必要がありますが、マクロ定義の場合は、それが不要というのが特徴です。すなわち、どんな型でもよいということです。
演算だけでなく、条件文もマクロ定義することができます。
#define DIVISION(a, b, ret) \ if (b == 0) {\ ret = 32767;\ } else {\ ret = a / b;\ }\
割り算を行うマクロです。「0」では割れないので、条件文を入れました。複数行になる場合は、各行の末尾にバックスラッシュを記述します。
これで、割り算を実行したい箇所に、このマクロを記述すれば、割る数を気にすることなく、プログラムを記述することができます。
マクロ定義の使い方
まず、定数定義のためのマクロですが、以下のように、配列の要素数の定義や、for文の繰り返し回数を定義するのに、よく使われます。
#include <stdio.h> #define MAX 10 void main(void) { int array[MAX]; int i; int sum; printf("データ入力\n"); for (i = 0; i < MAX; i++) { printf("array[%d] = ", i); scanf("%d", &array[i]); } sum = 0; for (i = 0; i < MAX; i++) { sum += array[i]; } printf("データ合計\n"); printf("sum = %d\n", sum); }
配列arrayの要素数は10、for文の繰り返し回数も10となります。
配列の要素数とfor文の繰り返し回数は、for文の処理に配列が関係している場合は、同じになることが多いため、マクロ定義しておくと便利です。
万一、要素数に変更があっても、マクロの定義を変えるだけで済みます。
マクロ定義を記述する位置は、それを使用する箇所より、上部に記述しなくてはいけません。通常は、#inclueの後に書くので、それほど気にする必要はないでしょう。
次に、さきほどの割り算のマクロ定義の使ってみます。
#include <stdio.h> #define DIVISION(a, b, ret) \ if (b == 0) {\ ret = 32767;\ } else {\ ret = a / b;\ }\ void main(void) { double x, y; double ans; printf("データ入力\n"); printf("x = "); scanf("%lf", &x); printf("y = "); scanf("%lf", &y); /* 割り算を行うマクロ */ DIVISION(x, y, ans); printf("x / y = %lf\n", ans); }
マクロは関数ではなく、置き換えになるので、プログラムの見た目は短いかもしれませんが、実際には、マクロ内のif文が展開されます。
したがって、処理速度は失われませんが、いくつも記述すると、プログラムのサイズが大きくなってしまいます。
マクロ定義の注意点
マクロ定義は、便利なので使用するべきですが、もちろん注意しなくてはならない点もありますので、紹介しておきます。
先ほども、お話ししましたが、マクロを使う前に、マクロを定義する必要があります。これは、通常の変数や関数でも同じですよね。
それから、定義したファイルのみしか利用できません。これも変数や関数でも同じですね。ただ、変数や関数は、externを用いれば、利用できます。
マクロはそれもできませんので、もし、その必要があるなら、マクロ定義用のヘッダファイルを作ります。
そこに、マクロ定義を記述します。そして、必要なファイルでヘッダファイルを#includeすれば、複数ファイルでも利用できます。
関数のように定義した割り算のマクロですが、こちらは引数の型は何でもいいことは、お話ししました。
先ほどの例のプログラムを見てもらえれば、わかると思うのですが、関数と使い方が少し違うことに気付いているでしょうか?
第3引数がポインタでないところです。
通常の関数では、ポインタで渡さないと、演算結果が失われますよね。しかし、マクロの場合は、処理の置き換えになるため、ポインタの必要がないのです。
ここが関数とマクロの違いです。
もう一つ、マクロで注意してもらいたいことがあります。先ほど、マクロは処理の置き換えとお伝えしました。
したがって、演算の優先順位、型変換が思わぬ失敗を招くかもしれません。
次の例を見てください。
#include <stdio.h> #define CALC_MULT(a, b) (a * b) void main(void) { int x, y; int ans; printf("データ入力\n"); printf("x = "); scanf("%lf", &x); printf("y = "); scanf("%lf", &y); /* 掛け算 */ ans = CALC_MULT(x + 1, y); printf("(x + 1) * y = %d\n", ans); }
「(x + 1) * y」という演算をさせたいと思って、CALC_MULTのマクロを使いました。しかし、これでは正しい結果は得られません。
それは、
ans = x + 1 * y;
となってしまうからです。
カッコ()がついていないために、結果が違ってしまうのです。これを解決するには、
#define CALC_MULT(a, b) ((a) * (b))
とします。これだと、処理の置き換えで、
ans = (x + 1) * (y);
となり、思っている結果が得られます。
まとめ
マクロ定義について、お話ししました。定義の仕方や使い方は、そんなに難しくないと思います。
定数の定義なんかは、マクロ定義を使うことをお勧めします。
そうすることで、可読性、修正時の対応が格段に上がるからです。
処理をマクロにする場合は、注意点さえ気をつければ、十分に有効なので、ぜひ使ってみてください。
ただし、使用個所が多すぎると、プログラムのサイズが大きくなってしまうので、そこだけ気を付けてください。