<戻る  目次  進む>  
          <戻 進> 雑誌 白黒 明暗   フォント 既定 ゴシック 明朝 手書き   サイズ   標準  


   簡易的な関数の実現   

  1. 第1項:#define疑似命令の高度な機能
  2. 第2項:マクロという簡易関数
  3. 第3項:副作用の恐怖

[1]#define疑似命令の高度な機能

第1節で紹介した#define疑似命令は定数を宣言する疑似命令ですが、
実は、それ以上に高度な機能を持ち合わせています。
#define疑似命令による定数は、単なる置き換えによって実現されていますが、
これを利用すると特殊な処理を行わせることも可能です。

例えば、変数の中身を画面に表示するにはprintf文を次のように使用します。


printf("temp = %d\n",temp);
しかし、変数tempの値をあちこちで表示する必要がある場合に、
この文を毎回打ち込むのが面倒であれば、#define疑似命令で置き換えることが出来ます。

#include <stdio.h>

#define PRINT_TEMP printf("temp = %d\n",temp)

int main(void)
{
	int temp = 100;
	PRINT_TEMP;
	return 0;
}
このプログラムの実行結果は、次の通りになります。

temp = 100
このプログラムのトリックは、#define疑似命令の定義内容にあります。
#define疑似命令の機能は単なる置き換えで、数値でなくとも何でも置き換え出来ます。
ここでは、PRINT_TEMP が変数を表示するprintf文にそのまま置き換わっています。
つまり、プログラム的にはその部分にprintf文を書いているのと全く同じです。

この様な使い方で#define疑似命令を乱用するとプログラムがわかりにくくなりますが、
#define疑似命令の強力な機能の1つとして知っておくことは必要です。

目次に戻る


[2]マクロという簡易関数

前項で見た通り#define疑似命令は非常に強力ですが、
実は、#define疑似命令には更に強力な機能が備わっています。
#define疑似命令で簡単な関数を作ってしまうことが可能です。

#define疑似命令では、名前の後に()で文字を指定すると、
以後の置き換える内容で同じアルファベットの部分を置き換えることが出来ます。
例えば、次の#define疑似命令は、指定されたint型の変数を画面に表示します。


#define PRINTM(X) printf("%d\n",X)
次のプログラムは、先ほどの#define疑似命令を使う例です。

#include <stdio.h>

#define PRINTM(X) printf("%d\n",X)

int main(void)
{
	int a1 = 100,a2 = 50;
	PRINTM(a1);
	PRINTM(a2);
	return 0;
}
このプログラムの実行結果は、次の通りになります。

100
50
ここの#define疑似命令では、名前の後の()の中でXが指定されています。
これによって、以後の置き換え文の中のXは、
#define疑似命令を使用した時の()内の記号に置き換えられます。
先ほどの例では、Xがa1やa2に置き換わった内容になったのです。

この機能や使い方を見る限り、これはまるで関数のようです。
実際、この機能は簡易的な関数の変わりに使われており、マクロと呼ばれています。


[  マクロ  ]
#define疑似命令による置き換えで式などを簡単に表現すること。
マクロの使い方は普通の関数と全く同じですが、仕組みは大きく異なります。
関数の場合、その実態は1ヶ所にあり、必要な時に呼び出されて使われます。
しかし、マクロでは、使用している場所のプログラムそれ自体が置き換わり、
呼び出しなどの作業が必要ないため、若干高速になります。

しかし、マクロを使う場所全てが置き換わるので、あまり巨大なマクロを作ると、
その為にプログラムのサイズが極端に大きくなることもあります。

その為、一般的には、マクロは、決まり切った数式などに利用されます。
例えば、次のマクロは台形の面積を求めるマクロです。


#include <stdio.h>

#define GET_TRAPEZOID_AREA(A,B,H) (A + B) * H / 2

int main(void)
{
	int up,down,h,s;
	printf("上底、下底、高さ:");
	scanf("%d,%d,%d",&up,&down,&h);
	s = GET_TRAPEZOID_AREA(up,down,h);
	printf("面積:%d\n",s);
	return 0;
}
このプログラムの実行結果は次の通りになります。

上底、下底、高さ:5,10,8
面積:60
この様にすれば、決まり切った数式を何度も入力する必要がなくなります。

目次に戻る


[3]副作用の恐怖

#define疑似命令によるマクロは手軽で便利なのですが、
使い方を間違えると思わぬ現象に遭遇することがあります。

例えば、前回の台形の面積を求めるプログラムに置いて、
なんらかの事情で、高さを常に+3しなければならない場合を考えてみます。
次のプログラムは、そのように変更してみた例です。


#include <stdio.h>

#define GET_TRAPEZOID_AREA(A,B,H) (A + B) * H / 2

int main(void)
{
	int up,down,h,s;
	printf("上底、下底、高さ:");
	scanf("%d,%d,%d",&up,&down,&h);
	s = GET_TRAPEZOID_AREA(up,down,h + 3);
	printf("面積:%d\n",s);
	return 0;
}
このプログラムの実行結果は次の通りになります。

上底、下底、高さ:5,10,5
面積:76
高さは+3されているので、結果は前回と同じになるはずなのですが、
何故か、答えは76と表示されてしまっています。

これは、#define疑似命令は、単なる置き換え命令でしかないため、
GET_TRAPEZOID_AREA(up,down,h + 3) を (A + B) * H / 2 と置き換えると、
(up + down) * h + 3 / 2 と言う式になってしまいます。
これでは、高さに3が加わるのではなくなってしまい、計算がおかしくなります。
この様に、置き換えで予期しない計算結果になることをマクロの副作用と呼びます。

これを解決する方法は2つあります。1つは、呼び出し時にかっこをつけることです。


GET_TRAPEZOID_AREA(up,down,(h + 3));
と、かっこをつけておけば先に高さに3が加わるので、正常に計算出来ます。

もう1つの方法は、マクロの方にかっこをつけておく方法です。
マクロで使われている置き換え部分全てにかっこをつけ、更にマクロ全体にもつけます。


#define GET_TRAPEZOID_AREA(A,B,H) (((A) + (B)) * (H) / 2)
この様にすれば、全ての数値にかっこが付いているので大丈夫です。

しかし、気をつけて使用するのは面倒ですし、うっかり忘れてしまうかもしれません。
その為、マクロはあまり多用しない方が良いとされています。
#define疑似命令は定数の宣言にのみ使用して、
数式などの計算には出来る限り関数を使用する方が良いでしょう。

目次に戻る


<−前に戻る  先頭に戻る  次へ進む−>