C言語でビット単位で扱うときの構造体のビットフィールドの使い方

  • このエントリーをはてなブックマークに追加
Pocket

組み込みプログラムでは、メモリの節約のため、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;

とすれば、実現できます。

処理速度を考えた場合、ビットフィールドを使うより、こちらの方が有利です。

では、どういうときにビットフィールドを使うのか、ということになりますね。

組み込みマイコンでは、デバイスドライバと言われる、いわゆるハードウェアのレジスタ(アドレス)を操作するときに、構造体のビットフィールドでヘッダファイルが定義されています。

したがって、そのときはビットフィールドを使うことになります。

先ほどもお話ししたように、ビットフィールドは構造体なので、アクセスの仕方は何も変わりません。

そういった意味では、プログラマーがビットの処理を扱っているという感覚なしに使えるところが、メリットかもしれません。

まとめ

ビットフィールドの使い方についてお話ししました。構造体が理解できているのなら、この分野はさほど難しい話ではないと思います。

ビットフィールドを使わなくても、ビット演算子を用いれば、プログラムは書けますので、ビットフィールドがあるということだけ知っておけばいいと思います。

実際に自分で作って使用する機会は、ほぼないと思うからです。

しかし、構造体そのものを利用することはあります。その時、「この部分だけ、ビット扱いだな。」というときに、メンバに含めてしまえば、データ処理が一元化できるので、覚えておいて損はないでしょう。

  • このエントリーをはてなブックマークに追加

コメントを残す

*