Zephyr Cradle Diary


2008.08.26 (Tue)

〆切まであとわずかでーす。

[PG] char 型を 30bit ビットシフトする

C言語の話です。プログラミングのお話。

ここで言う char 型は 8bit の型定義としますが、これを 30 bit シフトするというのは問題ないのかどうか、という話。まあ仕事の最中にそんな記述を見て「これなんでコンパイルエラーにならんの?」となったのが発端なのですが。

C言語においてのビットシフトとは、まあ下みたいに書く訳です。

char x = 1;
x = x << 2;

char 型の変数 x (初期値1)を2bit分左にシフトした値にする、の意味です。結果的に x = 4 になるわけですが、まあその後の値はさておき。

ここでは、以下の式は許されるのかという話。

char x = 1;
x = x << 30;

ここで x はいくつになるかというと、 x = 0 です。 char 型は 8bit なので、当然ながらビットの上限を突破して全部0になってしまうわけです。

が。以下の式の y はどうやら非ゼロのようで。

char x = 1;
int y = x << 30;

手元にビルド環境がないので再現出来ないのがアレですが……*1。要はビットシフトの際、演算子「<<」の両側のオペランドは int 型(ここでは 32bit の型とします)に promotion されるようです。そりゃまあ演算の際は必ず int 型と同じ大きさのレジスタに入れるということを考えれば当たり前っちゃ当たり前なんですが、どうやら「C Standard」にも規定されているらしいです。

なので、この右辺「x << 30」の演算結果は一時的に int 型として保持され、例2なら char 型に cast (変換)して戻すために上位 24bit 分は落ちてしまって x = 0 に。例3なら int 型に格納できるのでゼロにはならない*2、てな話なようです。たぶん C++ でも同じ。

これまで char といえば 8bit 分のビットシフトしか出来ないって教わってきたので、これは軽く衝撃的でした。一部の ctype 系標準関数がこれで実装されている場合もあるらしく、何気なく使ってる可能性もあるのが怖いところです。いや怖いってゆっても、Cの仕様的には何の問題もない*3んですけどね。

*1 この式でコンパイラに怒られたら、右辺を int で cast してみて下さい。

*2 ちなみに y = 0x40000000 です。

*3 ただし上の例は正しくなく、シフトする値にはきちんと unsigned を付ける必要がある。符号付きを指定しなかった変数のビットシフトが算術シフトになるのか論理シフトになるのか(そもそも x や y が符号付きになるのかどうか)は、コンパイラに依存してしまうので。