組み込みプログラムでは、メモリの節約のため、tureかfalseかのようなフラグを扱うとき、1バイトよりも1ビットで表現することがあります。
そうすることで、1バイトで8つのフラグを持つことが出来るからです。
このとき、1ビット単位を扱うことが可能なビットフィールドについてお話ししようと思います。
ビットフィールドは構造体を用いる
構造体の作り方は、わかりますよね?
もし、わからない場合は、こちらのページを参照してください。
C言語 構造体を作ってみよう
ビットフィールドを扱うには構造体を用いて、このように宣言します。
struct タグ名 { メンバ変数1 : 1; メンバ変数2 : 1; ・ ・ ・ };
メンバ変数の後ろに「:ビット数」を記述し、何ビット使用するのかを指定します。1ビットなら、仮にメンバ変数の型がlongであっても最下位ビットのみ使用することになります。
これだけで、ビットフィールドは定義できます。
ここで、注意してもらいたいのは、プログラムではバイト単位で処理されます。したがって、ビットフィールドを用いるときは8ビット単位になるように宣言しましょう。
余るビットがある場合は、ダミーを宣言しておくといいです。
例えば、以下のような感じです。
struct st_ex { int m_data1 : 1; int m_data2 : 2; int m_data3 : 3; int m_dummy : 2; };
6ビットしか使わないの簡単な例です。また、ビット数の設定を「0」にすると、メモリの境界までパディングされます。
ビットフィールドの使い方
ビットフィールドも構造体ですから、通常の構造体と同じように利用できます。
#include <stdio.h> struct st_ex { int m_data1 : 1; int m_data2 : 2; int m_data3 : 3; int m_dummy : 2; }; void main(void) { struct st_ex ex_bit; ex_bit.m_data1 = 1; ex_bit.m_data2 = 2; ex_bit.m_data3 = 5; }
ただし、利用するビットは決まっていますので、m_data1には2以上の値を代入することは出来ません。仮に代入しても、最下位ビットの1か0しか反映されません。
同様にm_data2には4以上、m_data3には8以上の値は上位のビットが切り捨てられたものが代入されます。
ビットフィールドを使う場面はあるのか?
こういってしまうと、身も蓋もないのですが、ビットフィールドを使うことは、ほとんどありません。
なぜなら、ビット演算子が用意されているからです。
例えば、ある変数の最下位ビットを1にしたい場合は、
a = a | 0x01;
とすれば、aの最下位ビットを1にできますし、逆に0にしたい場合は、
a = a & ~0x01;
とすれば、実現できます。
処理速度を考えた場合、ビットフィールドを使うより、こちらの方が有利です。
では、どういうときにビットフィールドを使うのか、ということになりますね。
組み込みマイコンでは、デバイスドライバと言われる、いわゆるハードウェアのレジスタ(アドレス)を操作するときに、構造体のビットフィールドでヘッダファイルが定義されています。
したがって、そのときはビットフィールドを使うことになります。
先ほどもお話ししたように、ビットフィールドは構造体なので、アクセスの仕方は何も変わりません。
そういった意味では、プログラマーがビットの処理を扱っているという感覚なしに使えるところが、メリットかもしれません。
まとめ
ビットフィールドの使い方についてお話ししました。構造体が理解できているのなら、この分野はさほど難しい話ではないと思います。
ビットフィールドを使わなくても、ビット演算子を用いれば、プログラムは書けますので、ビットフィールドがあるということだけ知っておけばいいと思います。
実際に自分で作って使用する機会は、ほぼないと思うからです。
しかし、構造体そのものを利用することはあります。その時、「この部分だけ、ビット扱いだな。」というときに、メンバに含めてしまえば、データ処理が一元化できるので、覚えておいて損はないでしょう。