C++
Pola bitowe
Szukaj…
Wprowadzenie
Pola bitowe ściśle upakowują struktury C i C ++, aby zmniejszyć rozmiar. Wydaje się to bezbolesne: określ liczbę bitów dla członków, a kompilator działa na zasadzie mieszania bitów. Ograniczeniem jest niemożność pobrania adresu elementu pola bitowego, ponieważ jest on przechowywany razem. sizeof()
jest również niedozwolony.
Koszt pól bitowych jest wolniejszy, ponieważ pamięć musi zostać pobrana, a operacje bitowe zastosowane w celu wyodrębnienia lub zmodyfikowania wartości elementów. Te operacje zwiększają również rozmiar wykonywalnego pliku.
Uwagi
Jak drogie są operacje bitowe? Załóżmy prostą niebitową strukturę pola:
struct foo {
unsigned x;
unsigned y;
}
static struct foo my_var;
W pewnym późniejszym kodzie:
my_var.y = 5;
Jeśli sizeof (unsigned) == 4
, to x jest przechowywane na początku struktury, a y jest przechowywane 4 bajty. Wygenerowany kod asemblera może przypominać:
loada register1,#myvar ; get the address of the structure
storei register1[4],#0x05 ; put the value '5' at offset 4, e.g., set y=5
Jest to proste, ponieważ x nie jest mieszany zy. Ale wyobraź sobie redefiniowanie struktury za pomocą pól bitowych:
struct foo {
unsigned x : 4; /* Range 0-0x0f, or 0 through 15 */
unsigned y : 4;
}
Zarówno x
jak i y
zostaną przydzielone 4 bity, dzieląc jeden bajt. Struktura zajmuje zatem 1 bajt zamiast 8. Rozważ teraz zestaw, aby ustawić y
, zakładając, że skończy w górnej części:
loada register1,#myvar ; get the address of the structure
loadb register2,register1[0] ; get the byte from memory
andb register2,#0x0f ; zero out y in the byte, leaving x alone
orb register2,#0x50 ; put the 5 into the 'y' portion of the byte
stb register1[0],register2 ; put the modified byte back into memory
Może to być dobry kompromis, jeśli mamy tysiące lub miliony takich struktur, i pomaga utrzymać pamięć w pamięci podręcznej lub zapobiega zamianie - lub może nadmuchać plik wykonywalny, aby pogorszyć te problemy i spowolnić przetwarzanie. Tak jak w przypadku wszystkich rzeczy, dokonuj właściwego osądu.
Zastosowanie sterownika urządzenia: Unikaj pól bitowych jako sprytnej strategii implementacji sterowników urządzeń. Układy pól bitowych niekoniecznie są spójne między kompilatorami, przez co takie implementacje są nieprzenośne. Funkcja odczytu-modyfikacji-zapisu w celu ustawienia wartości może nie spełniać oczekiwań urządzeń, powodując nieoczekiwane zachowania.
Deklaracja i sposób użycia
struct FileAttributes
{
unsigned int ReadOnly: 1;
unsigned int Hidden: 1;
};
Tutaj każde z tych dwóch pól zajmie 1 bit pamięci. Jest określony przez : 1
wyrażenie po nazwach zmiennych. Podstawowym typem pola bitowego może być dowolny typ całkowy (8-bitowy int na 64-bitowy int). Zalecane jest użycie unsigned
typu, w przeciwnym razie mogą pojawić się niespodzianki.
Jeśli wymaganych jest więcej bitów, zastąp „1” wymaganą liczbą bitów. Na przykład:
struct Date
{
unsigned int Year : 13; // 2^13 = 8192, enough for "year" representation for long time
unsigned int Month: 4; // 2^4 = 16, enough to represent 1-12 month values.
unsigned int Day: 5; // 32
};
Cała konstrukcja jest za pomocą zaledwie 22 bitów, a przy normalnych ustawieniach kompilatora, sizeof
ta struktura będzie 4 bajty.
Użycie jest dość proste. Po prostu zadeklaruj zmienną i użyj jej jak zwykłej struktury.
Date d;
d.Year = 2016;
d.Month = 7;
d.Day = 22;
std::cout << "Year:" << d.Year << std::endl <<
"Month:" << d.Month << std::endl <<
"Day:" << d.Day << std::endl;