C言語

【C言語】ビットフィールドについて解説(共用体の併用についても)

C言語の入門の参考書にはビットフィールドについての解説が載っていないこともありますが、C言語を使う現場ではbitを扱うことが多々あります。

そのため、ビットフィールドはC言語を学ぶ上では必須の知識になりますので、ぜひこの機会にマスターしましょう!

※ビットフィールドは構造体の知識が必須になりますので、構造体についてまだ詳しくない方は、まず以下の記事を読んでマスターしてください!

▼参考記事
【C言語】新人の後輩向けに構造体を分かりやすく解説してみた

ビットフィールドとは?

変数を定義するとき、char型やshort型などの型を使って、変数を定義してきました。それぞれの型には、変数に格納できるサイズが決まっています。

例えば、char型だと1byte、short型だと2byteの変数定義になります。

型定義で一番サイズが小さいのは1byteのchar型ですが、C言語では1bitの変数も定義することができます。

 

しかし、1bitの型というものは存在しません。

bit単位で変数を定義したいときに使用するのが、「ビットフィールド」というものです。

ビットフィールドを使うことで、bit単位での変数を定義することができます。

ビットフィールドの宣言方法

ビットフィールドは、構造体を使って宣言することができます。

構造体のメンバ変数を指定する際、以下のような記載をするとbit変数を作成することが出来ます。

型  変数名 : bit値

 

以下、サンプルコードです。

例で作ったビットフィールドは、flag_1、flag_2、reserveは1bitの変数、data_1は3bitの変数、data_2は2bitの変数でそれぞれ定義しました。

全ての変数のサイズを合計すると、8bit(1byte)分になります。

ですので、各bit変数の配置は以下のようになっています。

1byte(8bit)のデータの中に、5つの変数も作成することができました。

ビットフィールドの定義と使用方法

先ほどはビットフィールドの宣言をして、ビットフィールドの構造体のひな型を作りました。

宣言した後は構造体の時と同様に、ビットフィールドの構造体を定義して、変数に値を入れる必要があります。

この辺りは構造体のときと一緒ですね。

ビットフィールドを使うメリット

ビットフィールドを使うと、例えば以下のように各bitごとに意味を持たせることができます。

これだけで、1byteの領域の中に実質5つの変数を作成したことになります。

このように各bitごとに意味を持たせることが出来ると、変数のサイズの縮小に繋がり、メモリを節約できるというメリットに繋がります。

 

たとえば、処理の判定にフラグを4つ必要になった場合のことを考えてみます。

フラグが4つ必要になるので、これまでだと以下のように変数を4つ作成することになりますよね。

unsinged char型の1byteで各フラグの変数を定義しているので、合計4byteの変数を定義しています。

 

しかし、このフラグはON(1)かOFF(0)のどちらかの値しか取らないので、変数のサイズは1bitあれば十分です。

そのためビットフィールドを使って先ほどのコードを変えると、以下のようになります。

各フラグ変数は1bitの変数で定義したので、合計4bitの変数となり、変数のサイズがかなり小さくなりました。

このようにビットフィールドを上手く使えば、無駄なメモリを使わなくなり、節約することができます。

ビットフィールドと共用体の併用について

ビットフィールドは、共用体の変数と一緒に使われることも多くあります。

ビットフィールドと共用体を一緒に使う場合は、先にビットフィールドを宣言して、その後に共用体を宣言します。

ビットフィールドと共用体の変数を併用すると、共用体で定義した変数とビットフィールドの変数のアドレスが同一になります。

 

先ほどの例だと、それぞれメモリの配置は以下のようになっています。

共用体の変数dataInfoと、ビットフィールドの各bit変数は同じアドレスに格納されています(メモリが共有されています)。

そのため、以下のサンプルコードのように、各bit変数の値を変えればdataInfoの値も変わりますし、逆にdataInfoの値を変えれば各bit変数の値も変わります。

各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の処理をしたいときに、共用体が使われることがあります。

最後に

ビットフィールドを自前で作成したりすることはあまり無いかもしれませんが、現場のソースコードでは必ず目にすると思います。

ビットフィールドが出てきたら、再度この記事を見て理解しながら追ってみてください!