こんにちは!駆け出しエンジニアのまっきーです。
今回は、ビットフィールドについて解説したいと思います。
組み込みエンジニアとして働いていくからには、メモリ操作の技術はとても大切です。では早速やっていきましょう!
共用体とは
まず、共用体について説明します。
共用体とはデータ構造の一種です。
メモリ領域を複数の変数で共有して扱うことが出来ます。
構造体ととても良く似ています。次のように定義します。
1 2 3 4 5 6 |
union 共用体名(省略可) { メンバー1 メンバー2 ・・・ } 変数名; |
共用体使用例
ナナさんのこちらのサイトからの引用です。
ソファーベッドってソファーとベッドの2つの役割がありますよね。
共用体を使うことで1つで2つの役割を担わせることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <stdio.h> // ソファーベッド共用体 union { char sofa[2]; // ソファー[2人掛け] short bed; // ベッド[1名用] } sofabed; int main(void) { // ソファーとして1バイト単位でアクセス sofabed.sofa[0] = 0x12; sofabed.sofa[1] = 0x34; printf("sofabed.sofa[0]=%X\n",sofabed.sofa[0]); printf("sofabed.sofa[1]=%X\n",sofabed.sofa[1]); // ベッドとして2バイトでアクセス sofabed.bed = 0x5678; printf("sofabed.bed=%X\n",sofabed.bed); return 0; } |
1 2 3 |
sofabed.sofa[0]=12 sofabed.sofa[1]=34 sofabed.bed=5678 |

このように、同じメモリに対して異なるアクセスが可能になります。
とても分かりやすいです。ナナさん、ありがとうございます。
ビットフィールドとは
ひとつのメモリ領域(バイト単位)に名前をつけてビット単位に振り分けるものです
つまり、データをビット単位で扱うことができます。例えば、int型は4バイトです。(環境によります)0と1を表すためだけにこれを用いるのはメモリの無駄になります。
そこで、ビットフィールドをを用いることでメモリを効率よく利用できるようになります。
ビットフィールドは構造体のメンバーとして定義します。
1 2 3 4 5 6 |
struct タグ名 { 型 メンバ名 : ビット数; 型 メンバ名 : ビット数; 型 メンバ名; : }; |
ビットフィールド使用例
実際のビットフィールドの作り方を説明します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <stdio.h> struct bit{ unsigned char A0:1; unsigned char A1:1; unsigned char A2:1; unsigned char A3:1; unsigned char A4:1; unsigned char A5:1; unsigned char A6:1; unsigned char A7:1; }; int main(void){ struct bit BIT; BIT.A0 = 0; BIT.A2 = 1; printf("%d\n",BIT.A0); printf("%d\n",BIT.A2); } |
1 2 |
1 0 |
上の例ではA0とA1にしかアクセスしていませんが、このように一つ一つのビットを操作できるようになります!
ビットフィールドと共用体
共用体とビットフィールドを用いることで、8ビットのフィールドを1バイトとして参照できるようになります。やっていることは、ビットフィールドそのものを共用体としているだけです。
サンプルコードを見てみましょう。
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 |
#include <stdio.h> struct bit{ unsigned char A0:1; unsigned char A1:1; unsigned char A2:1; unsigned char A3:1; unsigned char A4:1; unsigned char A5:1; unsigned char A6:1; unsigned char A7:1; }; union{ unsigned char BYTE; struct bit BIT; }A_RESISTA; int main(void){ A_RESISTA.BIT.A0 = 1; A_RESISTA.BIT.A1 = 1; A_RESISTA.BIT.A2 = 0; A_RESISTA.BIT.A3 = 1; printf("A_RESISTA.BIT.A0=%d\n",A_RESISTA.BIT.A0); printf("A_RESISTA.BIT.A1=%d\n",A_RESISTA.BIT.A1); printf("A_RESISTA.BIT.A2=%d\n",A_RESISTA.BIT.A2); printf("A_RESISTA.BIT.A3=%d\n",A_RESISTA.BIT.A3); printf("A_RESISTA.BYTE=%X\n",A_RESISTA.BYTE); } |
1 2 3 4 5 |
A_RESISTA.BIT.A0=1 A_RESISTA.BIT.A1=1 A_RESISTA.BIT.A2=0 A_RESISTA.BIT.A3=1 A_RESISTA.BYTE=B |
メイン関数の中で、ビットフィールドA_RESISTAの値は0b00001011(0x0B)となっています。
バイト単位で参照しているA_RESISTA.BYTEは16進数で表示されています。Bと表示されているので、1バイト単位で参照できていることがわかりますね!
まとめ
共用体やビットフィールドを活用することで、ビット単位での操作ができます。
このような操作ができるのも、Cならではといった感じがしますね。
最初にメモリ操作は大切とは言ったものの、ビットフィールドを使わずともビット演算で解決してしまうことも多いようです。なので実際の現場ではあまり頻繁に使われるわけではないそうです。
ですが、知っておいて損のない知識であると思います。次回はラズパイで実際にビットフィールドを使ってみようと思います!
ありがとうございました!