Bitfeld
Deklariert ein Klassen-Datenelement mit expliziter Größe in Bits. Benachbarte Bit-Felder können (müssen aber nicht) gepackt werden, um einzelne Bytes gemeinsam zu nutzen und zu durchtrennen.
Eine Bit-Feld-Deklaration ist eine Klassen-Datenelement-Deklaration, die den folgenden Deklarator verwendet
Bezeichner (optional) attr (optional) : Größe |
(1) | ||||||||
Bezeichner (optional) attr (optional) : Größe Klammer-oder-Gleichheits-Initialisierer |
(2) | (seit C++20) | |||||||
Der Typ des Bit-Felds wird durch die decl-specifier-seq der Deklarationssyntax eingeführt.
| attr | - | (seit C++11) Sequenz einer beliebigen Anzahl von Attributen |
| identifier | - | der Name des zu deklarierenden Bit-Felds. Der Name ist optional: Namenslose Bit-Felder führen die angegebene Anzahl von Auffüllbits ein. |
| size | - | ein ganzzahliger konstanter Ausdruck mit einem Wert größer oder gleich Null. Wenn größer als Null, ist dies die Anzahl der Bits, die dieses Bit-Feld belegen wird. Der Wert Null ist nur für namenlose Bit-Felder zulässig und hat eine besondere Bedeutung. |
| Klammer-oder-Gleichheits-Initialisierer | - | Standard-Member-Initialisierer, der mit diesem Bit-Feld verwendet werden soll |
Inhalt |
[bearbeiten] Erklärung
Der Typ eines Bit-Felds kann nur ein ganzzahliger Typ (einschließlich bool) oder ein (möglicherweise cv-qualifizierter) Aufzählungstyp sein. Ein namenloses Bit-Feld kann nicht mit einem cv-qualifizierten Typ deklariert werden.
Ein Bit-Feld kann kein statisches Datenelement sein.
Es gibt keine Bit-Feld-prvalues: Die Umwandlung von lvalues in rvalues erzeugt immer ein Objekt des zugrunde liegenden Typs des Bit-Felds.
Die Anzahl der Bits in einem Bit-Feld begrenzt den Wertebereich, den es speichern kann
#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 }
Mögliche Ausgabe
7 0
Mehrere benachbarte Bit-Felder werden normalerweise zusammengepackt (obwohl dieses Verhalten implementierungsabhängig ist)
#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'; }
Mögliche Ausgabe
2 1110000011110111
Das spezielle namenlose Bit-Feld der Größe Null kann verwendet werden, um Auffüllungen zu erzwingen. Es gibt an, dass das nächste Bit-Feld am Anfang seiner Alloziationseinheit beginnt.
#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 }
Mögliche Ausgabe
2
Wenn die angegebene Größe des Bit-Felds größer ist als die Größe seines Typs, wird der Wert durch den Typ begrenzt: ein std::uint8_t b : 1000; speichert immer noch Werte im Bereich [0, 255]. Die zusätzlichen Bits sind Auffüllbits.
Da Bit-Felder nicht notwendigerweise am Anfang eines Bytes beginnen, kann die Adresse eines Bit-Felds nicht genommen werden. Zeiger und nicht-const Referenzen auf Bit-Felder sind nicht möglich. Beim Initialisieren einer const-Referenz aus einem Bit-Feld wird ein temporäres Objekt erstellt (dessen Typ der Typ des Bit-Felds ist), mit dem Wert des Bit-Felds kopierinitialisiert und die Referenz an dieses temporäre Objekt gebunden.
|
Es gibt keine Standard-Member-Initialisierer für Bit-Felder: int b : 1 = 0; und int b : 1 {0} sind fehlerhaft. |
(bis C++20) |
|
Im Falle einer Mehrdeutigkeit zwischen der Größe des Bit-Felds und dem Standard-Member-Initialisierer wird die längste Sequenz von Token gewählt, die eine gültige Größe bildet. 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 }; |
(seit C++20) |
[bearbeiten] Hinweise
Die folgenden Eigenschaften von Bit-Feldern sind implementierungsabhängig
- Der Wert, der sich aus der Zuweisung oder Initialisierung eines vorzeichenbehafteten Bit-Felds mit einem Wert außerhalb seines Bereichs ergibt, oder aus der Inkrementierung eines vorzeichenbehafteten Bit-Felds über seinen Bereich hinaus.
- Alles über die tatsächlichen Alloziationsdetails von Bit-Feldern innerhalb des Klassenobjekts.
- Zum Beispiel, auf einigen Plattformen durchdringen Bit-Felder keine Bytes, auf anderen schon.
- Ebenso werden auf einigen Plattformen Bit-Felder von links nach rechts, auf anderen von rechts nach links gepackt.
In der Programmiersprache C darf die Breite eines Bit-Felds nicht die Breite des zugrunde liegenden Typs überschreiten, und ob int Bit-Felder, die nicht explizit signed oder unsigned sind, vorzeichenbehaftet oder vorzeichenlos sind, ist implementierungsabhängig. Zum Beispiel, int b : 3; kann den Wertebereich [0, 7] oder [-4, 3] in C haben, aber nur die letztere Wahl ist in C++ erlaubt.
[bearbeiten] Fehlerberichte
Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | angewendet auf | Verhalten wie veröffentlicht | Korrigiertes Verhalten |
|---|---|---|---|
| CWG 324 | C++98 | es war nicht spezifiziert, ob der Rückgabewert einer Zuweisung zu einem Bit-Feld ein Bit-Feld ist |
Bit-Feld-Spezifikationen für Operatoren, die lvalues zurückgeben können |
| CWG 739 | C++98 | Vorzeichenbehaftung von Bit-Feldern, die weder deklariert sind signed noch unsigned waren implementierungsabhängig |
konsistent mit den zugrunde liegenden Typen |
| CWG 2229 | C++98 | namenlose Bit-Felder konnten mit einem cv-qualifizierten Typ deklariert werden | verboten |
| CWG 2511 | C++98 | cv-Qualifizierungen waren in Bit-Feld-Typen nicht erlaubt | Bit-Felder können cv-qualifiziert sein Aufzählungstypen |
[bearbeiten] Referenzen
- C++23 Standard (ISO/IEC 14882:2024)
- 11.4.10 Bit-Felder [class.bit]
- C++20 Standard (ISO/IEC 14882:2020)
- 11.4.9 Bit-Felder [class.bit]
- C++17 Standard (ISO/IEC 14882:2017)
- 12.2.4 Bit-Felder [class.bit]
- C++14 Standard (ISO/IEC 14882:2014)
- 9.6 Bit-Felder [class.bit]
- C++11 Standard (ISO/IEC 14882:2011)
- 9.6 Bit-Felder [class.bit]
- C++03-Standard (ISO/IEC 14882:2003)
- 9.6 Bit-Felder [class.bit]
- C++98 Standard (ISO/IEC 14882:1998)
- 9.6 Bit-Felder [class.bit]
[bearbeiten] Siehe auch
| implementiert eine Bit-Array fester Länge (Klassenvorlage) | |
| speichereffizientes dynamisches Bitset (class template specialization) | |
| Bit-Manipulation (C++20) | Dienstprogramme zum Zugreifen, Bearbeiten und Verarbeiten einzelner Bits und Bitsequenzen |
| C-Dokumentation für Bit-Felder
| |