Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

C言語のビットフィールドとは?メモリを節約する宣言方法と実例を解説

この記事では、C言語におけるビットフィールド(bit field)の概念と使い方について詳しく解説します。

たとえば、Cプログラムの中にTRUE/FALSEを表す変数が複数あり、それらがstatusという構造体にまとめられているケースを考えてみましょう。

struct {
    unsigned int widthValidated;
    unsigned int heightValidated;
} status;

この構造体は8バイトのメモリ領域を必要とします。しかし実際には、各変数に格納する値は0か1だけです。わずか1ビットで足りる情報のために、それぞれ32ビット分の領域を使うのは非効率です。

このような状況でメモリを有効活用するために、C言語にはビットフィールドという仕組みが用意されています。

ビットフィールドによるメモリの節約

構造体のメンバーを宣言するときにビット幅(使用するビット数)を指定すると、Cコンパイラに対して「そのビット数しか使用しない」ことを伝えられます。先ほどの構造体は、次のように書き直せます。

struct {
    unsigned int widthValidated : 1;
    unsigned int heightValidated : 1;
} status;

この構造体の場合、status変数全体としては4バイトのメモリ領域を占有しますが、値の格納に実際に使われるのは2ビットだけです。

さらに、1ビット幅の変数を32個まで増やしても、この構造体が必要なメモリは4バイトのままです。しかし33個目の変数を追加すると、新たなメモリスロットが割り当てられ、使用量は8バイトに増えます。

次のサンプルコードで、この挙動を確認してみましょう。

サンプルコード

#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

通常の構造体status1が8バイトを消費するのに対し、ビットフィールドを使ったstatus2は半分の4バイトで済んでいることがわかります。

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

構造体内でビットフィールドを宣言する際の基本的な書式は次のとおりです。

struct {
    type [member_name] : width ;
};

各要素の意味は以下の表のとおりです。

要素説明
typeビットフィールドの値をどのように解釈するかを決める整数型。intsigned intunsigned int のいずれかを指定できます。
member_nameビットフィールドの名前です。
widthビットフィールドのビット数です。指定した型のビット幅以下である必要があります。

このように、あらかじめビット幅を指定して宣言された変数を「ビットフィールド」と呼びます。ビットフィールドは必ずしも1ビットである必要はなく、複数ビットを保持することも可能です。

たとえば、0から7までの値を格納したい場合は、3ビット幅のビットフィールドを次のように定義できます。

struct {
    unsigned int age : 3;
} Age;

この構造体定義により、Cコンパイラはage変数が値の格納に3ビットしか使用しないものとして扱います。3ビットで表現できる範囲(0〜7)を超える値を代入しようとしても、そのまま格納することはできません。実際に試してみましょう。

サンプルコード

#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

0から7までは正しく格納されていますが、3ビットの上限を超える8を代入すると、下位3ビットのみが残され値は0になってしまいます。このように、ビットフィールドに格納できる値の範囲はビット幅によって制限される点に注意してください。


  1. C言語で配列内の指定範囲の積(剰余演算)を求める方法

    配列 A、範囲の左端 L、右端 R、そして素数 P を入力として与え、L から R までの範囲内にある要素の総乗(積)を P で割った余りを計算して出力するのが本記事の課題です。下図のように、配列の要素が並んでおり、左端の値 L は 2、右端の値 R は 6 です。プログラムはこの範囲内に含まれる要素の積を順次計算していきます。入出力例Input-: A[] = { 1, 2, 3, 4, 5, 6 } P = 29 L = 2 R = 6 Output-: 24 Input-: A[] = {1, 2, 3, 4, 5, 6}, L = 2 R = 5 P = 113

  2. C/C++の多次元配列とは?基本概念から動的メモリ確保まで徹底解説

    C/C++における多次元配列とは、簡単に言えば「配列の配列」として定義されるデータ構造です。多次元配列では、データが表形式(行優先順/row-major order)でメモリ上に格納されます。 以下の図は、3×3×3の次元を持つ多次元配列のメモリ割り当て戦略を示したものです。 アルゴリズム 2次元配列を動的に確保し、操作するための基本的な手順は以下の通りです。 Begin 配列の次元を宣言する new演算子を使用して2次元配列 a[][] を動的に確保する 配列に要素を格納する 配列の内容を出力する deleteによってメモリを解放する End サン