
まず、最も基本的な関数による実装から説明を始めます。
2つ変数の値を変更する必要があるので、当然ポインタ型の引数を使います。
後は、関数の側で2つの変数を交換すればよいのですが、ここでの注意点は、
という方法では駄目だということです。void swap(int *a,int *b) { *a = *b; *b = *a; return; }
このプログラムの実行結果は、次の通りです。#include <stdio.h> void swap(int *a,int *b); int main(void) { int a = 10,b = 100; printf("a = %3d : b = %3d\n",a,b); swap(&a,&b); printf("a = %3d : b = %3d\n",a,b); return 0; } void swap(int *a,int *b) { int temp; temp = *a; *a = *b; *b = temp; return; }
後は、これを型ごとに用意すれば良いだけなのですが、
a = 10 : b = 100
a = 100 : b = 10
まず考えられたのは、次のような加減算による実装です。
原理は簡単ですね。先にbを足しておいて、bの値を保存するやり方です。#define SWAP(a,b) (a += b,b = a - b,a -= b)
もうひとつ、排他的論理和を利用した方法も考え出されました。
排他的論理和の説明はめんどくさいので省きますが、原理的には大差ありません。#define SWAP(a,b) (a ^= b,b = a ^ b,a ^= b)
この2つのマクロは、ほとんどの場合には問題なく動作するのですが、
実は、うまく動作しないケースがあり、それが最大の問題となります。
それは、同じ変数を指定した場合に起こります。
論より証拠なので、実際に試してみることにします。
このプログラムの実行結果は、次の通りです。#include <stdio.h> #define SWAP(a,b) (a += b,b = a - b,a -= b) int main(void) { int a = 98; printf("a = %3d\n",a); SWAP(a,a); printf("a = %3d\n",a); return 0; }
同じ変数であるaの値を交換したなら、結果は当然そのままなはずですが、
a = 98
a = 0
同じ変数を交換することなんて無いと思うかもしれませんが、
実際には、ソートプログラムで、array[i] と array[j] を交換する時に i == j だった、
ということはありえます。しかも、SWAPの使い道のほとんどはソートプログラムです。
この問題はどうしようもないので、一般的にはこれ以上踏み込んではいないようなのですが、
筆者はこれも無理にでも解決したくて、&&演算子による方法を使っています。
&&演算子では、直前に実行した式が偽なら、後の式は実行しない性質があります。
次のマクロは、&&演算子の性質を利用したSWAPマクロです。
このマクロでは、aとbの値が異なるときだけ、交換を実行します。#define SWAP(a,b) ((a != b) && (a += b,b = a - b,a -= b))
あるいは、演算子の性質に依存したくない場合は、3項演算子を使うこともできます。
後半の0は、文法のつじつま合わせのためのダミー値にすきません。#define SWAP(a,b) ((a != b)? (a += b,b = a - b,a -= b) : 0 )
ポインタ変数を使用しない場合ならSWAPマクロを作ることができますが、
ポインタ変数の交換だけは、どうしても最初に示した関数による実装しかありません。
そして困ったことに、ソートプログラムでポインタを交換することもあるのです。
さらに、実数値を交換するときに、情報落ちが起こる可能性があります。
たとえば、変数aの値がものすごく巨大で、bの値がものすごく極小だった場合、
a+bを計算しても、結果が丸められ、aと同じになってしまう可能性があります。
従って、正確さが求められる場合には使用できません。
結局、確実に値を交換するためには、どうしても一時変数が必要になってきます。
そのためには、マクロ内で変数を宣言する必要があります。
マクロは単なる置き換えに過ぎないので、型を指定すれば変数を宣言できます。
また、{} で囲んでブロック化すれば、変数名の衝突を避けることができます。
具体的には、次のようになります。
このマクロは、初めの引数に型を指定する必要がある分、使い勝手は多少劣りますが、#define SWAP(type,a,b) { type temp = a; a = b; b = temp; }
次のプログラムは、このマクロを使用して変数を交換する例です。
このプログラムの実行結果は、次の通りです。#include <stdio.h> #define SWAP(type,a,b) { type temp = a; a = b; b = temp; } int main(void) { int a = 10,b = 100; printf("a = %3d : b = %3d\n",a,b); SWAP(int,a,b) printf("a = %3d : b = %3d\n",a,b); return 0; }
もちろん、同じ変数を指定した場合にも問題なく動作します。
a = 10 : b = 100
a = 100 : b = 10
最後に;をつけられないのは気持ち悪いという人は
としてやればマクロだけでは文として完結しないので、#define swap(type,a,b) do{type _c;_c=a;a=b;b=_c;}while(0)
最後に;をつけて動作するようになります。