C言語の入門の参考書にはビットフィールドについての解説が載っていないこともありますが、C言語を使う現場ではbitを扱うことが多々あります。
そのため、ビットフィールドはC言語を学ぶ上では必須の知識になりますので、ぜひこの機会にマスターしましょう!
※ビットフィールドは構造体の知識が必須になりますので、構造体についてまだ詳しくない方は、まず以下の記事を読んでマスターしてください!
ビットフィールドとは?
変数を定義するとき、char型やshort型などの型を使って、変数を定義してきました。それぞれの型には、変数に格納できるサイズが決まっています。
例えば、char型だと1byte、short型だと2byteの変数定義になります。
型定義で一番サイズが小さいのは1byteのchar型ですが、C言語では1bitの変数も定義することができます。
しかし、1bitの型というものは存在しません。
bit単位で変数を定義したいときに使用するのが、「ビットフィールド」というものです。
ビットフィールドを使うことで、bit単位での変数を定義することができます。
ビットフィールドの宣言方法
ビットフィールドは、構造体を使って宣言することができます。
構造体のメンバ変数を指定する際、以下のような記載をするとbit変数を作成することが出来ます。
型 変数名 : bit値
以下、サンプルコードです。
1 2 3 4 5 6 7 8 9 |
/* ビットフィールドの宣言 */ typedef struct { unsigned char flag_1 : 1; /* 1bit変数 */ unsigned char data_1 : 3; /* 3bit変数 */ unsigned char flag_2 : 1; /* 1bit変数 */ unsigned char data_2 : 2; /* 2bit変数 */ unsigned char reserve : 1; /* 予約領域 */ }st_BitField; |
例で作ったビットフィールドは、flag_1、flag_2、reserveは1bitの変数、data_1は3bitの変数、data_2は2bitの変数でそれぞれ定義しました。
全ての変数のサイズを合計すると、8bit(1byte)分になります。
ですので、各bit変数の配置は以下のようになっています。

1byte(8bit)のデータの中に、5つの変数も作成することができました。
ビットフィールドの定義と使用方法
先ほどはビットフィールドの宣言をして、ビットフィールドの構造体のひな型を作りました。
宣言した後は構造体の時と同様に、ビットフィールドの構造体を定義して、変数に値を入れる必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include<stdio.h> /* ビットフィールドの宣言 */ typedef struct { unsigned char flag_1 : 1; /* 1bit変数 */ unsigned char data_1 : 3; /* 3bit変数 */ unsigned char flag_2 : 1; /* 1bit変数 */ unsigned char data_2 : 2; /* 2bit変数 */ }st_BitField; int main() { /* 構造体の定義 */ st_BitField bitData; /* 各bit変数に値を格納する */ bitData.flag_1 = 0x1; bitData.data_1 = 0x3; bitData.flag_2 = 0x0; bitData.data_2 = 0x2; printf("flag_1...%d\n",bitData.flag_1); printf("data_1...%d\n",bitData.data_1); printf("flag_2...%d\n",bitData.flag_2); printf("data_2...%d\n",bitData.data_2); return 0; } |
1 2 3 4 |
flag_1...1 data_1...3 flag_2...0 data_2...2 |
この辺りは構造体のときと一緒ですね。
ビットフィールドを使うメリット
ビットフィールドを使うと、例えば以下のように各bitごとに意味を持たせることができます。

これだけで、1byteの領域の中に実質5つの変数を作成したことになります。
このように各bitごとに意味を持たせることが出来ると、変数のサイズの縮小に繋がり、メモリを節約できるというメリットに繋がります。
たとえば、処理の判定にフラグを4つ必要になった場合のことを考えてみます。
フラグが4つ必要になるので、これまでだと以下のように変数を4つ作成することになりますよね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include<stdio.h> #define FLAG_ON (1) #define FLAG_OFF (0) int main() { unsigned char flag_1,flag_2,flag_3,flag_4; /* flag_1~flag_4までのいずれかがON(1)になっている場合 */ if(( flag_1 == FLAG_ON ) || ( flag_2 == FLAG_ON ) || ( flag_3 == FLAG_ON ) || ( flag_4 == FLAG_ON )) { printf("フラグがONになっています\n"); } return 0; } |
unsinged char型の1byteで各フラグの変数を定義しているので、合計4byteの変数を定義しています。
しかし、このフラグはON(1)かOFF(0)のどちらかの値しか取らないので、変数のサイズは1bitあれば十分です。

そのためビットフィールドを使って先ほどのコードを変えると、以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include<stdio.h> #define FLAG_ON (1) #define FLAG_OFF (0) /* ビットフィールドの宣言 */ typedef struct { unsigned char flag_1 : 1; /* 1bit変数 */ unsigned char flag_2 : 1; /* 1bit変数 */ unsigned char flag_3 : 1; /* 1bit変数 */ unsigned char flag_4 : 1; /* 1bit変数 */ }st_FlagInfo; int main() { /* 構造体の定義 */ st_FlagInfo flagInfo; /* flag_1~flag_4までのいずれかがON(1)になっている場合 */ if(( flagInfo.flag_1 == FLAG_ON ) || ( flagInfo.flag_2 == FLAG_ON ) || ( flagInfo.flag_3 == FLAG_ON ) || ( flagInfo.flag_4 == FLAG_ON )) { printf("フラグがONになっています\n"); } return 0; } |
各フラグ変数は1bitの変数で定義したので、合計4bitの変数となり、変数のサイズがかなり小さくなりました。
このようにビットフィールドを上手く使えば、無駄なメモリを使わなくなり、節約することができます。
ビットフィールドと共用体の併用について
ビットフィールドは、共用体の変数と一緒に使われることも多くあります。
ビットフィールドと共用体を一緒に使う場合は、先にビットフィールドを宣言して、その後に共用体を宣言します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* ビットフィールドの宣言 */ typedef struct { unsigned char flag_1 : 1; /* 1bit変数 */ unsigned char data_1 : 3; /* 3bit変数 */ unsigned char flag_2 : 1; /* 1bit変数 */ unsigned char data_2 : 2; /* 2bit変数 */ unsigned char reserve : 1; /* 予約領域 */ }st_BitField; /* 共用体の宣言 */ typedef union { unsigned char dataInfo; /* 1byte変数 */ st_BitField bitData; /* ビットフィールドの変数 */ }uni_Info; |
ビットフィールドと共用体の変数を併用すると、共用体で定義した変数とビットフィールドの変数のアドレスが同一になります。
先ほどの例だと、それぞれメモリの配置は以下のようになっています。

共用体の変数dataInfoと、ビットフィールドの各bit変数は同じアドレスに格納されています(メモリが共有されています)。
そのため、以下のサンプルコードのように、各bit変数の値を変えればdataInfoの値も変わりますし、逆にdataInfoの値を変えれば各bit変数の値も変わります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include<stdio.h> /* ビットフィールドの宣言 */ typedef struct { unsigned char flag_1 : 1; /* 1bit変数 */ unsigned char data_1 : 3; /* 3bit変数 */ unsigned char flag_2 : 1; /* 1bit変数 */ unsigned char data_2 : 2; /* 2bit変数 */ unsigned char reserve : 1; /* 予約領域 */ }st_BitField; /* 共用体の宣言 */ typedef union { unsigned char dataInfo; /* 1byte変数 */ st_BitField bitData; /* ビットフィールドの変数 */ }uni_Info; int main() { /* 構造体の定義 */ uni_Info uniInfo; /* 各bit変数に値を格納する */ uniInfo.bitData.flag_1 = 0x1; uniInfo.bitData.data_1 = 0x7; uniInfo.bitData.flag_2 = 0x1; uniInfo.bitData.data_2 = 0x3; printf("bit変数の値を変えた場合\n"); printf("flag_1...%d\n",uniInfo.bitData.flag_1); printf("data_1...%d\n",uniInfo.bitData.data_1); printf("flag_2...%d\n",uniInfo.bitData.flag_2); printf("data_2...%d\n",uniInfo.bitData.data_2); printf("dataInfo...%d\n",uniInfo.dataInfo); /* 共用体の変数に0を代入 */ uniInfo.dataInfo = 0; printf("dataInfoの値を変えた場合\n"); printf("flag_1...%d\n",uniInfo.bitData.flag_1); printf("data_1...%d\n",uniInfo.bitData.data_1); printf("flag_2...%d\n",uniInfo.bitData.flag_2); printf("data_2...%d\n",uniInfo.bitData.data_2); printf("dataInfo...%d\n",uniInfo.dataInfo); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 |
bit変数の値を変えた場合 flag_1...1 data_1...7 flag_2...1 data_2...3 dataInfo...127 dataInfoの値を変えた場合 flag_1...0 data_1...0 flag_2...0 data_2...0 dataInfo...0 |
各bit変数に値を入れた場合、以下画像のようなbit配置になるので、構造体のビットフィールドは”0111 1111″という配置になります。
そのためメモリを共有しているdataInfoも”0111 1111″というbit配置になるので、値は127になります。

逆に、dataInfoに0を入れた場合、dataInfoのメモリ配置は”0000 0000″になります。そのため、flag_1やdata_1などのbitも全て0になってしまうため、各bit変数の値も0になってしまいます。

上記例のように、全てのbitをOFF(0)にしたいとき、dataInfoに0を入れれば全てのbitが0に落ちてくれます。
このように、一括でbitの処理をしたいときに、共用体が使われることがあります。
最後に
ビットフィールドを自前で作成したりすることはあまり無いかもしれませんが、現場のソースコードでは必ず目にすると思います。
ビットフィールドが出てきたら、再度この記事を見て理解しながら追ってみてください!