ビットフィールド
ビット単位の明示的なサイズを持つクラスデータメンバを宣言します。 隣接するビットフィールドメンバは同じバイトを共有するためにパックされることや複数のバイトにまたがることがあります。
ビットフィールド宣言は以下の宣言子を用いたクラスデータメンバ宣言です。
identifier(オプション) attr(オプション) : size
|
(1) | ||||||||
identifier(オプション) attr(オプション) : size brace-or-equal-initializer
|
(2) | (C++20以上) | |||||||
ビットフィールドの型は宣言の構文の decl-specifier-seq によって導入されます。
| attr(C++11) | - | オプショナルな任意個の属性の並び。 |
| identifier | - | 宣言されているビットフィールドの名前。 名前はオプショナルです。 名前のないビットフィールドは指定されたビット数のパディングを導入します。 |
| size | - | ゼロ以上の値を持つ整数定数式。 ゼロより大きいとき、これはそのビットフィールドが占めるビット数です。 値ゼロは名前のないビットフィールドに対してのみ使用でき、クラス定義内の次のビットフィールドが確保単位の境界で始まることを指定するという、特別な意味を持ちます。 |
| brace-or-equal-initializer | - | このビットフィールドで使用されるデフォルトメンバ初期化子。 |
説明
ビットフィールドのビット数は、保持できる値の範囲の制限を設定します。
#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 <iostream>
struct S {
// これは通常2バイトを占めます。
// 3ビット: b1 の値。
// 2ビット: 未使用。
// 6ビット: b2 の値。
// 2ビット: b3 の値。
// 3ビット: 未使用。
unsigned char b1 : 3, : 2, b2 : 6, b3 : 2;
};
int main()
{
std::cout << sizeof(S) << '\n'; // usually prints 2
}
出力例:
2
特別なサイズゼロの無名ビットフィールドはパディングを強制的に分割させることができます。 これは次のビットフィールドが確保単位の先頭で始まることを指定します。
#include <iostream>
struct S {
// これは通常2バイトを占めます。
// 3ビット: b1 の値。
// 5ビット: 未使用。
// 6ビット: b2 の値。
// 2ビット: b3 の値。
unsigned char b1 : 3;
unsigned char :0; // 新しいバイトを開始します。
unsigned char b2 : 6;
unsigned char b3 : 2;
};
int main()
{
std::cout << sizeof(S) << '\n'; // 通常、 2 を表示します。
}
出力例:
2
ビットフィールドの指定されたサイズがその型のサイズより大きい場合、その値はその型によって制限されます。 std::uint8_t b : 1000; はやはり0から255までの値しか保持できません。 余分なビットは未使用のパディングになります。
ビットフィールドはバイトの先頭で開始するとは限らないため、ビットフィールドのアドレスを取ることはできません。 ビットフィールドへのポインタおよび非 const 参照はできません。 ビットフィールドから const 参照が初期化されたときは、一時オブジェクト (型はビットフィールドの型です) が作成され、そのビットフィールドの値でコピー初期化され、そしてその一時オブジェクトに参照が束縛されます。
ビットフィールドの型には整数型または列挙型のみが使用できます。
ビットフィールドは静的データメンバにできません。
ビットフィールドの prvalue はありません。 左辺値から右辺値への変換は常にそのビットフィールドのベースとなる型のオブジェクトを生成します。
ビットフィールドに対するデフォルトメンバ初期化子はありません。 int b : 1 = 0; および int b : 1 {0} は ill-formed です。 |
(C++20未満) |
|
ビットフィールドのサイズとデフォルトメンバ初期化子の間で曖昧な場合は、有効なサイズを形成するトークンの最も長い並びが選択されます。 int a;
const int b = 0;
struct S {
// 単純なケース。
int x1 : 8 = 42; // OK、「= 42」は brace-or-equal-initializer です。
int x2 : 8 { 42 }; // OK、「{ 42 }」は brace-or-equal-initializer です。
// 曖昧。
int y1 : true ? 8 : a = 42; // OK、 brace-or-equal-initializer は存在しません。
int y2 : true ? 8 : b = 42; // エラー、 const int へは代入できません。
int y3 : (true ? 8 : b) = 42; // OK、「= 42」は brace-or-equal-initializer です。
int z : 1 || new int { 0 }; // OK、 brace-or-equal-initializer は存在しません。
};
|
(C++20以上) |
ノート
ビットフィールドの以下の性質は処理系定義です。
- 符号付きビットフィールドを範囲外の値で代入または初期化した結果の値、または符号付きビットフィールドをその範囲を超えてインクリメントした結果の値。
- クラスオブジェクト内のビットフィールドの実際の確保の詳細に関するすべて。
- 例えば、プラットフォームによっては、ビットフィールドは複数のバイトにまたがりませんが、プラットフォームによっては、またがります。
- また、プラットフォームによっては、ビットフィールドは左から右にパックされますが、プラットフォームによっては、右から左です。
|
(C++14未満) |
C プログラミング言語では、ビットフィールドの幅はベースとなる型の幅を越えることはできません。
参考文献
- C++11 standard (ISO/IEC 14882:2011):
- 9.6 Bit-fields [class.bit]
- C++98 standard (ISO/IEC 14882:1998):
- 9.6 Bit-fields [class.bit]
関連項目
ビットフィールド の C言語リファレンス
| |
| 固定長のビット配列を実装します (クラステンプレート) | |