Namespaces
Variants

Bit-field

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

明示的なサイズ(ビット単位)でクラスのデータメンバを宣言します。隣接するビットフィールドメンバは、個々のバイトを共有し、またまたがるようにパックされる場合とされない場合があります。

ビットフィールド宣言は、以下の宣言子を使用する クラスデータメンバー宣言 です:

識別子  (オプション) 属性  (オプション) : サイズ (1)
識別子  (オプション) 属性  (オプション) : サイズ 波括弧または等号初期化子 (2) (C++20以降)

ビットフィールドの は、 宣言構文 decl-specifier-seq によって導入されます。

attr - (since C++11) 任意の数の attributes のシーケンス
identifier - 宣言されるビットフィールドの名前。名前はオプション:無名ビットフィールドは指定された数の padding bits を導入する。
size - ゼロ以上の値を持つ integral constant expression 。ゼロより大きい場合、このビットフィールドが占有するビット数。値ゼロは無名ビットフィールドにのみ許可され、 特別な意味 を持つ。
brace-or-equal-initializer - このビットフィールドで使用される default member initializer

目次

説明

ビットフィールドの型は、整数型( bool を含む)または(CV修飾された可能性のある)列挙型のみが可能です。無名ビットフィールドはCV修飾型で宣言できません。

ビットフィールドは staticデータメンバ にすることはできません。

ビットフィールドの prvalue は存在しません:左辺値から右辺値への変換は常にビットフィールドの基底型のオブジェクトを生成します。

ビットフィールド内のビット数は、それが保持できる値の範囲の上限を設定します:

#include <iostream>
struct S
{
    // 3ビットの符号なしフィールド、許容値は0...7
    unsigned int b : 3;
};
int main()
{
    S s = {6};
    ++s.b; // ビットフィールドに値7を格納
    std::cout << s.b << '\n';
    ++s.b; // 値8はこのビットフィールドに収まらない
    std::cout << s.b << '\n'; // 形式的には実装定義、通常は0
}

出力例:

7
0

複数の隣接するビットフィールドは通常、一緒にパックされます(ただし、この動作は実装定義です):

#include <bit>
#include <cstdint>
#include <iostream>
struct S
{
    // 通常2バイトを占有:
    unsigned char b1 : 3; // 最初の3ビット(1バイト目)はb1
    unsigned char    : 2; // 次の2ビット(1バイト目)は未使用としてブロック
    unsigned char b2 : 6; // b2の6ビット - 1バイト目に収まらない => 2バイト目から開始
    unsigned char b3 : 2; // b3の2ビット - 2バイト目の最後のビット
};
int main()
{
    std::cout << sizeof(S) << '\n'; // 通常2を出力
    S s;
    // 識別可能なフィールド値を設定
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;
    // Sのフィールドレイアウトを表示
    auto i = std::bit_cast<std::uint16_t>(s);
    // 通常 1110000011110111 を出力
    // 内訳: └┬┘├┘└┬┘└─┬──┘└┤
    //       b1 u  a   b2  b3
    // ここで「u」は構造体で指定された未使用の:2を、「a」は次のフィールドを
    // バイト境界に揃えるためのコンパイラ追加のパディングを示す
    // バイトアライメントはb2の型がunsigned charと宣言されているため発生;
    // b2がuint16_tで宣言されていた場合、「a」はなく、b2は「u」に隣接する
    for (auto b = i; b; b >>= 1) // LSBファーストで出力
        std::cout << (b & 1);
    std::cout << '\n';
}

出力例:

2
1110000011110111

サイズゼロの特別な無名ビットフィールドは、パディングを強制的に分割するために使用できます。これは、次のビットフィールドがその割り当て単位の先頭から始まることを指定します:

#include <iostream>
struct S
{
    // will usually occupy 2 bytes:
    // 3 bits: value of b1
    // 5 bits: unused
    // 2 bits: value of b2
    // 6 bits: unused
    unsigned char b1 : 3;
    unsigned char :0; // start a new byte
    unsigned char b2 : 2;
};
int main()
{
    std::cout << sizeof(S) << '\n'; // usually prints 2
                                    // would usually print 1 if not for
                                    // the padding break in line 11
}

出力例:

2

