2009-02-09

C++のビットフィールド

ここ1年半、C++で組込みの仕事をさせていただいております。
最近ビットフィールドと共用体を使用して2byteデータを上位1bitと下位15bitを切り分けるソースコードを見かけました。
ビットフィールドの仕組み自体、忘却の彼方だったので少々調べてみました。

適当ですがこんな感じの定義があったとして。
union UUnionValue {
struct SBitAccess {
unsigned int bit0:1;
unsigned int bit1to3:3;
signed int bit4to15:12;
} bitAccess;
unsigned short shortValue;
} unionValue;

unionValue.shortValue
に入力ビット列を代入すると
unitoValue.bitAccess.bit0
unitoValue.bitAccess.bit1to3
unitoValue.bitAccess.bit4to15
でそれぞれ上位1bitを符号無し、上位2bit〜4bitを符号無し、上位5bit〜16bitを符号有りで取得できると言うもの。
便利ですね。

私は今まで上位2〜4bitを符号無しで取得するには
(入力ビット列 >> 12) & 0x0007
下位5bit〜16bitを符号付きで取得するには
(入力ビット列 & 0x0fff) | ((入力ビット列 & 0x0800) ? 0xf000 : 0x0000)
のようにしていました。符号付きの方は単純なマスクだけではダメなのが面倒です。ビットフィールドを使用すべきだったのでしょうか?
と思ってすこし調べてみました。



結論

ビット単位の切り分けのためにビットフィールド使っちゃダメ。
ダメな理由は MISRA‐C を読むと何度もしつこく書いてありますが処理系に依存したコードになるからです。
どのあたりが依存しているかというと
  • short型の値がどのように格納されるか(ビッグエンディアンorリトルエンディアン)
  • ビットフィールドがメモリ上にどのように配置されるか
    • ビットフィールドが上位bitから割り当てられるか、下位bitから割り当てられるか
    • 1つのビットフィールドが記憶域単位をまたがって配置されるか
のようです。
記憶域単位というのがbyteを指すのかWORDを指すのかは不明ですが、恐らくbyteかと思います。
本当はC90やC99などの規格を参照すべき所なんですけどね…

web上で検索してみると、処理系依存の部分に触れられていない解説が多いようです。
特にパディングについてはWORDをまたがらないようにパディングされるという説明が多いようですが、規格上はどこでどのようにパディングしようと処理系の自由のようです。

未規定ではなく処理系定義については 処理系でこのような定義 があるはずなので確認すべきなんでしょうねぇ…
今の開発案件の処理系定義って何処にあるんだろ?

0 件のコメント: