読者です 読者をやめる 読者になる 読者になる

mcommit's message

大阪でソフトウェア開発の仕事をしている simotinこと宮崎といいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

C言語のシフト演算は気まぐれ

仕事でC言語のコードを見ていて、シフト演算に関するバグにはまったので備忘録として挙げておきたいと思います。

#include <stdio.h>
int main(int argc, char **argv)
{
    char num = 0xB3;    // - 77
    unsigned short hoge = (num << 0); 
    printf("0x%02X", hoge);
    return 0;
}


のようなコードを書いた場合hogeには何が入るでしょうか。

ここではchar型の変数を0bitシフトして、unsigned short型の変数に代入しています。

恐らくhogeは0x00B3になると思われたのではないでしょうか。というより私はそう思いました。


残念ながら答えは

「分からない」

が正解です。

私の手元のGCCでは、hogeには0xFFB3が入りました。

ちなみに unsigned char num = 0xB3;

とした場合には、hogeは0x00B3になります。

シフト演算による上位ビットに何が入るかはC言語の仕様では定義されておらず、処理系依存になっているそうです。この挙動は知りませんでした。

ビット演算やシフト演算を扱う際は無意識に符号無し型(unsigned)を使っていたのでこれまで遭遇しなかったんだと思います。

感想

C言語には仕様として未定義の動作がちらほらあるようです。
そういったコードについては処理系(コンパイラ)によってどう動くかがわからないので、移植性を妨げ、バグの温床になります。

何が未定義なのかを把握し、どのようなコードを書くのが正しい姿(安全な書き方)なのかを知っておくことが重要だと強く感じました。

上記のビット演算に関する話は、きちんとした入門書などでは解説されていることが多いようです。