ビットフィールドのためのポータブルなコード

ビットフィールドは、構造体のサイズをコンパクトにするために便利な仕組みだ。

typedef struct {
    int bar: 24;
    int baz: 8;
} Foo;

32 ビット環境なら、こんな構造体だと、この構造体は 4 バイトですむ。

ただ、24 ビットとか 8 ビットというような環境に依存するコードをハードコーディングしたくない。

ここでは、baz が 8 ビット、bar はその残り、という要求仕様がある場合で検討してみる。要するに 8 はハードコードでも良いということにする。

typedef struct {
    int bar: sizeof(int) - 8;
    int baz: 8;
} Foo;


まず思いつくのはこんな感じのコードだけど、ビットフィールドに sizeof 演算子は使用できないのでコンパイルエラーが発生する。ビットフィールドに sizeof 演算子が使用できないのは ANSI の仕様だ。


ということで、ポータブルなビットフィールドを書くのは以下のようにしなければならない。

#include <stdio.h>
#include <limits.h>

#if UINT_MAX==(1<<32)-1
#define INT_BIT 32
#elif UINT_MAX==(1<<64)-1
#define INT_BIT 64
#else
#error "This product does not support this platform."
#endif

typedef struct {
    int bar: INT_BIT - 8;
    int baz: 8;
} Foo;

int main(int argc, char *argv[]) {
    printf("%d\n", sizeof(Foo));
    return 0;
}

ANSI では limits.h に CHAR_BIT という char のビット数が定義されてはいるけど、INT_BIT のようなものは定義されていないため、UINT_MAX から類推するしかない。ただ、unsigned int が 32 バイトなのに、UINT_MAX が (1<<32)-1 ではない(与えられたビットを全部使用しない)変態的な処理系も存在するかも知れないので注意が必要だ。