サーチ…


前書き

Cのほとんどの変数は整数のバイト数のサイズを持っています。ビットフィールドは、整数バイトを必ずしも占有しない構造体の一部です。それらは任意の数のビットとすることができる。複数のビットフィールドを単一の記憶装置にパックすることができる。それらは標準Cの一部ですが、実装が定義する多くの側面があります。彼らはCの中で最も携帯性が低い部分の1つです。

構文

  • 型指定子の識別子:size;

パラメーター

パラメータ説明
型指定子 signedunsignedintまたは_Bool
識別子構造体のこのフィールドの名前
サイズこのフィールドに使用するビット数

備考

ビットフィールドの唯一の移植可能なタイプsignedunsigned signedunsignedまたは_Boolです。プレーンint型が使用できますが、標準では(§6.7.2¶5) ビットフィールドに対しては、指定子int signed intと同じ型またはunsigned intと同じ型を指定するかどうかは実装定義です。

他の整数型は特定の実装で許されるかもしれませんが、それらを使うことは移植性がありません。

ビットフィールド

単純なビットフィールドを使用して、特定のビット数を含む可能性のあるものを記述することができます。

struct encoderPosition {
   unsigned int encoderCounts : 23;
   unsigned int encoderTurns  : 4;
   unsigned int _reserved     : 5;
};

この例では、23ビットの単精度と4ビットのマルチターンを記述するエンコーダを考えます。ビットフィールドは、特定のビット数に関連するデータを出力するハードウェアとのインタフェースに使用されることが多い。もう1つの例は、FPGAとの通信で、FPGAが32ビットセクションでデータをメモリに書き込み、ハードウェアの読み取りを可能にします。

struct FPGAInfo {
    union {
        struct bits {
            unsigned int bulb1On  : 1;
            unsigned int bulb2On  : 1;
            unsigned int bulb1Off : 1;
            unsigned int bulb2Off : 1;
            unsigned int jetOn    : 1;
        };
        unsigned int data;
   };
};

この例では、個々のビットのデータにアクセスしたり、データパケット全体を書き込むこと(FPGAの動作をエミュレートする)のために一般的に使用される構造を示しました。私たちはこのようなビットにアクセスすることができます:

FPGAInfo fInfo;
fInfo.data = 0xFF34F;
if (fInfo.bits.bulb1On) {
    printf("Bulb 1 is on\n");
}

これは有効ですが、C99標準6.7.2.1、アイテム10:

ユニット内のビットフィールドの割り当て順序(高位から低位または低位から高位へ)は、実装定義されています。

このようにビットフィールドを定義するときは、エンディアンを認識する必要があります。そのため、マシンのエンディアンをチェックするためにプリプロセッサディレクティブを使用する必要があります。これの例は次のとおりです。

typedef union {
    struct bits {
#if defined(WIN32) || defined(LITTLE_ENDIAN)
    uint8_t commFailure :1;
    uint8_t hardwareFailure :1;
    uint8_t _reserved :6;
#else
    uint8_t _reserved :6;
    uint8_t hardwareFailure :1;
    uint8_t commFailure :1;
#endif
    };
    uint8_t data;
} hardwareStatus;

ビットフィールドを小さな整数として使う

#include <stdio.h>

int main(void)
{
    /* define a small bit-field that can hold values from 0 .. 7 */
    struct
    {
        unsigned int uint3: 3;
    } small;

    /* extract the right 3 bits from a value */
    unsigned int value = 255 - 2; /* Binary 11111101 */
    small.uint3 = value;          /* Binary      101 */
    printf("%d", small.uint3);

    /* This is in effect an infinite loop */
    for (small.uint3 = 0; small.uint3 < 8; small.uint3++)
    {
        printf("%d\n", small.uint3);
    }

    return 0;
}

ビットフィールドアライメント

ビットフィールドは、文字幅より小さい構造体フィールドを宣言することができます。ビットフィールドは、バイトレベルまたはワードレベルのマスクで実装されます。次の例では、8バイトの構造体が生成されます。

struct C
{
    short s;            /* 2 bytes */
    char  c;            /* 1 byte */
    int   bit1 : 1;     /* 1 bit */
    int   nib  : 4;     /* 4 bits padded up to boundary of 8 bits. Thus 3 bits are padded */
    int   sept : 7;     /* 7 Bits septet, padded up to boundary of 32 bits. */
};

コメントには1つの可能なレイアウトが記載されていますが、標準ではアドレス指定可能なストレージユニットのアライメントが指定されていないため 、他のレイアウトも可能です。

