[ Pobierz całość w formacie PDF ]
różnych konfiguracji. Jednak w tym przypadku unia jest używana w celu wyrażenia
charakterystyki obiektu, który przechowuje różne typy danych, a nie zaoszczędzenia
zasobów. Unie używane w ten sposób są zwykle zawarte w strukturze zawierającej pole
typu, które określa, jakiego rodzaju dane są przechowywane w unii. To pole jest często
reprezentowane za pomocą typu wyliczeniowego, a jego nazwą jest zwykle type. Poniż-
szy przykład, część biblioteki RPC (ang. Remote Procedure Call; zdalne wywoływanie
procedur), zawiera strukturę używaną do reprezentowania komunikatów RPC. Obsłu-
giwane są dwa różne rodzaje komunikatów: wywołanie oraz odpowiedz. Typ wylicze-
niowy msg_type stanowi rozróżnienie między tymi dwoma typami, natomiast unia zawie-
ra strukturę z elementami danych dla każdego typu40.
enum msg_type {
CALL=0,
REPLY=1
};
[...]
struct rpc_msg {
u_int32_t rm_xid;
enum msg_type rm_direction;
39
netbsdsrc/lib/libc/stdlib/malloc.c: 213.
40
netbsdsrc/include/rpc/rpc_msg.h: 54 158.
Rozdział 3. f& Zaawansowane typy danych języka C 91
union {
struct call_body RM_cmb;
struct reply_body RM_rmb;
} ru;
};
3.3.3. Uzyskiwanie dostępu
do różnych reprezentacji wewnętrznych
Ostatnie użycie unii jest związane z przechowywaniem danych w ramach jednego pola
unii oraz uzyskiwaniem dostępu do innego w celu przeniesienia danych między róż-
nymi reprezentacjami wewnętrznymi. Choć tego rodzaju rozwiązania są z gruntu nie-
przenośne, można bez przeszkód przeprowadzać pewne konwersje. Inne są przydatne
w pewnych określonych przypadkach, zależnych od danej maszyny. Poniższa definicja
struktury jest używana przez program archiwizujący tar w celu reprezentowania infor-
macji o każdym pliku z archiwum41.
union record {
char charptr[RECORDSIZE];
struct header {
char name[NAMSIZ];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char linkflag;
char linkname[NAMSIZ];
char magic[8];
char uname[TUNMLEN];
char gname[TGNMLEN];
char devmajor[8];
char devminor[8];
} header;
};
Aby umożliwić wykrywanie uszkodzenia danych, w polu chksum jest umieszczana suma
bajtów wszystkich rekordów składających się na plik (wliczając w to rekord nagłów-
kowy). Programy wykorzystują składową unii charptr do iteracyjnego przeglądania
danych nagłówka bajt po bajcie, obliczając sumę kontrolną oraz składową header
w celu uzyskania dostępu do określonych pól nagłówka. Ze względu na fakt, że typy
całkowite w języku C (wliczając znaki) są poprawne w zakresie wszystkich możliwych
wzorców bitowych, jakie mogą reprezentować, uzyskiwanie dostępu do wewnętrznej
reprezentacji innych typów języka C (wskazników, liczb zmiennoprzecinkowych i in-
nych typów całkowitych) jako typu całkowitego jest z założenia operacją dozwoloną.
Jednak operacja odwrotna generowanie innego typu poprzez jego reprezentację cał-
kowitą nie zawsze daje poprawne wyniki. W przypadku większości architektur ope-
racją bezpieczną jest generowanie typów niecałkowitych na podstawie danych, które
stanowią starszą wersję kopii ich wartości.
41
netbsdsrc/usr.bin/file/tar.h: 36 54.
92 Czytanie kodu. Punkt widzenia twórców oprogramowania open-source
Poza tym wykorzystanie struktur w celu uzyskania dostępu do zależnych od architektu-
ry elementów danych w pewnym innym formacie jest działaniem z gruntu nieprzeno-
śnym. Może to być przydatne w przypadku interpretowania typu danych w oparciu
o jego reprezentacjÄ™ lub tworzenia typu danych na podstawie jego reprezentacji. W przy-
kładzie z listingu 3.342 unia u jest używana w celu uzyskania dostępu do wewnętrznej
reprezentacji liczby zmiennoprzecinkowej v w formie mantysy, wykładnika oraz zna-
ku. Taka konwersja jest wykorzystywana do rozbicia liczby zmiennoprzecinkowej na
znormalizowany ułamek oraz całkowitą potęgę liczby 2.
Listing 3.3. Uzyskiwanie dostępu do wewnętrznej reprezentacji typu przez wykorzystanie unii
double
frexp(double value, int *eptr) Zwracany wykładnik
{
union {
double v; Wartość jest przechowywana w tym polu
Dostęp do reprezentacji wewnętrznej odbywa się przez to pole
struct {
u_int u_mant2 : 32; Mantysa
u_int u_mant1 : 20;
u_int u_exp : 11; Wykładnik
u_int u_sign : 1;
} s;
} u;
if (value) {
u.v = value; Zachowanie wartości
*eptr = u.s.u_exp - 1022; Pobranie i ustawienie wykładnika ze znakiem
u.s.u_exp = 1022; Wykładnik zerowy
return(u.v); Zwrócenie znormalizowanej mantysy
} else {
*eptr = 0;
return((double)0);
}
}
wiczenie 3.9. Zlokalizuj 20 różnych wystąpień unii na płycie dołączonej do książki
i sklasyfikuj powody ich wykorzystania. Zminimalizuj czas poświęcony każdemu wystą-
pieniu. Utwórz wykres ilustrujący częstotliwość używania tej konstrukcji w zależności
od powodu.
wiczenie 3.10. Zaproponuj przenośną alternatywę względem implementacji nieprze-
nośnych konstrukcji używanych w przypadku struktur i unii. Omów swoją propozycję
w kontekście kosztów implementacyjnych, możliwości konserwacji oraz wydajności.
3.4. Dynamiczne przydzielanie pamięci
Struktura danych, której rozmiar nie jest znany w momencie pisania programu lub wzrasta
w podczas pracy programu, jest przechowywana w pamięci przydzielanej dynamicznie
w trakcie jego działania. Programy odwołują się do pamięci przydzielanej dynamicz-
[ Pobierz całość w formacie PDF ]