ビットフィールドの指定サイズがその型のサイズより大きい場合、値は型によって制限されます: std:: uint8_t b : 1000 ; は依然として [ 0 , 255 ] の範囲の値を保持します。余分なビットは パディングビット となります。

ビットフィールドは必ずしもバイトの先頭から始まるわけではないため、ビットフィールドのアドレスを取得することはできません。ビットフィールドへのポインタおよび非const参照は不可能です。 const参照の初期化 時にビットフィールドから初期化する場合、一時オブジェクトが作成され(その型はビットフィールドの型)、ビットフィールドの値でコピー初期化され、参照はその一時オブジェクトに束縛されます。

ビットフィールドに対する デフォルトメンバ初期化子 は存在しません: int b : 1 = 0 ; および int b : 1 { 0 } は不適格です。

(C++20まで)

ビットフィールドのサイズとデフォルトメンバ初期化子の間で曖昧性がある場合、有効なサイズを形成する最長のトークン列が選択されます:

int a;
const int b = 0;
struct S
{
    // 単純なケース
    int x1 : 8 = 42; // OK; "= 42" はブレースまたは等号初期化子
    int x2 : 8 {42}; // OK; "{42}" はブレースまたは等号初期化子
    // 曖昧性
    int y1 : true ? 8 : a = 42;   // OK; ブレースまたは等号初期化子は存在しない
    int y2 : true ? 8 : b = 42;   // エラー: const int に代入できない
    int y3 : (true ? 8 : b) = 42; // OK; "= 42" はブレースまたは等号初期化子
    int z : 1 || new int{0};      // OK; ブレースまたは等号初期化子は存在しない
};
(C++20から)

注記

ビットフィールドの以下のプロパティは 実装定義 です:

  • 符号付きビットフィールドに範囲外の値を代入または初期化した結果の値、あるいは符号付きビットフィールドをその範囲を超えてインクリメントした結果の値。
  • クラスオブジェクト内でのビットフィールドの実際の割り当て詳細に関するすべての事項。
  • 例えば、一部のプラットフォームではビットフィールドはバイトをまたがず、他のプラットフォームではまたぎます。
  • また、一部のプラットフォームではビットフィールドは左から右にパックされ、他のプラットフォームでは右から左にパックされます。

Cプログラミング言語において、ビットフィールドの幅は基底型の幅を超えることはできず、明示的に signed または unsigned が指定されていない int ビットフィールドが符号付きか符号なしかは実装定義です。例えば、 int b : 3 ; の値の範囲はC言語では [ 0 , 7 ] または [ - 4 , 3 ] となり得ますが、C++では後者の選択肢のみが許容されます。

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 324 C++98 ビットフィールドへの代入の戻り値が
ビットフィールドかどうかは未規定
左辺値を返す可能性のある演算子に対して
ビットフィールドの仕様を追加
CWG 739 C++98 signed unsigned も指定されていないビットフィールドの
符号付き/符号なしは実装定義
基底型と一貫性を持つ
CWG 2229 C++98 無名ビットフィールドをCV修飾型で宣言可能 禁止
CWG 2511 C++98 ビットフィールド型でCV修飾は許可されていなかった ビットフィールドはCV修飾された
列挙型を持つことができる

参考文献

  • C++23規格 (ISO/IEC 14882:2024):
  • 11.4.10 ビットフィールド [class.bit]
  • C++20規格 (ISO/IEC 14882:2020):
  • 11.4.9 ビットフィールド [class.bit]
  • C++17標準 (ISO/IEC 14882:2017):
  • 12.2.4 ビットフィールド [class.bit]
  • C++14 標準 (ISO/IEC 14882:2014):
  • 9.6 ビットフィールド [class.bit]
  • C++11標準 (ISO/IEC 14882:2011):
  • 9.6 ビットフィールド [class.bit]
  • C++03標準 (ISO/IEC 14882:2003):
  • 9.6 ビットフィールド [class.bit]
  • C++98標準 (ISO/IEC 14882:1998):
  • 9.6 ビットフィールド [class.bit]

関連項目

固定長ビット配列を実装
(クラステンプレート)
省メモリな動的ビットセット
(クラステンプレート特殊化)
ビット操作 (C++20) 個々のビットとビットシーケンスへのアクセス、操作、処理のためのユーティリティ
Cドキュメント for ビットフィールド