危険なswapマクロ関数

二つの値を交換するswap関数のマクロ版を考えます。
#define swap(type,a,b) do { type t; t=a; a=b; b=t; } while(0)
というのが一般的な方法です。(※)

このswapマクロは、様々な型に適用できるので非常に便利に思えます(ついでに 普通の関数として実装したswapのようにポインタを渡す必要もありません)。ほ とんどの場合で、このマクロは正しく動作します。

しかし、次のようなコードを考えてみましょう。

	int a[5] = { 0, 0, 0, 0, 0 };
	int i = 3;
	
	swap(int,i,a[i]);
4番目の配列要素 a[3](= 0) と インデックスi(= 3)を入れ換えます。その結果、 i = 0, a[3] = 3になると予想されます。ところが実際にはそうなりません。 関数マクロの引数は遅延評価されてしまうからです。

マクロの本体が展開される時、引数の名前がそのまま本体の仮引数と置き換えら れます(これを名前呼出し(call by name)と呼びます)。さきほどの例ですと、 swapマクロは次のように展開されます。(見やすくするために適当に整形してい ます。)

1	do {
2	    int t;
3
4	    t    = i;
5	    i    = a[i];
6	    a[i] = t;
7
8	} while (0)
5行目の i = a[i] で、iに0が代入されてしまうので、6行目は、a[0] = 3 となっ てしまいます。結局、swap(int,i,a[i])は正しく動作しないのです。 (swap(int,a[i],i)は正しい結果になります。)関数形式のマクロ(あるいは名前呼 出し機構を持った関数)を使って、swap関数を正しく実装することは不可能なこと が知られています。

マクロ関数の危険性については、たとえば、#define sqrt(x) ((x)*(x))と定義し た時の、sqrt(n++);や、#define sqrt(x) x*x としたときの、sqrt(n+n); のような問題はよく知られていますが、ここで挙げたswapの場合はあまり取り挙げ られない話題のような気がします。プログラミング関係のウェブページを眺めてい ると、結構swapマクロを注意なしに紹介しているので気にかかるところです。

まあ、swap(i,a[i])を使う場面なんてあまり考えられませんので気にすることも無 いと思うかもしれませんが、このように正しく動かないことがあり得るということ は頭の片隅に入れておきましょう。

(※)
本体が、
    { type t; t=a; a=b; b=t; }  /* --- (1) */
でなく
    do { type t; t=a; a=b; b=t; } while(0)
と余計なdo〜whileで囲まれているのは、以下の場合に対処するためです。
    if (exp)
        swap(int, x, y);
    else
        swap(int, x, z);
この場合、(1)のようにマクロが定義されていると、
    if (exp)
        { int t; t=x; x=y; y=t; };
    else
        { int t; t=x; x=z; z=t; };
と展開されてしまい、文法エラーになってしまいます。 (elseが浮いてしまいます。)

おまけ

以下のような、(非常にトリッキーな)swapマクロの実装を見かけるかもしれませ ん。
#define swap(a,b) (a ^= b, b ^= a, a ^= b)
この方法だと、余計な一時変数を使わずに値を交換することができます。ただし く値が交換できるかどうか確認してみましょう。
	a		b
-----------------------------------
step1	a^b		b		(a ^= b)
step2	a^b		b^(a^b) = a	(b ^= a)
step3	(a^b)^a	= b	a		(a ^= b)
たしかに、変数aとbの値が交換されます。しかし、swap(a,a)としたらどうなる でしょうか?
	a		b
-----------------------------------
step1	a^a = 0		0		(a ^= b)
step2	0		0^0 = a		(b ^= a)
step3	0^0 = 0		0		(a ^= b)
このように、途中で値が0になってしまいまい、元の値が消滅してしまいます。
トップページ

$Id: swap.html,v 1.1.1.1 2003/10/09 05:24:13 hattori Exp $
Copyright(C) 2000 by Kenta HATTORI, All Rights Reserved.