組み込みC/C++

C/C++リテラシー向上のためのページ

ビットフィールドを掘り下げる①

ビットフィールドを指定した場合、一般には下のような書き方をします。

 

 struct StBit0 {
    unsigned btFirst: 1;
    unsigned btSecond: 1;
    unsigned btNull: 5;
    unsigned btLast: 1;
};     

 

今回注目したいのはビットフィールドで宣言した構造体変数のサイズについてです。上の例では構造体変数一個につき何バイトの領域が確保されるのでしょうか?ビットフィールドとしては 1+1+5+1 で8bitしか使っていませんから1byteだとうれしいですが、この場合はint型の大きさ(4byte)分取られます。おそらくこれは処理系に依存しません。注目すべきはunsigned です。これは unsigned int 表記の省略形でした。という事はunsigned intで領域が確保されているという事です。さてでは次の場合はどうなるでしょうか?

 

 struct StBit0 {
    unsigned char btFirst: 1;
    unsigned char btSecond: 1;
    unsigned char btNull: 5;
    unsigned char btLast: 1;
};     

 

unsigned charとなっていますから、1byteの領域確保のような気がします。もしくは良く勉強している人だと4byteと答えるかもしれません。しかし答えは処理系依存です。書籍によってはビットフィールドで確保する領域というのは、どの型で指定したところでint型の大きさの倍数になるという書き方をしているものがありますが、実際は処理系に依存するようです。なおVisualStudioではこの宣言は1byteの領域確保になりました。 デュアルターゲット開発をする場合の落とし穴になることがありますから認識しておく必要のある項目です。

 

#include <stdio.h>
struct StBit1 {
    unsigned char a: 8;
};

struct StBit2 {
unsigned short a: 8;
};

struct StBit3 {
unsigned long a: 8;
};

int _tmain(int argc, _TCHAR* argv[])
{
/*sizeof で調べる*/
printf("StBit1 = %d\n",sizeof(StBit1));
printf("StBit2 = %d\n",sizeof(StBit2));
printf("StBit3 = %d\n",sizeof(StBit3));

/*素直に差分を求める*/
StBit1 stBit1;
StBit2 stBit2;
StBit3 stBit3;

printf("real StBit1 = %d\n", ( (int)( (&stBit1)+1)-(int)(&stBit1) ) );
printf("real StBit2 = %d\n", ( (int)( (&stBit2)+1)-(int)(&stBit2) ) );
printf("real StBit3 = %d\n", ( (int)( (&stBit3)+1)-(int)(&stBit3) ) );

return 0;
}

 

このプログラムの結果は

StBit1 = 1
StBit2 = 2
StBit3 = 4
real StBit1 = 1
real StBit2 = 2
real StBit3 = 4

となります。

 

まとめ

・ビットフィールドにビット演算をするようなプログラム、たとえばstBit2 &= 0x0100;のようなプログラムがあると環境によって結果が変わってしまう事があります。こういった処理はしないようにしましょう。