名前のないビットフィールドのサイズは任意ですが、初期化や参照はできません。

ゼロ幅のビットフィールドには名前を付けることができず、次のフィールドをビットフィールドのデータ型で定義された境界に揃えます。これは、ビットフィールド間のビットをパディングすることによって達成される。

構造体 'A'のサイズは1バイトです。

struct A
{
    unsigned char c1 : 3;
    unsigned char c2 : 4;
    unsigned char c3 : 1;
};

構造Bでは、最初の名前のないビットフィールドは2ビットをスキップします。ゼロ幅ビットフィールドの後c2せるc3チャー境界から開始する(SO 3ビットが間にスキップされc2及びc3 3つのパディングビットが後に存在するc4 。このような構造のサイズは2バイトです。

struct B
{
    unsigned char c1 : 1;
    unsigned char    : 2;    /* Skips 2 bits in the layout */
    unsigned char c2 : 2;
    unsigned char    : 0;    /* Causes padding up to next container boundary */ 
    unsigned char c3 : 4;
    unsigned char c4 : 1;
};

ビットフィールドはいつ役に立つのですか?

ビットフィールドは、構造と同様に、多くの変数を1つのオブジェクトにまとめるために使用されます。これにより、メモリ使用量が削減され、組み込み環境で特に役立ちます。

 e.g. consider the following variables having the ranges as given below.
 a --> range 0 - 3
 b --> range 0 - 1
 c --> range 0 - 7
 d --> range 0 - 1
 e --> range 0 - 1

これらの変数を別々に宣言する場合は、それぞれが少なくとも8ビットの整数でなければならず、必要なスペースの合計は5バイトになります。さらに、変数は8ビット符号なし整数(0-255)の全範囲を使用することはありません。ここでは、ビットフィールドを使用できます。

typedef struct {
   unsigned int a:2;
   unsigned int b:1;
   unsigned int c:3;
   unsigned int d:1;
   unsigned int e:1;
} bit_a;

構造内のビットフィールドは他の構造と同じようにアクセスされます。プログラマは、変数が範囲内で書かれていることに注意する必要があります。範囲外の場合、動作は未定義です。

int main(void)
{
   bit_a bita_var;
   bita_var.a = 2;              // to write into element a
   printf ("%d",bita_var.a);    // to read from element a.
   return 0;
}

多くの場合、プログラマはビットフィールドのセットをゼロにしたいと思う。これは要素ごとに行うことができますが、2番目の方法があります。構造体のサイズ以上の符号なしの型で、上記の構造体の共用体を作成するだけです。次に、この符号なし整数をゼロにすることによって、ビットフィールドのセット全体をゼロにすることができる。

typedef union {
    struct {
       unsigned int a:2;
       unsigned int b:1;
       unsigned int c:3;
       unsigned int d:1;
       unsigned int e:1;
    };
    uint8_t data;
} union_bit;    

使用方法は次のとおりです

int main(void)
{
   union_bit un_bit;
   un_bit.data = 0x00;        // clear the whole bit-field
   un_bit.a = 2;              // write into element a
   printf ("%d",un_bit.a);    // read from element a.
   return 0;
}

結論として、ビットフィールドは、限定された範囲を取ることができる多くの変数を持つメモリ制約条件で一般的に使用されます。

ビットフィールド用ではない

  1. ビットフィールドの配列、ビットフィールドへのポインタ、およびビットフィールドを返す関数は使用できません。
  2. アドレス演算子(&)はビットフィールドメンバには適用できません。
  3. ビットフィールドのデータ型は、フィールドのサイズを格納するのに十分な幅でなければなりません。
  4. sizeof()演算子はビットフィールドに適用できません。
  5. ビットフィールドの型typedefを独立して作成する方法はありません(ただし、ビットフィールドを含む構造体のtypedefを作成することは可能です)。
typedef struct mybitfield
{
    unsigned char c1 : 20;   /* incorrect, see point 3 */
    unsigned char c2 : 4;    /* correct */
    unsigned char c3 : 1;
    unsigned int x[10]: 5;   /* incorrect, see point 1 */
} A;

int SomeFunction(void)
{
    // Somewhere in the code
    A a = { … };
    printf("Address of a.c2 is %p\n", &a.c2);      /* incorrect, see point 2 */
    printf("Size of a.c2 is %zu\n", sizeof(a.c2)); /* incorrect, see point 4 */
}


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow