名前空間
変種
操作

ビットフィールド

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

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

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

identifier(optional) attr(optional) : size (1)
identifier(optional) attr(optional) : size brace-or-equal-initializer (2) (C++20以降)

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

attr - (C++11以降) 任意の数の属性のシーケンス
identifier - 宣言されるビットフィールドの名前。名前は省略可能です。名前のないビットフィールドは、指定された数のパディングビットを導入します。
size - 0以上の値を持つ整数定数式。0より大きい場合、これはこのビットフィールドが占めるビット数です。値0は名前のないビットフィールドにのみ許可され、特別な意味を持ちます。
brace-or-equal-initializer - このビットフィールドで使用されるデフォルトメンバー初期化子

目次

[編集] 説明

ビットフィールドの型は、整数型(boolを含む)または(cv修飾されている可能性のある)列挙型のみであり、名前のないビットフィールドをcv修飾された型で宣言することはできません。

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

ビットフィールドのprvalueはありません。lvalue-to-rvalue変換は常にビットフィールドの基底型のオブジェクトを生成します。

ビットフィールドのビット数は、保持できる値の範囲に制限を設定します。

#include <iostream>
 
struct S
{
    // three-bit unsigned field, allowed values are 0...7
    unsigned int b : 3;
};
 
int main()
{
    S s = {6};
 
    ++s.b; // store the value 7 in the bit-field
    std::cout << s.b << '\n';
 
    ++s.b; // the value 8 does not fit in this bit-field
    std::cout << s.b << '\n'; // formally implementation-defined, typically 0
}

実行結果の例

7
0

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

#include <bit>
#include <cstdint>
#include <iostream>
 
struct S
{
    // will usually occupy 2 bytes:
    unsigned char b1 : 3; // 1st 3 bits (in 1st byte) are b1
    unsigned char    : 2; // next 2 bits (in 1st byte) are blocked out as unused
    unsigned char b2 : 6; // 6 bits for b2 - doesn't fit into the 1st byte => starts a 2nd
    unsigned char b3 : 2; // 2 bits for b3 - next (and final) bits in the 2nd byte
};
 
int main()
{
    std::cout << sizeof(S) << '\n'; // usually prints 2
 
    S s;
    // set distinguishable field values
    s.b1 = 0b111;
    s.b2 = 0b101111;
    s.b3 = 0b11;
 
    // show layout of fields in S
    auto i = std::bit_cast<std::uint16_t>(s);
    // usually prints 1110000011110111
    // breakdown is:  └┬┘├┘└┬┘└─┬──┘└┤
    //                b1 u  a   b2  b3
    // where “u” marks the unused :2 specified in the struct, and
    // “a” marks compiler-added padding to byte-align the next field.
    // Byte-alignment is happening because b2's type is declared unsigned char;
    // if b2 were declared uint16_t there would be no “a”, b2 would abut “u”.
    for (auto b = i; b; b >>= 1) // print LSB-first
        std::cout << (b & 1);
    std::cout << '\n';
}

実行結果の例

2
1110000011110111

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

#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;は、依然として[0255]の範囲の値を保持します。余分なビットはパディングビットです。

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

ビットフィールドにはデフォルトメンバー初期化子はありません。int b : 1 = 0;int b : 1 {0}は不正です。

(C++20まで)

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

int a;
const int b = 0;
 
struct S
{
    // simple cases
    int x1 : 8 = 42; // OK; "= 42" is brace-or-equal-initializer
    int x2 : 8 {42}; // OK; "{42}" is brace-or-equal-initializer
 
    // ambiguities
    int y1 : true ? 8 : a = 42;   // OK; brace-or-equal-initializer is absent
    int y2 : true ? 8 : b = 42;   // error: cannot assign to const int
    int y3 : (true ? 8 : b) = 42; // OK; "= 42" is brace-or-equal-initializer
    int z : 1 || new int{0};      // OK; brace-or-equal-initializer is absent
};
(C++20以降)

[編集] 注釈

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

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

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

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

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

[編集] 参照

  • C++23標準 (ISO/IEC 14882:2024)
  • 11.4.10 ビットフィールド [class.bit]
  • C++20 standard (ISO/IEC 14882:2020)
  • 11.4.9 ビットフィールド [class.bit]
  • C++17 standard (ISO/IEC 14882:2017)
  • 12.2.4 ビットフィールド [class.bit]
  • C++14 standard (ISO/IEC 14882:2014)
  • 9.6 ビットフィールド [class.bit]
  • C++11 standard (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]

[編集] 関連項目

固定長のビット配列を実装する
(クラステンプレート) [編集]
空間効率の良い動的ビットセット
(class template specialization) [編集]
ビット操作 (C++20) 個々のビットとビットシーケンスにアクセス、操作、処理するためのユーティリティ。
ビットフィールドに関するCドキュメント
English 日本語 中文(简体) 中文(繁體)