ビットフィールド

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内, 検索

ビットフィールド (: bit field) は、プログラミングにおいてブーリアン型フラグをコンパクトなビットの並びとして格納する一般的な方式である。ビットフィールドの格納には、既存の固定ビット幅の整数型の領域を使用する。個々のフラグは、別々のビット位置に格納される。通常はソースコードで、個別のビットがフラグに対応する意味を付けられた、2の冪乗の定数が定義される。ビット演算論理積論理和否定の組み合わせが、フラグのセット・リセットとテストを行うために使われる。

ビットフィールドはビット配列と区別される。ビット配列は、整数でインデックスを付けられた大きなビットの集合を保存するために使用され、コンピュータ言語でサポートされる整数型よりも大きいことがある。一方で、ビットフィールドは典型的にはワードサイズの範囲内であり、ビットの表示は数値によるインデックスから独立している。それにもかかわらずビットフィールドは、それぞれのフラグのビットインデックスが列挙型の値であるビット配列を使用することで(Java言語のEnumSetクラスのように)、安全かつエレガントに実装することが出来る。これにより直接ビットを操作することの危険を回避することができる。

[編集]

C言語での実装例:

#define PREFERENCE_LIKES_ICE_CREAM ((unsigned char) (1 << 0)) // 0x01
#define PREFERENCE_PLAYS_GOLF      ((unsigned char) (1 << 1)) // 0x02
#define PREFERENCE_WATCHES_TV      ((unsigned char) (1 << 2)) // 0x04
#define PREFERENCE_READS_BOOKS     ((unsigned char) (1 << 3)) // 0x08
 
unsigned char preference;
 
void set_preference(unsigned char flag) {
     preference |= flag;
}
 
void reset_preference(unsigned char flag) {
    preference &= ~flag;
}
 
unsigned char get_preference(unsigned char flag) {
    return (preference & flag) != 0;
}

2の冪乗を表すためにハードコードされた数値(0x08)を使用する代わりに、ビットシフト演算子を使用した表現(1 << 3)を使用することは、可読性が高く推奨されている。

カーニハンリッチーの本、プログラミング言語Cでは、直接フィールドを定義し、アクセスする方法が記述されている。この方法を使用することで、ビット演算子が不要となり、ビットメンバに構造体メンバと同じようにアクセスすることが出来る。 structを使用した例:

struct preferences {
    unsigned int likes_ice_cream : 1;
    unsigned int plays_golf : 1;
    unsigned int watches_tv : 1;
    unsigned int reads_books : 1;
}; 
 
struct preferences fred;
 
fred.likes_ice_cream = 1;
fred.plays_golf = 1;
fred.watches_tv = 1;
fred.reads_books = 0;
 
if (fred.likes_ice_cream == 1)
    /* ... */

しかし、構造体のビットメンバは実用上は欠点がある。まず、メモリ上のビットの順序コンパイラによって変化する。加えて、多くの一般的なコンパイラは、ビットメンバの読み書きに対して、非効率なコードを生成する。さらに、ビットフィールドに関しては(特にマルチプロセッサシステムの場合は)、潜在的なスレッドセーフの問題がある。ほとんどのCPUではメモリ上の任意のビットの集合を扱えず、代わりにワード全体をロード/ストアする必要があるという事実のためである。以下のコード例はミューテックスを使用したとしても、スレッドセーフではない:

struct foo {
    int flag : 1;
    int counter : 15;
};
 
struct foo my_foo;
 
/* ... */
 
pthread_mutex_lock(&my_mutex);
my_foo.flag = !my_foo.flag;
pthread_mutex_unlock(&my_mutex);
 
/* 他のスレッド */
my_foo.counter += 1;

ほとんどのCPUでは、flagcounterを別々にロードしてストアすることは、ハードウェアレベルで不可能である。(これがスレッドセーフであるためには、counter自身がスレッドセーフである必要がなくても、flagcounter両方について、全てのアクセスの前後でミューテックスをロック・アンロックする必要がある。)同様に、ビット順序の問題ため、ビットフィールドが正確にワードサイズの構造である依存性がある。

[編集] 関連項目

[編集] 外部リンク

個人用ツール
名前空間

変種
操作
案内
ヘルプ
ツールボックス
他の言語