C言語ビットフィールド:メモリ効率を最大化するテクニック
ビットフィールドの必要性
CプログラムにTRUE/FALSE変数が多数含まれ、それらがstatusという構造体にグループ化されているとします:
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status;
この構造体は8バイトのメモリ空間を必要としますが、実際には各変数に0または1のみを格納します。Cプログラミング言語は、このような状況でメモリ空間をより効率的に利用する方法を提供します。
このような変数を構造体内で使用する場合、変数の幅を定義できます。これはCコンパイラに、そのビット数のみを使用することを伝えます。例えば、上記構造体は以下のように書き換えられます:
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
上記構造体ではstatus変数に4バイトのメモリ空間が必要ですが、実際には2ビットのみで値を格納できます。
幅1ビットの変数を最大32個使用しても、status構造体は4バイトを使用します。しかし33個になると、次のメモリスロットが割り当てられ、8バイトを使用し始めます。以下の例で概念を理解しましょう:
Live Demo#include <stdio.h>
#include <string.h>
/* 通常の構造体 */
struct {
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* ビットフィールドを持つ構造体 */
struct {
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main() {
printf("Memory size occupied by status1 : %d\n", sizeof(status1));
printf("Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
実行結果:
Memory size occupied by status1 : 8 Memory size occupied by status2 : 4
ビットフィールド宣言
構造体内でのビットフィールド宣言の形式:
struct {
type [member_name] : width;
};
ビットフィールドの変数要素を説明する表:
| 番号 | 要素と説明 |
|---|---|
| 1 | type ビットフィールドの値の解釈を決定する整数型。int、signed int、unsigned intが指定可能。 |
| 2 | member_name ビットフィールドの名前。 |
| 3 | width ビットフィールドのビット数。指定された型のビット幅以下でなければならない。 |
事前定義された幅を持つ変数をビットフィールドと呼びます。ビットフィールドは1ビット以上を保持できます。例えば0〜7の値を格納する変数が必要なら、幅3ビットのビットフィールドとして定義できます:
struct {
unsigned int age : 3;
} Age;
上記構造体定義は、age変数が値を格納するのに3ビットのみを使用することをCコンパイラに指示します。3ビットより多く使用しようとすると許可されません。以下の例を試してみましょう:
Live Demo#include <stdio.h>
#include <string.h>
struct {
unsigned int age : 3;
} Age;
int main() {
Age.age = 4;
printf("Sizeof(Age) : %d\n", sizeof(Age));
printf("Age.age : %d\n", Age.age);
Age.age = 7;
printf("Age.age : %d\n", Age.age);
Age.age = 8; /* 3ビットを超える値 */
printf("Age.age : %d\n", Age.age);
return 0;
}
上記コードをコンパイルすると警告が出ますが、実行すると以下の結果になります:
Sizeof(Age) : 4 Age.age : 4 Age.age : 7 Age.age : 0
C言語