C ビットフィールド

提供:yonewiki

C++へ戻る


※このページではC言語にも存在していたという意味で記事タイトルがC ビットフィールドになっていますが、
C++でも同様です。C++だけの機能がある場合は明記します。

ビットフィールド

ビットフィールドは、int変数の型の大きさ(4Byte)をバイト単位ではなくビット単位で確保しようとするものですが、構造体の中で定義される変数をビット単位で領域を分け合うことにより利用するべきメモリサイズを節約できる技術になります。真と偽の2値を扱うためのbool型でも1バイトを消費してしまうように型の大きさがバイト単位で確保されるものになっているのですが、ビットフィールドを使うことによって、2値の表現で必要となる1ビットのbool型のような変数のための領域の確保を実現できます。他にもMIDIの要素値やマイコンのように保持可能な値の範囲が0~127や1~128であるような7bitの大きさのint型を作ったりすることも出来ます。特殊な型で、通常の変数と混用することで、桁あふれが発生しやすくなりますので、十分に留意して扱う必要が発生します。


ビットって何?っていう段階の人の場合は2進数のお勉強からになりますね。ネットで2進数を検索して勉強してみて下さい。数を2進数での表現をしたときの桁数がビットに相当します。8bit=1Byteでして、8bitあれば、十進数におきかえたところの0~255までを表現できます。最初の一桁を符号としてみなす符号付き十進数なら-128~127となります。通常の変数はByte単位で、メモリの利用する大きさが区切られていますが、これをbit単位での利用をしてメモリ利用効率を上げるのが目的のビットフィードですが、変数で扱える値も変化するので、ビット単位で数値計算が出来ないと扱うことはできません。


具体的には以下のようにして、ビットフィールドを利用した変数を定義できます。

struct stBFBool{
    unsigned int bf1_aflag :1;
    unsigned int bf1_bflag :1;
    unsigned int bf1_cflag :1;
    unsigned int bf1_dflag :1;
    unsigned int bf1_eflag :1;
    unsigned int bf1_fflag :1;
    unsigned int bf1_gflag :1;
    
    unsigned int bf1_xflag :1;
    unsigned int bf1_yflag :1;
    unsigned int bf1_zflag :1;
};

上記のビットフィールドにより26bitで26個の1bitのunsigned int型の変数を定義できることになります。bool型のように扱えるだけであってbool型ではないです。bool型は1Byteの変数で0を偽、0以外になるすべての値を真とするものです。ここでは、1bitの符号無しintの1ビット長で0を偽、1を真とし、それ以外の値を代入しようとするとオーバフローする型です。また、ビットフィールドという手法を使っているため、アドレスの参照ができないという制限があります。ポインタでの操作はできないと考えてよいと思います。これらの制限とメリットのどちらが得かを考えて利用する必要があります。


ビットフィールドには他にも制限があり、int型より大きいサイズのbit数の指定はできません。したがって、

unsigned int bf1_yflag :33;

としても、32までしか使えないため、コンパイルエラーになります。


bit値がint型のしきりをまたぐような大きさでは確保できません。例えば

unsigned int bf1_xflag :24;
unsigned int bf1_yflag :24;
unsigned int bf1_zflag :24;
unsigned int bf1_aaflag :24;

とすると、1変数あたりで32-24=8で8ビットずつがあまるので、4つあつまると1byte節約できるようにも感じますが、実際には、int型の大きさをまたいでbit値を確保できないために詰めることができず、節約にはなりません。


あえて、節約しない方法もあります。

struct stBFBool{
    unsigned int bf1_aflag :1;
    unsigned int           :0;
    unsigned int bf1_bflag :1;
    unsigned int           :0;
    unsigned int bf1_cflag :1;
    unsigned int           :0;
    unsigned int bf1_dflag :1;
    unsigned int           :0;
    unsigned int bf1_eflag :1;
    unsigned int           :0;
    unsigned int bf1_fflag :1;
    unsigned int           :0;
    unsigned int bf1_gflag :1;
    unsigned int           :0;
    
    unsigned int bf1_xflag :1;
    unsigned int           :0;
    unsigned int bf1_yflag :1;
    unsigned int           :0;
    unsigned int bf1_zflag :1;
    unsigned int           :0;
};

上記のようにビットフィールド値に0を指定すると、次のint型の区切りまでビットフィールドの確保開始位置をシフトします。1bitの変数ですが、4Byteのメモリサイズを使うようにふるまいます。


指定したビットを使わないために以下のように指定してビットフィールドに隙間を設けたようなメモリ確保もできます。

struct stBFBool{
    unsigned int bf1_aflag :1;
    unsigned int           :7;
    unsigned int bf1_bflag :1;
    unsigned int           :7;
    unsigned int bf1_cflag :1;
    unsigned int           :7;
    unsigned int bf1_dflag :1;
    unsigned int           :7;
    unsigned int bf1_eflag :1;
    unsigned int           :7;
    unsigned int bf1_fflag :1;
    unsigned int           :7;
    unsigned int bf1_gflag :1;
    unsigned int           :7;
    
    unsigned int bf1_xflag :1;
    unsigned int           :7;
    unsigned int bf1_yflag :1;
    unsigned int           :7;
    unsigned int bf1_zflag :1;
    unsigned int           :7;
};

と上記のように8ビットずつの単位で1ビットの変数を作ることもできます。名前定義されていない変数に7bitを割り当てることで8bit毎のメモリ確保になるようにふるまいます。


マイクロソフト固有の仕様になりますが、ビットフィールドの記述順にメモリの下位側から上位ビット側へとメモリが確保され、次の1バイト大きいアドレス番号へとメモリの確保する領域が進んでいくように処理がなされます。


絵でもかかないと意味の分からないビットフィールドについての記事でしたが、あまり使わない技術ですので、あえて絵はかきません。想像力(創造力)をはたらかせるべし!


C++へ戻る