C言語でマクロを使用する際の注意点(気をつけないと予期せぬバグ発生の原因となりえる)について書きます。
まずは、以下のコードを見てください。
1 : #include <stdio.h>
2 :
3 : #define CUBE(NUM) (NUM * NUM * NUM)
4 :
5 : int main(void)
6 : {
7 : int num = 4;
8 :
9 : printf("%d の三乗は %d\n", num, CUBE(num));
10 :
11 : return 0;
12 : }
結果)
3行目で定義しているCUBEマクロは、その名の示すとおり三乗を求めるマクロです。
一見正常に動作している様に見えますが、このマクロ、実はバグの温床になっています。
以下のコードを見てください。(※ 冒頭のコードの一部を修正したものです)
1 : #include <stdio.h>
2 :
3 : #define CUBE(NUM) (NUM * NUM * NUM)
4 :
5 : int main(void)
6 : {
7 : printf("%d の三乗は %d\n", 10 + 20, CUBE(10 + 20) );
8 :
9 : return 0;
10 : }
結果)
30の三乗という事で、 30 x 30 x 30 = 27000 が表示されるかと思いきや、430が表示されてしまいました。
この理由は、CUBEマクロを展開してみれば直ぐに分かります。
CUBE(10 + 20) ⇒ (10 + 20 * 10 + 20 * 10 + 20) ⇒ (10 + 200 + 200 + 20) ⇒ 430
演算子には優先順位が有る為、
このように
「値を計算するマクロ中に、数値と演算子のセットを入れると、予期せぬバグを引き起こす」
可能性があります。
これを防止する為には、「マクロへ演算子を渡さない」という鉄の掟を作るか(← 笑)、以下の様にマクロを修正します。
#define CUBE(NUM) ((NUM) * (NUM) * (NUM))
つまり、マクロに渡された値を、括弧で囲むという事です。
1 : #include <stdio.h>
2 :
3 : #define CUBE(NUM) ((NUM) * (NUM) * (NUM))
4 :
5 : int main(void)
6 : {
7 : printf("%d の三乗は %d\n", 10 + 20, CUBE(10 + 20) );
8 :
9 : return 0;
10 : }
結果)
はい、一件落着~~♪
・・・と思いきや、実はこの、値を括弧で囲ったマクロでも、バグの温床になっています。
1 : #include <stdio.h>
2 :
3 : #define CUBE(NUM) ((NUM) * (NUM) * (NUM))
4 :
5 : int main(void)
6 : {
7 : int i = 2;
8 :
9 : printf("%d の三乗は %d\n", i, CUBE(++i) );
10 :
11 : return 0;
12 : }
結果)
9行目のコードを展開すると、以下の様になります。
printf("%d の三乗は %d\n", i, ((++i) * (++i) * (++i)) );
副作用完了点までの評価順序は未定義の為、この場合の結果は保証されません。
つまり、処理系によって結果が異なる可能性があるという事です orz
では、この場合にどうすれば良いのかというと、(マクロからは遠ざかってしまいますが)インライン関数を使用します。
1 : #include <stdio.h>
2 :
3 : inline int cube(int num)
4 : {
5 : return num * num * num;
6 : }
7 :
8 : int main(void)
9 : {
10 : int i = 2;
11 :
12 : printf("%d の三乗は %d\n", i, cube(++i) );
13 :
14 : return 0;
15 : }
結果)
・・・まぁ、インライン関数って 開発環境によっては使用できなかったりするんですがね^^;)
とりあえず今回はここまで♪