C言語関係掲示板

過去ログ

No.1230 type型の二つの値を交換する関数形式のマクロ

[戻る] [ホームページ]
No.16250

関数形式マクロ
投稿者---チェリーC(2004/08/20 11:42:13)


type型の二つの値を交換する関数形式のマクロ

 swap(type,a,b)

を定義したいのですが、どうすればよいのでしょうか?

例えばint型の変数x,yの値が5と10であるとき、swap(int,x,y)を
呼び出した後は、x,yにそれぞれ10と5を格納したいです。


No.16252

Re:関数形式マクロ
投稿者---かずま(2004/08/20 13:02:19)


#define swap(type, a, b)  { type t = a; a = b; b = t; }

これでいいと思います。しかし、この場合 swap(int, x, y) は
関数呼び出しではなく文であることに注意が必要です。

    if (x > y)
        swap(int, x, y)    /* <-- セミコロンを付けない */
    else
        puts("nothing to do");


セミコロンを付けることを前提に

#define swap(type, a, b)  do { type t = a; a = b; b = t; } while (0)

などと書く場合もありますが、そこまでしても、やはり swap() は
関数ではありませんから、誤魔化しですね。



No.16253

Re:関数形式マクロ
投稿者---チェリーC(2004/08/20 13:14:34)


ありがとうございます。このとおりにしたらできました。

ただ・・どうもここで言う関数と文の違いがいまいち
理解できないのですが、
どこがどう違うのか教えていただけないでしょうか?


No.16255

Re:関数形式マクロ
投稿者---シャノン(2004/08/20 13:55:12)


>ありがとうございます。このとおりにしたらできました。
>
>ただ・・どうもここで言う関数と文の違いがいまいち
>理解できないのですが、
>どこがどう違うのか教えていただけないでしょうか?

関数は、それを呼び出す位置とは別の場所にあって、
呼び出したら処理をされて戻ってきますよね。

マクロは、コンパイル時にその内容が展開されます。
つまり、

> #define swap(type, a, b)  { type t = a; a = b; b = t; }

>    if (x > y)
>        swap(int, x, y)    /* <-- セミコロンを付けない */
>    else
>        puts("nothing to do");

というコードは、

    if (x > y)
        { int t = x; x = y; y = t; }
    else
        puts("nothing to do");

このように解釈されてコンパイルされます。
この違いをわかっていないと、痛い目に遭うことがあります。



No.16259

Re:関数形式マクロ
投稿者---Sciggepy(2004/08/20 16:40:37)


>マクロは、コンパイル時にその内容が展開されます。
より正確に言えば、プリプロセス時でしょうか。

> #define swap(type, a, b)  { type t = a; a = b; b = t; }

>    if (x > y)
>        swap(int, x, y)    /* <-- セミコロンを付けない */
>    else
>        puts("nothing to do");

というコードは、

    if (x > y)
        { int t = x; x = y; y = t; }
    else
        puts("nothing to do");
>このように解釈されてコンパイルされます。
>この違いをわかっていないと、痛い目に遭うことがあります。
(例)
#define SQUARE1(x) ((x)*(x))
inline int SQUARE2(int x) {return x*x;}
//...
int x=2,y;
y=SQUARE1(x++);    //((x++)*(x++))より、x=4,y=6になる
x=2;
y=SQUARE2(x++);    //x=3,y=4
(参考までに)入れ替えるものが同じ型の数値ならば、

#define SWAP(a,b) {(a)=(b)-(a); (b)-=(a); (a)+=(b);}

でも可能です。



No.16261

Re:関数形式マクロ
投稿者---Ban(2004/08/20 17:01:53)


>#define SWAP(a,b) {(a)=(b)-(a); (b)-=(a); (a)+=(b);}

面白いですが、気をつけないとオーバフロー/アンダフローの危険がありますね。
# あまり一般的じゃない(?)手法なので割と見落とされそうです。




No.16265

Re:関数形式マクロ
投稿者---Sciggepy(2004/08/20 19:28:31)


>面白いですが、気をつけないとオーバフロー/アンダフローの危険がありますね。
そうですね。効率も安全性も、一時変数を使う方法より劣ります。

ところで、オーバー風呂やアンダーフローを判定する方法はありますか?



No.16266

Re:関数形式マクロ
投稿者---シャノン(2004/08/20 19:34:53)


>>面白いですが、気をつけないとオーバフロー/アンダフローの危険がありますね。
>そうですね。効率も安全性も、一時変数を使う方法より劣ります。
>
>ところで、オーバー風呂やアンダーフローを判定する方法はありますか?

1時間以上入っているとのぼせてオーバー風呂しますw
というのは置いといて。

C 言語的にはチェックする方法は無かったと思います。
ある型が保持しうる最大の値 + 1 は未定義だったような…
#規格書読んでません。
#整数型限定かなぁ…実数だったらどうなるんだろう




