UserPreferences

KazuoMoriwaka/Journal/2008-11-26


クイズ: linux-2.6/include/linux/kernel.h より
#define min(x, y) ({                            \
        typeof(x) _min1 = (x);                  \
        typeof(y) _min2 = (y);                  \
        (void) (&_min1 == &_min2);              \
        _min1 < _min2 ? _min1 : _min2; })

なぜ4行目で &_min1 と &_min2 を比較しているのか?

こたえ。

xとyの型が違うときに、コンパイラでwarningを出すため。

xとyを比較するときに、型が違うかもしれない。 しかしCは暗黙のキャストがあるので、大小比較だけでは型が同じか十分チェックできない。 たとえば、unsigned int型とunsigned long型の比較でも問題を報告することなく、勝手にキャストして比較してしまう。 ポインタにして比較することで、異なる型のポインタ同士での演算とすることで、コンパイラのチェックにひっかかるようにする。

特にカーネルだと、このような型の差に気付かずにコーディングしてしまうと 32bit環境では再現しないけど64bit環境で248日(10ms * 2**31)くらい動かしたら出てくる問題が… とか、 そういうトラブルのタネになりやすい。そのため、このようなチェックをおこなう。

比較をしているだけで結果を使っていないため、無駄な操作であることはコンパイラもすぐ検出できる。 そのため最適化をかけていればこの比較がない場合と出力されるコードはほとんど同じになり、実行時のペナルティにはならない。

回答の傾向と感想

最短では20分、長いひとでも半日くらいでした。20分の人はさすがですね。 コンパイル時に何か変だったら警告を出そうとするんだろうな、というところまでは割とあっさり到達できるようです。 そのあと答に到達するまでの一歩にはCのこまかい仕様の知識と実装についての情報が必要ですから、実験をはじめて… というのが王道パターンのようでした。

Java使いの人に出題して答えをバラしたところ、「キモッ!Cキモッ!」と連発されてしまいました。。 たしかにtemplateやらRTTIやら使える言語だと型の比較を明示的に書けますし、 このハックだと、gccの実装次第でどこまでwarning出すかがかわりそうなのが嫌ですね。