すべてがFになる

Tsukasa OISHI

今日、C言語でビットシフトをしていたら思わぬ挙動に出会ってしまいました。

unsigned long shift_one(unsigned long x)
{
    unsigned long shifted = (1 << x);
    return shifted;
}

という関数の引数に31という値を渡すと、

0xffffffff80000000

という結果が返ってきました。うーん、上位4バイト、すべてがFになっているぞう。あいた領域が0で埋まったり1で埋まったりするのは経験がある。下位4バイトの最上位ビットが1なのがあやしいな。いや、そこが1なのは、1を31ビット左シフトしたんだから当たり前なんだけど、その結果が上位4バイトのすべてがFになるにつながっている気がする。
と思っていて、でもunsignedだから最上位ビットは符号じゃないよ? とか考えていたんだけど、まてよこれ符号扱いになっているのでは? と考えてググってみたらすぐにわかりました。

結果からいうと64ビット環境で、" 1

  1. 関数の中で (1

  2. unsigned long (64ビット) の変数 shiftedに代入されるときに、上位4バイトは符号ビットだった下位4バイトの最上位の1で埋められる。

ということになったようでした。最初は意味がわからなくてびっくりしました。C言語を普段から扱っている人には今さらな話題なんでしょうけど。
で、どうすればいいかというと リテラルの 1 をunsigned longとして扱ってもらえればいいので、

unsigned long shift_one(unsigned long x)
{
    unsigned long shifted = (1UL << x);
    return shifted;
}

こう書いて解決しました。