No.16267

Re:関数形式マクロ
投稿者---Ban(2004/08/20 19:44:27)


>C 言語的にはチェックする方法は無かったと思います。
>ある型が保持しうる最大の値 + 1 は未定義だったような…
>#規格書読んでません。
>#整数型限定かなぁ…実数だったらどうなるんだろう

私もISは引いてませんが、多分標準の C にはないです。
上位の型にキャストしてから確認するくらいでしょうか。
# 使っている環境によってはチェッカが検出してくれるかも。



No.16268

Re:関数形式マクロ
投稿者---Sciggepy(2004/08/20 20:42:26)


符号なし値に限って言えば、

unsigned int a=???,b=???,c;
c=a/2+b/2;
(1)aが奇数かつbが奇数
c&gt;=UINT_MAX/2ならば、オーバーフロー
(2)(1)以外
c&gt;UINT_MAX/2ならば、オーバーフロー

で判断できそうですが、どうでしょう?



No.16262

Re:関数形式マクロ
投稿者---シャノン(2004/08/20 17:09:24)


>>マクロは、コンパイル時にその内容が展開されます。
>より正確に言えば、プリプロセス時でしょうか。

ま、そうですね。
そう言われることも予想しておりましたが、わかりやすくするために
敢えてコンパイル時という言い方をしました。

補足:
先頭が # で始まる、#include 、#define 、#if などの行が
「プリプロセッサ」と呼ばれることはご存知かと思います。
これらの行が解釈されるのは、プリプロセス時、つまり、
プロセス(コンパイル)の前になります。

>(参考までに)入れ替えるものが同じ型の数値ならば、
>
>#define SWAP(a,b) {(a)=(b)-(a); (b)-=(a); (a)+=(b);}
>
>でも可能です。

整数型に限り、

#define SWAP(a,b) {(a)^=(b); (b)^=(a); (a)^=(b);}

でもできます。
やめといた方が無難だと思いますけどw


No.16263

Re:関数形式マクロ
投稿者---ぽこ(2004/08/20 17:15:55)


>>(参考までに)入れ替えるものが同じ型の数値ならば、
>>
>>#define SWAP(a,b) {(a)=(b)-(a); (b)-=(a); (a)+=(b);}
>>
>>でも可能です。
>
>整数型に限り、
>
>#define SWAP(a,b) {(a)^=(b); (b)^=(a); (a)^=(b);}
>
>でもできます。
>やめといた方が無難だと思いますけどw

どちらも方法も止めるべきでしょうね。
int a = 10;
とあったときに
SWAP(a,a)
とかやると目も当てられません。


No.16264

Re:関数形式マクロ
投稿者---シャノン(2004/08/20 17:25:13)


>>>(参考までに)入れ替えるものが同じ型の数値ならば、
>>>
>>>#define SWAP(a,b) {(a)=(b)-(a); (b)-=(a); (a)+=(b);}
>>>
>>>でも可能です。
>>
>>整数型に限り、
>>
>>#define SWAP(a,b) {(a)^=(b); (b)^=(a); (a)^=(b);}
>>
>>でもできます。
>>やめといた方が無難だと思いますけどw
>
>どちらも方法も止めるべきでしょうね。
>int a = 10;
>とあったときに
>SWAP(a,a)
>とかやると目も当てられません。

ええい、これでどうだっ(<だからやめろってw)
#define SWAP(a,b) { if( &a != &b ){ (a)^=(b); (b)^=(a); (a)^=(b); }}


No.16269

Re:関数形式マクロ
投稿者---NykR(2004/08/20 21:50:46)


int x=2,y;
y=SQUARE1(x++);    //((x++)*(x++))より、x=4,y=6になる

えーっと、

直前の副作用完了点から次の副作用完了点までの間に, 式の評価によって一つのオブジェクトに格納された値を変更する回数は, 高々1回でなければならない。(JIS X 3010 6.5)

ので、((x++)*(x++))は未定義の式です。ですから、x=4,y=6になるとは限りませんし、この式を評価しようとした結果がどうなるかは全くわかりません。


# x*xという計算をしたあとで、2回まとめてインクリメントされるかもしれないし
# パソコンが奇声をあげて駆け出すかもしれない


No.16270

Re:関数形式マクロ
投稿者---Sciggepy(2004/08/20 22:17:44)


そんな仕様があったことは知りませんでしたが、BCCでコンパイルしてみると、
   ;	
   ;		y=((x++)*(x++));
   ;	
?live1@32: ; EAX = x
	mov       edx,eax
	imul      edx,eax
	inc       eax
	inc       eax
となりました。ふむ、x*xを処理した後に2回xをインクリメントしているようだ...gccでも結果は同じでしたが、処理内容が少し違っていました。

結論としては、((x++)*(x++))のように書いてはならないということですね。