ビットフィールドのパッキング順序は実装定義です。つまり、宣言すると
struct my_struct {
struct device *dev;
unsigned char a:1,
b:1,
c:1,
d:1;
};
a
またはb
またはc
またはd
が実際に存在する場所を決定するのは完全にコンパイラ次第です__BIG_ENDIAN_BITFIELD
。__LITTLE_ENDIAN_BITFIELD
一方、定義すると、
struct my_struct {
struct device *dev;
unsigned char abcd;
};
#define MASK_A (1U << 0U)
#define MASK_B (1U << 1U)
#define MASK_C (1U << 2U)
#define MASK_D (1U << 3U)
static inline unsigned char get(const struct my_struct *const m, const unsigned char mask)
{
return m->abcd & mask;
}
static inline unsigned char set(struct my_struct *const m, const unsigned char mask, const unsigned char value)
{
m->abcd = (m->abcd & mask) | (mask & value);
return m->abcd & mask;
}
static inline unsigned char flip(struct my_struct *const m, const unsigned char mask)
{
m->abcd ^= mask;
return m->abcd & mask;
}
これはa
、ポインターに続くバイトの最下位ビットb
、2 番目のビット、c
3 番目、d
4 番目のビットにマップされます。
C コンパイラが をサポートしている場合static inline
、これらの関数はマクロと同じくらい高速ですが、マクロに問題があるわけではありません。副作用。
これにより、ビット フィールドをグループとして操作することもできます。たとえばb
、構造体に設定するには、 をt
使用しますset(&t, MASK_B, MASK_B)
。を設定b
してクリアa
するには、 を使用しますset(&t, MASK_A | MASK_B, MASK_B)
。が設定されているかどうかをテストするa
には、 を使用しますget(&t, MASK_A)
。a
またはb
が設定されているかどうかをテストするには、 を使用しますget(&t, MASK_A | MASK_B)
。a
との両方b
が設定されているかどうかを確認するには、 を使用します(get(&t, MASK_A | MASK_B)) == (MASK_A | MASK_B))
。3 つの関数はすべて、マスクが適用された結果のビット マスクを返します。つまり、他のすべてのビットはゼロです。
個人的には、この後者のアプローチを好みます。主な理由は、後者の方がより明示的であり (完全に制御できる)、汎用性が高く (個別だけでなくグループでも操作できる)、スペース効率が高い (コンパイラーはコマンドラインスイッチなどを使用しないように明示的に指示しない限り、パディングを追加します)。フラグの使用方法に応じて、マクロまたは静的インライン関数を介してフラグにアクセスすることをお勧めします。
とはいえ、構造がアプリケーションの内部にあり、大容量ストレージに保存されたり送信されたりすることがないのであれば、どちらのアプローチにも異論はありません。