さて、今日はC/C++のビットフィールド構造体とメモリへの
マッピングについて書いておきます。
まず、ビットフィールド構造体とは以下のような物を言います。
struct BitData { uint8_t sample_bit0 : 1;//ビットフィールド1 uint8_t sample_bit1 : 1;//ビットフィールド2 uint8_t sample_bit2 : 1;//ビットフィールド3 uint8_t sample_bit3 : 1;//ビットフィールド4 uint8_t sample_bit4 : 1;//ビットフィールド5 uint8_t sample_bit5 : 1;//ビットフィールド6 uint8_t sample_bit6 : 1;//ビットフィールド7 uint8_t sample_bit7 : 1;//ビットフィールド8 };
上記の例の場合、この構造体はメモリ上で1byteの容量を占有します。
この構造体を確保済みのメモリにポインタでマッピングを行なう場合に
注意をする点があったので、メモとしてここに書いておきます。
通常、構造体の並びを決めてその構造体の内容をそのまま保存して呼び出すといった、自分でデータ構造の仕様を決めている場合は特に意識する事もないのですが、
他の人が作成したファイルの読み書き仕様に沿って読み書きを行なう場合には以下の点に注意する必要があります。
- バイナリデータ仕様書上のフィールドの並びを
そのまま構造体のならびにしてはいけない。 - 仕様書上のフィールドの並びを構造体に落としこむ場合は
8bit単位で仕様書とは逆順で並べる。
どういうことかというと、例えば最初に示した「BitData」構造体を例にすると、
uint8_t sample_bit0 : 1;//ビットフィールド1 uint8_t sample_bit1 : 1;//ビットフィールド2 uint8_t sample_bit2 : 1;//ビットフィールド3 uint8_t sample_bit3 : 1;//ビットフィールド4 uint8_t sample_bit4 : 1;//ビットフィールド5 uint8_t sample_bit5 : 1;//ビットフィールド6 uint8_t sample_bit6 : 1;//ビットフィールド7 uint8_t sample_bit7 : 1;//ビットフィールド8
↑が仕様書で指示された並びだったとする。
この場合、構造体に落としこむ時、構造体の定義を以下のようにする必要がある。
struct BitData { uint8_t sample_bit7 : 1;//ビットフィールド8 uint8_t sample_bit6 : 1;//ビットフィールド7 uint8_t sample_bit5 : 1;//ビットフィールド6 uint8_t sample_bit4 : 1;//ビットフィールド5 uint8_t sample_bit3 : 1;//ビットフィールド4 uint8_t sample_bit2 : 1;//ビットフィールド3 uint8_t sample_bit1 : 1;//ビットフィールド2 uint8_t sample_bit0 : 1;//ビットフィールド1 };
理由はまじめに調べていないが、
おそらく、バイトオーダーに起因する物だと思われる。
今回は一般的なリトルエンディアン環境でコーディングを行なっていたので、
8bit毎に反転させる必要があったが、もし、ビッグエンディアン環境で動かす場合は反転させる必要が無いので、この場合は予め、「#define」マクロで各環境に応じて切り替えられるように作成しておく必要があるかもしれない。
余談だが、この想定で間違い無い場合は自身でデータ仕様を決めてバイナリファイルなどを扱う場合でも「#define」マクロで各環境に応じて切り替える必要が
出てくると思うので、注意したほうがいい。