エンジニアのソフトウェア的愛情

または私は如何にして心配するのを止めてプログラムを・愛する・ようになったか

「マジックナンバー」はなにか?

プログラマとしては、今更な話題ですが。

プログラミングの基礎として、マジックナンバーを使うのをよそうというのがあります。

    for(int i = 0; i < 10; ++i) // `10'ってなに?

ちゃんと名前をつけようと。

    static const int ArrayCount = 10;
    // ...
    for(int i = 0; i < ArrayCount; ++i)

これを徹底してくと、こういったコードが時折表れる。

    static const int ShiftSize4 = 4;
    static const int Mask4Bits  = 0x0f;
    // ...
    int high = (value >> ShiftSize4) & Mask4Bits;
    int low  = value & Mask4Bits;

ここで表れる「ShiftSize4」とか「Mask4Bits」とか。名前自身がマジックナンバーになっているわけで、名前付けした効果がまるでないわけで。

これが、例えば、取り出す値が5ビットになったらどうなるか。もともと名前付けは、数値が変わっても意味付けが変わらなければ、名前に対応付けられる数値の方を操作するだけで変更に対応できるように、という効果を期待しているわけで。それをここで適用してみると。

    static const int ShiftSize4 = 5;    // ?
    static const int Maks4Bits  = 0x1f; // ?

近い将来に破綻するのが目に見えるよう。だったら名前も変えたら、などとやっていたら、何のために名前をつけたのかわからなくなってしまう。

わたしも明確な答えを持っているわけではないんですが、こういう場合式全体に名前をつけるのが、よいのではないかと(言うまでもない、という声も聞こえそうですが)。

    int high = getHigh(value);
    int low  = getLow(value);

そして、getHigh/getLowはこれでいいのでは。

    int getHigh(int value) { return (value >> 5) & 0x1f; }
    int getLow(int value)  { return value & 0x1f;        }

getHigh/getLowの中では'5'とか'0x1f'という数値そのものが意味を持っているわけで、それをわざわざ名前に置き換える必要はないだろう、というのがわたしの今現在の感覚。

難しい話をしているつもりはないのに、これをきちんと説明するのが難しい。まだまだ未熟。

補足。
'0x1f'というのは、さすがに不格好と思うので、ここはせめて書き直した方がよいかも。

    int mask(int bitSize)  { return (1u << bitSize) - 1;    }
    int getHigh(int value) { return (value >> 5) & mask(5); }
    int getLow(int value)  { return value & mask(5);        }

コンパイルのときに警告でそうだけど。

補足その2。
奇特にも、こちらご覧になっておられるプログラマの方がいらっしゃったら、ご意見頂けるとありがたいです。