去年のクリスマスプレゼント



C言語にビットフィールドというのがある。

例えば、以下の構造体 field_test のサイズは、32bitのシステムの場合は int (32bit) x 2 で 8byteである。

struct field_test {
int foo ;
int bar ;
};

だけど、以下のようにビットフィールドを使うと、foo と bar を合わせて 32bit なので 4byteになる。fooやbarがオーバーフローしない限りは、メモリの節約ができるというわけ。

struct field_test {
int foo : 10 ;
int bar : 22 ;
};

だけども、去年の暮れ(クリスマス直前)に会社で発生したクリスマスプレゼント、もとい、ツールの障害は、上記の記述が原因であった。例えば、以下のような field.cがあったとする

-- field.c --

#include <stdio.h>

struct field_test {
int foo : 10;
int bar : 22;
};

int main(void){
struct field_test ft ;
ft.foo = -1;
printf("ft.foo = %d\n", ft.foo);
return 0;
}

--

GCCでコンパイルした場合は、特におかしなところはないように見える。

% gcc -o field field.c
% ./field
ft.foo = -1

ところが、Sun の Forte でコンパイルすると、、、

% cc -o field field.c
% ./field
ft.foo = 1023

-1 が 1023 になっとるやんけ!

最初はデバッガかけて ft.fooがおかしな値になってるのは分かったんだけど、その理由が全然わからず。ヘッダファイルの中の構造体のメンバ変数にビットフィールドが使われているのを見て「もしや?」と思って、調べたら上記のような現象が判明した。それでも、なんで Forte だと ft.foo に -1 を入れると 1023 になるのか、全然意味わからず。

原因の調査でC言語のバイブル「プログラミング言語C 第2版」通称 K&R を調べたら、以下の記述が見つかった。

「フィールドは int としてのみ宣言してもよいが、移植可能にするには、signed あるいは unsigned を明示的に指定すべきである。」(183ページ)

通常のビットフィールドが使われていない int は、暗黙のうちに signed int と解釈されている。つまり、マイナスの値が使える。だけども、何故か Forte では、ビットフィールドを使った場合に限り、int と書いただけだと unsigned int と解釈されていたのだった。つまり、マイナスの値が使えない。

以下のように書けば、Forte でコンパイルしても foo や bar で符号付き整数が扱える。

struct field_test {
signed int foo : 10;
signed int bar : 22;
};

最初は「なんで、ビットフィールド使った時だけ int の解釈を変えるんだー!Forteのアホー!」と思った。そしたら、会社の先輩が K&R の初版を持って来て、ビットフィールドについての説明部分を見せてくれた。そこには以下のように書いてあった。

「ビットフィールドは符号なしである。」

つまり、昔はビットフィールドを使った変数では、符号無し整数しか扱えなかったらしい。K&Rの初版から第二版までの間に、C言語の仕様が変更されて、ビットフィールドを使った変数でも符号付き整数が扱えるようになったと思われる。

もし Forte が昔のC言語の仕様と互換性を保つために、ビットフィールドを使った時は int を unsgined int と解釈しているのであれば、それほどおかしな事でもないかな、と思い直したのだった。

C言語に歴史あり、と思った出来事でした。

Posted: 木 - 1月 8, 2004 at 09:53 午後        


©