bit field (tsz. bit fields)
A bitmezőkkel bitenként szabályozható egy struktúratag mérete.
struct Flags {
unsigned int a : 1;
unsigned int b : 3;
unsigned int c : 4;
};
Ez azt jelenti:
a
egyetlen bitet foglalb
három bitetc
négy bitetA teljes Flags
struktúra tehát összesen 8 bit, azaz 1 byte-ot is elfoglalhat – attól függően, hogyan igazítja el a fordító.
Flags f;
f.a = 1; // csak az LSB-t állítja be
f.b = 7; // max érték: 2³ - 1 = 7
f.c = 15; // max érték: 2⁴ - 1 = 15
Ha túl nagy értéket adunk:
f.b = 10; // csak az alsó 3 bit lesz megtartva => 10 = 1010 → vágás után: 010
A bitmezők típusa lehet:
unsigned int
(ajánlott)signed int
(óvatosan)bool
(egyes fordítók támogatják)A típus határozza meg:
struct Permissions {
unsigned int read : 1;
unsigned int write : 1;
unsigned int execute : 1;
};
Permissions p;
p.read = 1;
p.write = 0;
p.execute = 1;
Ez 0b101
lesz (binárisan). Akár tömöríthető egyetlen bájtba is.
Előny | Magyarázat | |
---|---|---|
Memóriahatékonyság | Kevés adattartalom → kis hely | |
Egyszerű hozzáférés | f.a = 1; nem kell maszk
|
|
Kód olvashatóbb, mint manuális bitkezelés | myStruct.flag = 1 jobban olvasható, mint `x
|
= (1 << 3);` |
Hátrány | Miért baj |
---|---|
Fordítófüggő elrendezés | Nem hordozható binárisan |
Nehéz bitcímzés | Nem lehet pointerrel címezni tagot |
Nem lehet tömböt csinálni belőlük | pl. int bits : 2; nem lehetséges
|
Általában nem támogatják atomikus műveletek |
A fordító kitöltő (padding) biteket szúrhat be, hogy megfeleljen az igazítási szabályoknak.
struct Example {
unsigned int a : 3;
unsigned int b : 5;
unsigned int c : 8;
};
→ lehet, hogy a
és b
egy int
-en vannak, de c
új int
-en kezdődik.
struct Bits {
unsigned char a : 2;
unsigned char b : 2;
unsigned char c : 4;
};
std::cout << sizeof(Bits); // Lehet 1 vagy 2 byte – fordítótól függ
💡 Használj static_assert(sizeof(...))
vagy offsetof
makrót, ha fontos az elrendezés.
Manuálisan így kéne:
uint8_t flags = 0;
flags |= (1 << 0); // read
flags |= (1 << 2); // execute
Bitmezővel:
struct {
unsigned int read : 1;
unsigned int write : 1;
unsigned int execute : 1;
} perm;
perm.read = 1;
perm.execute = 1;
→ Sokkal tisztább, olvashatóbb.
struct Status {
unsigned int error : 1;
unsigned int warning : 1;
unsigned int ready : 1;
unsigned int busy : 1;
};
Ha jön egy byte:
uint8_t data = 0b1011;
Status* s = reinterpret_cast<Status*>(&data);
std::cout << s->error << s->warning << s->ready << s->busy;
⚠️ Nem hordozható és nem szabványos! Csak tesztkódban ajánlott.
Használható unióval:
union Byte {
struct {
unsigned int low : 4;
unsigned int high : 4;
} nibbles;
uint8_t byte;
};
Ez lehetővé teszi, hogy egy mezőként is és bitenként is elérjük az értéket.
struct State {
uint8_t mode : 2;
uint8_t isActive : 1;
uint8_t hasError : 1;
uint8_t reserved : 4;
};
Ez egy teljes byte, ami 8 biteken belül tartalmaz 3 különféle státuszt + paddinget.
Tulajdonság | Bit Field |
---|---|
Memóriahasználat | Minimalizált, ha helyesen használod |
Címezhetőség | Nem lehet pointerrel elérni |
Bináris kompatibilitás | Nem garantált |
Olvashatóság | Jobb, mint bitmaszkolás |
Használat | Flags, állapotok, beágyazott rendszerek |
Elrendezés | Fordítófüggő (endianness, padding) |
A bit field egy rendkívül hasznos, de veszélyes eszköz a C++ eszköztárában. Használata akkor javasolt, ha bit-szintű optimalizálásra van szükség, de nagy odafigyelést igényel a hordozhatóság és pontos elrendezés miatt.
🧠 Ha precíz, hardverközeli vezérlés kell, a bitmező a barátod. Ha hordozható, absztrakt kód kell, maradj inkább az
enum class
vagybitset
mellett.