6 STRUKTURY________
W konsekwencji unia jest strukturą, w której wszystkie składowe są umieszczone bez przesunięcia względem jej początku i która jest dostatecznie duża, aby pomieścić „najobszerniejszą” ze składowych. Położenie unii w pamięci spełnia przy tym wymagania typów wszystkich jej składowych. Operacje dozwolone dla struktur są również dozwolone dla unii: przypisywanie i kopiowanie unii traktowanych jako całość, pobieranie adresu unii i dostęp do ich składowych.
Unię można zainicjować jedynie wartością o typie jej pierwszej składowej, a zatem naszą unię u można zainicjować tylko wartością całkowitą.
Dystrybutor pamięci, opisany w rozdz. 8, pokazuje, jak można skorzystać z unii do wymuszenia określonego położenia zmiennej w pamięci maszyny.
Gdy trzeba oszczędzać pamięć, wówczas pakowanie kilku obiektów w jedno słowo maszyny może się okazać koniecznością. Postępuje się tak zwykle ze zbiorami jedno-bitowych znaczników opisujących stan obiektów, na przykład w tablicach symboli kompilatora. Narzucone z zewnątrz formaty danych, jak w przypadku łączy z urządzeniami zewnętrznymi, często także wymagają dostępu do kawałków słów.
Wyobraźmy sobie fragment kompilatora zarządzający tablicą symboli. Z każdym identyfikatorem w programie są związane pewne informacje, na przykład, że jest lub nie jest słowem kluczowym, że jest lub nie jest zewnętrzny, statyczny itp. Najbardziej zwartym sposobem kodowania takich informacji jest gromadzenie jednobitowych znaczników w pojedynczym obiekcie typu char lub int.
Zazwyczaj robi się to przez zdefiniowanie zbioru „masek” odpowiadających właściwym pozycjom bitów, na przykład w ten sposób:
#define KEYWORD 01 /* słowo kluczowe */
#define EXTERNAL 02 /* obiekt zewnętrzny */
#define STATIC 04 /* obiekt statyczny */
lub też
enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 };
Liczby w maskach muszą być potęgami dwójki. „Żonglowanie” tymi bitami staje się potem sprawą dobrania odpowiednich operatorów przesuwania, maskowania i dopeh niania, znanych nam z rozdz. 2.
6.9 POLA BITOWE ___
powered by
Mi siol
Pewne zwroty pojawiają się bardzo często, np.
flags |= EXTERNAL | STATIC;
ustawia bity EXTERNAL i STATIC w zmiennej flags,
flags &= ~(EXTERNAL | STATIC);
kasuje te bity, a warunek
if ((flags & (EXTERNAL | STATIC)) == 0) ...
jest prawdziwy, kiedy oba bity są skasowane.
Chociaż takie zwroty są chętnie stosowane, język C oferuje alternatywny mechanizm definiowania i bezpośredniej obsługi pól wewnątrz słowa bez korzystania z bitowych operatorów logicznych. Polem bitowym, lub krócej polem, jest zbiór przylegających do siebie bitów znajdujących się w jednej, zależnej od implementacji, jednostce pamięci zwanej „słowem”. Składnia definicji pola i odwołania do pola jest wzorowana na strukturach. Na przykład, zestaw symboli zdefiniowanych powyżej za pomocą #define można zastąpić definicją trzech pól:
struct {
unsigned int is_keyword : 1; unsigned int is__extern : 1; unsigned int is_static : 1;
} flags;
Definiuje ona zmienną o nazwie flags, zawierającą trzy jednobitowe pola. Liczba występująca po dwukropku oznacza rozmiar pola w bitach. Pola zadeklarowano jako unsigned int, aby zagwarantować, że są wielkościami bez znaku.
Odwołania do poszczególnych pól są takie same, jak do innych składowych struktury: flags.is_keyword, flags.is_extern itp. Pola zachowują się jak małe zmienne całkowite i mogą występować w wyrażeniach arytmetycznych na równi z innymi obiektami całkowitymi. Zatem poprzednie przykłady można teraz napisać w sposób bardziej naturalny: wyrażenie
flags.is_extem = flags.is_static = 1;
ustawia bity,
flags. is_extern = flags.is_static = 0; kasuje je, a warunek
if (flags.is_extern == 0 && flags.is_static == 0)... sprawdza, czy oba są skasowane.
201