Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis treci
Przedmowa.....................................................................................7
Wstp..........................................................................................11
Rozdzia 1. Wprowadzenie ..............................................................................15
1.1. Motywy i metody czytania kodu......................................................................... 16
1.1.1. Kod jako literatura ................................................................................... 16
1.1.2. Kod jako model ....................................................................................... 19
1.1.3. Utrzymanie kodu ..................................................................................... 20
1.1.4. Rozwj.................................................................................................... 21
1.1.5. Ponowne wykorzystanie........................................................................... 22
1.1.6. Inspekcje ................................................................................................. 23
1.2. Jak czyta t ksik? ........................................................................................ 24
1.2.1. Konwencje typograficzne......................................................................... 24
1.2.2. Diagramy ................................................................................................ 25
1.2.3. wiczenia................................................................................................ 27
1.2.4. Materia dodatkowy ................................................................................. 27
1.2.5. Narzdzia ................................................................................................ 28
1.2.6. Zarys treci.............................................................................................. 28
1.2.7. Debata na temat najlepszego jzyka.......................................................... 29
Dalsza lektura .......................................................................................................... 30
Spis treci
Rozdzia 3.
Zaawansowane
typy danych jzyka C
Wskaniki, struktury, unie, pami przydzielana dynamicznie oraz deklaracje nazw
typw to podstawowe elementy budowy bardziej wyrafinowanych typw danych i algorytmw jzyka C. Chocia niejednokrotnie ju si z nimi spotkalimy, ich uycie jest
wystarczajco specyficzne, wyspecjalizowane i idiomatyczne, aby powici im wicej
uwagi. W niniejszym rozdziale zostan omwione najczciej stosowane sposoby uycia
wskanikw, struktur i unii w programach oraz przedstawione przykady dla kadego
wzorca uycia. Rozpoznajc funkcje penione przez okrelone konstrukcje jzykowe
mona lepiej zrozumie kod, ktry z nich korzysta.
Umiejtno tworzenia nowych obszarw przechowywania danych w trakcie dziaania programu poprzez dynamiczne przydzielanie i zwalnianie pamici stanowi jedn
z istotnych koncepcji wykorzystywanych obok wskanikw i struktur w celu tworzenia i manipulowania nowymi typami danych. Wystarczy pozna kilka najwaniejszych
idiomw dotyczcych zarzdzania pamici przydzielan dynamicznie, by znika wikszo niejasnoci.
3.1. Wskaniki
Wikszo osb uczcych si jzyka C pocztkowo traktuje wskaniki z pewn doz
szacunku i jednoczenie obawy. Jest prawd, e kod wykorzystujcy wskaniki jest
niekiedy bardzo tajemniczy i czsto stanowi rdo subtelnych bdw oraz nieprzewidzialnych awarii. Wynika to gwnie z faktu, e wskanik jest niskopoziomowym,
oferujcym ogromne moliwoci, ale prymitywnym narzdziem. Naley nauczy si
rozpoznawa sposoby jego uycia, co pozwoli pozna czsto stosowane wzorce zapisu kodu. Nieliczne pozostae fragmenty kodu, ktrych nie bdzie mona odpowiednio
zaklasyfikowa, naley traktowa podejrzliwie.
74
75
Warto zwracana przez funkcj jest wykorzystywana jedynie w celu okrelenia bdu.
Wiele funkcji interfejsu API systemu Windows wykorzystuje tak konwencj. Po stronie obiektu wywoujcego zwykle widzi si odpowiedni argument przekazywany do
funkcji poprzez zastosowanie operatora adresu (&) wzgldem zmiennej.
Argumenty wskanikowe s rwnie uywane w celu uniknicia narzutu zwizanego
z kopiowaniem duych elementw przy kadym wywoaniu funkcji. Takie argumenty to
zwykle struktury, rzadziej liczby zmiennoprzecinkowe o podwjnej precyzji. Tablice s
zawsze przekazywane przez referencj i w przypadku wikszoci architektur inne proste
typy danych wydajniej jest kopiowa w momencie wywoania funkcji ni przekazywa
je przez referencj. Ponisza funkcja wykorzystuje dwa argumenty typu strukturalnego
2
XFree86-3.3/xc/doc/specs/PEX5/PEX5.1/SI/xref.h: 113.
XFree86-3.3/xc/doc/specs/PEX5/PEX5.1/SI/xref.c: 268.
76
(now oraz then) przekazywane przez referencj tylko w celu obliczenia wyniku bez modyfikowania treci struktur. Zapewne zapisano j w ten sposb w celu uniknicia narzutu
zwizanego z kopiowaniem struktury timeval przy kadym wywoaniu5.
static double
diffsec(struct timeval *now),
struct timeval *then)
{
return ((now->tv_sec then->tv_sec)*1.0
+ (now->tv_usec then->tv_usec)/1000000.0);
}
Rola kadego z argumentw jest czasem rwnie identyfikowana poprzez uycie komentarza IN lub OUT, jak w poniszej (zapisanej w stylu sprzed ANSI C) definicji funkcji7:
int
atmresolve(rt, m, dst, desten)
register struct rtentry *rt;
struct muf *m;
register struct sockaddr *dst;
register struct atm_pseudohdr *desten;
{
/* OUT */
Inicjalizacja stosu
Odoenie finchar na stos
netbsdsrc/bin/stty/print.c: 56.
77
Tabela 3.1. Kod wykorzystujcy indeksy oraz wskaniki, sucy do uzyskiwania dostpu
do tablicy a o elementach typu T
Kod z uyciem indeksw
int i;
T *p
i = 0
p = a lub p = &a[0]
a[i]
*p
a[i].f
p->f
i++
p++
i += K
p += K
i == N
p == &a[N] lub p == a + N
if (count-- == 0)
return (num);
*bp++ = *--stackp;
} while (stackp > de_stack);
78
s_ad /= 256;
b = s_ad % 256;
a = s_ad / 256;
sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
return addr;
Funkcja tworzy wynik w buforze addr. Gdyby nie zosta on zadeklarowany jako static,
jego zawarto (czytelna reprezentacja adresu internetowego) staaby si nieprawidowa
po zakoczeniu dziaania funkcji. Nawet powysza konstrukcja nie jest w peni bezpieczna. Funkcje wykorzystujce globalne lub statyczne zmienne lokalne nie s w wikszoci przypadkw wielobiene. Oznacza to, e funkcja nie moe zosta wywoana z poziomu wtku innego programu, kiedy jedna instancja tej funkcji ju dziaa. Co gorsza,
w naszym przypadku wynik funkcji musi zosta zapisany w innym miejscu (na przykad
przy uyciu wywoania funkcji strdup) zanim funkcja zostanie wywoana ponownie.
W przeciwnym razie zostaby on nadpisany nowym rezultatem. Przykadowo, przedstawiona implementacja funkcji inet_ntoa nie mogaby zosta uyta zamiast funkcji
10
naddr_ntoa w nastpujcym kontekcie :
(void)fprintf(ftrace, "%s Router Ad from %s to %s via %s life=%d\n",
act, naddr_ntoa(from), naddr_ntoa(to),
ifp ? ifp->int_name : "?",
ntohs(p->ad.icmp_ad_life));
W tym przypadku w celu obejcia opisanego problemu funkcja naddr_ntoa jest uywana
jako kod opakowujcy dla funkcji inet_ntoa, zapewniajc przechowanie jej wyniku
w licie cyklicznej o czterech rnych buforach tymczasowych11.
char *
naddr_ntoa(naddr a)
{
#define NUM_BUFS 4
static int bufno;
static struct {
char
str[16];
} bufs[NUM_BUFS];
char *s;
struct in_addr addr;
/* xxx.xxx.xxx.xxx\0 */
addr.s_addr = a;
s = strcpy(bufs[bufno].str, inet_ntoa(addr));
bufno = (bufno+1) % NUM_BUFS;
return s;
11
12
79
okrelenia, w jaki sposb naley odczytywa lub pomija dane. Funkcja jest wywoywana (listing 3.2:1) z rnymi argumentami (listing 3.2:3) w celu wykonania pocztkowego przegldu danych lub odtworzenia albo pominicia plikw danych.
Listing 3.2. Parametryzacja przy uyciu argumentw funkcji
/*
* Verify that the tape drive can be accessed and
* that it actually is a dump tape.
*/
void
setup()
{
[...]
getfile(xtrmap, xtrmapskip);
[...]
}
/* Prompt user to load a new dump volume. */
void
getvol(int nextvol)
{
[...]
getfile(xtrlnkfile, xtrlnkskip);
[...]
getfile(xtrfile, xtrskip);
[...]
}
/*
* skip over a file on the tape
*/
void
skipfile()
{
curfile.action = SKIP;
getfile(xtrnull, xtrnull);
}
/* Extract a file from the tape. */
void
getfile(void (*fill)(char *, long), (*skip)(char *, long))
{
[...]
(*fill)((char *)buf, (long)(size > TP_BSIZE ?
fssize : (curblk - 1) * TP_BSIZE + size));
[...]
(*skip)(clearedbuf, (long)(size > TP_BSIZE ?
TP_BSIZE : size));
[...]
(*fill)((char *)buf, (long)((curblk * TP_BSIZE) + size));
[...]
}
/* Write out the next block of a file. */
static void
xtrfile(char *buf, long size)
{ [...] }
/* Skip over a hole in a file. */
static void
xtrskip(char *buf, long size)
{ [...] }
/* Collect the next block of a symbolic link. */
static void
[1]
[1]
[1]
[2]
80
Wiele plikw z bibliotek jzyka C, takich jak qsort i bsearch, pobiera argumenty bdce
wskanikami na funkcje, ktre okrelaj sposb ich dziaania13.
getnfile()
{
[...]
qsort(nl, nname, sizeof(nltype), valcmp);
[...]
}
valcmp(nltype *p1, nltype *p2)
{
if ( p1 -> value < p2 -> value ) {
return LESSTHAN;
}
if ( p1 -> value > p2 -> value ) {
return GREATERTHAN;
}
return EQUALTO;
}
14
81
[...]
fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
[...]
(*closefunc)(fin);
W powyszym przykadzie kodu poddany dereferencji wskanik out1 moe zosta uyty
zamiast oryginalnej wartoci zmiennej output. Aliasw uywa si z wielu rnych
powodw.
Kwestie wydajnociowe
Przypisanie wskanika jest bardziej wydajne od przypisania wikszego obiektu. W poniszym przykadzie wskanik curt mgby by zmienn strukturaln, a nie wskanikiem
na tak struktur. Jednak odpowiednia instrukcja przypisania byaby mniej wydajna,
gdy wizaaby si ze skopiowaniem zawartoci caej struktury16.
static struct termios cbreakt, rawt, *curt;
[...]
curt = useraw ? &rawt : &cbreakt;
netbsdsrc/bin/sh/output.c: 81 84.
16
netbsdsrc/lib/libcurses/tty.c: 66 171.
17
82
inicjalizowany tak, aby wskazywa na wpis w tabeli ziaren generatora liczb pseudolosowych. W podobny sposb jest on ustawiany w funkcji srrandom(). W kocu, w funkcji
18
rrandom() zmienna fptr jest uywana do zmodyfikowania wartoci, na ktr wskazuje .
static long rntb[32] = {
3, 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
[...]
};
static long *fptr = &rntb[4];
[...]
void
srrandom(int x)
{
[...]
fptr = &state[rand_sep];
[...]
long
rrandom()
{
[...]
*fptr += *rptr;
i = (*fptr >> 1) & 0x7fffffff;
netbsdsrc/games/rogue/random.c: 62 109.
19
20
netbsdsrc/lib/libc/string/strlen.c: 51 59.
83
size_t
strlen(const char *str)
{
register const char *s;
Czytajc kod operujcy na cigach znakw, naley pamita o istniejcym rozrnieniu na wskaniki znakowe i tablice znakw. Cho obie konstrukcje s czsto uywane
do reprezentowania cigw znakw (ze wzgldu na fakt, e tablica znakw jest automatycznie konwertowana na wskanik na pierwszy znak w tablicy w momencie
przekazania do funkcji), odpowiednie typy i operacje, ktre mona na nich wykonywa,
s rne. Przykadowo, poniszy kod definiuje zmienn pw_file jako wskanik na znak
wskazujcy na sta znakow zawierajc sekwencj "/etc/passwd"21.
static char *pw_file = "/etc/passwd";
Rozmiar zmiennej pw_file na maszynie Autora wynosi 4 i moe ona zosta zmodyfikowana tak, aby wskazywaa inne miejsce, jednak prba zmiany zawartoci pamici,
na ktr wskazuje, spowoduje niezdefiniowane zachowanie.
Z drugiej strony, poniszy wiersz definiuje zmienn line jako tablic znakw zainicjalizowan treci "/dev/XtyXX", po ktrej wystpuje warto zerowa22.
static char line[] = "/dev/XtyXX";
Zastosowanie operatora sizeof wzgldem line daje w wyniku 11. Zmienna line zawsze
bdzie odwoywa si do tego samego obszaru pamici, a elementy ktre zawiera mog
by dowolnie modyfikowane23.
line[5] = 'p';
21
netbsdsrc/distrib/utils/libhack/getpwent.c: 45.
22
netbsdsrc/lib/libutil/pty.c: 71.
23
netbsdsrc/lib/libutil/pty.c: 81.
24
netbsdsrc/usr.bin/less/less/version.c: 575.
25
netbsdsrc/libexec/identd/version.c: 3.
26
netbsdsrc/libexec/identd/identd.c: 77.
84
27
28
85
wiczenie 3.3. Jeeli Czytelnik zna jzyk C++ lub Java, moe sprbowa wyjani,
w jaki sposb mona zminimalizowa (w jzyku C++) lub unikn (w jzyku Java)
uywania wskanikw.
wiczenie 3.4. Czym rni si wskanik jzyka C od adresu pamici? Jaki ma to wpyw
na zrozumienie kodu? Jakie narzdzia wykorzystuj istniejc rnic?
wiczenie 3.5. Czy cigi znakw zawierajce informacje o wersji programu powinny
by reprezentowane jako tablice znakw czy jako wskaniki na cigi znakw? Uzasadnij odpowied.
3.2. Struktury
Konstrukcja jzyka C struct to zgrupowanie elementw danych, ktre umoliwia
uywanie ich wsplnie. Struktury s uywane w programach pisanych w C w celu:
zgrupowania elementw danych uywanych zwykle wsplnie;
zwrcenia wielu elementw danych z funkcji;
konstruowania powizanych struktur danych (podrozdzia 4.7);
odwzorowania organizacji danych w urzdzeniach sprztowych,
W innych przypadkach moe chodzi o liczby zespolone lub pola tworzce wiersze tabeli.
netbsdsrc/games/snake/snake/snake.c: 75 77.
86
je w ramach zwracanej struktury. W poniszym przykadzie funkcja difftv zwraca rnic midzy dwiema wartociami czasowymi wyraonymi w sekundach (tv_sec) oraz
w mikrosekundach (tv_usec) w postaci struktury timeval30.
static struct timeval
difftv(struct timeval a, struct timeval b)
{
static struct timeval diff;
31
32
87
Pakiety danych sieciowych rwnie s czsto kodowane przy uyciu struktur jzyka
C w celu okrelenia struktury ich elementw, co ukazuje ponisza klasyczna definicja
nagwka pakietw TCP33.
struct tcphdr {
u_int16_t th_sport;
u_int16_t th_dport;
tcp_seq
th_seq;
tcp_seq
th_ack;
/*
/*
/*
/*
source port */
destination port */
sequence number */
acknowledgement number */
Wreszcie, struktury s rwnie uywane w celu odwzorowania sposobu przechowywania danych na nonikach danych, na przykad dyskach lub tamach. Przykadowo,
waciwoci dysku partycji systemu MS-DOS okrela si za pomoc tak zwanego bloku
parametrw BIOS. Jego pola odwzorowuje ponisza struktura34.
struct bpb33 {
u_int16_t
u_int8_t
u_int16_t
u_int8_t
u_int16_t
u_int16_t
u_int8_t
u_int16_t
u_int16_t
u_int16_t
u_int16_t
};
bpbBytesPerSec;
bpbSecPerClust;
bpbResSectors;
bpbFATs;
bpbRootDirEnts;
bpbSectors;
bpbMedia;
bpbFATsecs;
bpbSecPerTrack;
bpbHeads;
bpbHiddenSecs;
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
Sposb uporzdkowania pl w ramach struktury jest zaleny od architektury oraz kompilatora. Ponadto reprezentacja rnych elementw w strukturze jest zalena od architektury oraz systemu operacyjnego (system operacyjny moe wymusza na procesorze
dostosowanie si do okrelonej kolejnoci przechowywania bajtw). Nawet proste typy
danych, takie jak liczby cakowite, mog posiada swoj warto przechowywan na
rne sposoby. Std uycie struktur w celu odwzorowania zewntrznych danych stanowi z gruntu nieprzenone rozwizanie.
netbsdsrc/sys/netinet/tcp.h: 43 47.
34
netbsdsrc/sys/msdosfs/bpb.h: 22 34.
35
netbsdsrc/sys/sys/domain.h: 50 65.
/* AF_xxx */
88
};
};
{
f_type;
/*
f_count;
/*
f_msgcount;
/*
ucred *f_cred; /*
fileops {
(*fo_read)(struct
descriptor type */
reference count */
references from message queue */
credentials associated with descriptor */
int
} *f_ops;
off_t
f_offset;
caddr_t
f_data;
/* vnode or socket */
W powyszym przykadzie kady obiekt reprezentujcy otwarty plik lub gniazdo dzieli
swoje metody read, write, ioctl, poll oraz close poprzez skadow struktury f_ops,
wskazujc na dzielon struktur fileops.
36
37
netbsdsrc/sys/sys/file.h: 51 73.
89
wiczenie 3.6. Alternatywnym sposobem przekazywania do funkcji danych typu tablicowego przez warto i zwracania ich jako rzeczywistych wartoci jest zawarcie tablicy
w strukturze. Zlokalizuj takie przykady na pycie doczonej do ksiki. Wyjanij,
dlaczego takie podejcie nie jest wykorzystywane zbyt czsto.
wiczenie 3.7. Zlokalizuj 20 oddzielnych wystpie struktur na pycie doczonej do
ksiki i okrel powd ich wykorzystania. Zapoznaj si z bibliotek struktur danych
oglnego przeznaczenia, tak jak STL jzyka C++ lub java.util. Wska, ktre wystpienia mona by zapisa korzystajc z biblioteki. Zminimalizuj czas powicony kademu wystpieniu.
wiczenie 3.8. Wiele technik, bibliotek oraz narzdzi obsuguje przenone kodowanie
danych w celu ich przenoszenia midzy aplikacjami. Okrel techniki majce zastosowanie w Twoim rodowisku i porwnaj je z uyciem podejcia bazujcego na strukturach jzyka C.
3.3. Unie
Konstrukcja jzyka C union grupuje elementy, ktre dziel ten sam obszar pamici.
Moliwy jest dostp tylko do jednego elementu naraz spord wspuytkujcych taki
obszar. Unie s uywane w jzyku C w celu:
zapewnienia wydajnego wykorzystania pamici;
zaimplementowania polimorfizmu;
umoliwienia dostpu do danych przy uyciu rnych reprezentacji wewntrznych.
/* when free */
/* magic number */
/* bucket # */
/* range magic number */
netbsdsrc/lib/libc/stdlib/malloc.h: 78 92.
90
Bloki pamici mog by zajte lub wolne. W kadym przypadku musz by przechowywane rne wartoci a ze wzgldu na fakt, e blok nie moe by jednoczenie wolny
i zajty, mog one dzieli t sam przestrze pamici. Dodatkowe koszty programistyczne zwizane z konserwacj takiego rozwizania s amortyzowane tysicami elementw,
ktre s przydzielane w wyniku wywoa funkcji bibliotecznych.
Warto zwrci uwag na definicj makro, ktra wystpuje po definicji struktury ovu.
Takie definicje s czsto uywane jako skrcona forma odwoywania si bezporednio
do skadowych struktury w ramach unii bez koniecznoci uywania na pocztku nazwy
skadowej unii. Std przedstawiony kod ma posta39
op->ov_index = bucket;
rm_xid;
rm_direction;
39
netbsdsrc/lib/libc/stdlib/malloc.c: 213.
40
netbsdsrc/include/rpc/rpc_msg.h: 54 158.
};
91
union {
struct call_body RM_cmb;
struct reply_body RM_rmb;
} ru;
Aby umoliwi wykrywanie uszkodzenia danych, w polu chksum jest umieszczana suma
bajtw wszystkich rekordw skadajcych si na plik (wliczajc w to rekord nagwkowy). Programy wykorzystuj skadow unii charptr do iteracyjnego przegldania
danych nagwka bajt po bajcie, obliczajc sum kontroln oraz skadow header
w celu uzyskania dostpu do okrelonych pl nagwka. Ze wzgldu na fakt, e typy
cakowite w jzyku C (wliczajc znaki) s poprawne w zakresie wszystkich moliwych
wzorcw bitowych, jakie mog reprezentowa, uzyskiwanie dostpu do wewntrznej
reprezentacji innych typw jzyka C (wskanikw, liczb zmiennoprzecinkowych i innych typw cakowitych) jako typu cakowitego jest z zaoenia operacj dozwolon.
Jednak operacja odwrotna generowanie innego typu poprzez jego reprezentacj cakowit nie zawsze daje poprawne wyniki. W przypadku wikszoci architektur operacj bezpieczn jest generowanie typw niecakowitych na podstawie danych, ktre
stanowi starsz wersj kopii ich wartoci.
41
netbsdsrc/usr.bin/file/tar.h: 36 54.
92
Poza tym wykorzystanie struktur w celu uzyskania dostpu do zalenych od architektury elementw danych w pewnym innym formacie jest dziaaniem z gruntu nieprzenonym. Moe to by przydatne w przypadku interpretowania typu danych w oparciu
o jego reprezentacj lub tworzenia typu danych na podstawie jego reprezentacji. W przykadzie z listingu 3.342 unia u jest uywana w celu uzyskania dostpu do wewntrznej
reprezentacji liczby zmiennoprzecinkowej v w formie mantysy, wykadnika oraz znaku. Taka konwersja jest wykorzystywana do rozbicia liczby zmiennoprzecinkowej na
znormalizowany uamek oraz cakowit potg liczby 2.
Listing 3.3. Uzyskiwanie dostpu do wewntrznej reprezentacji typu przez wykorzystanie unii
double
frexp(double value, int *eptr)
{
union {
double v;
struct {
u_int u_mant2 : 32;
u_int u_mant1 : 20;
u_int u_exp : 11;
u_int u_sign : 1;
} s;
} u;
if (value) {
u.v = value;
*eptr = u.s.u_exp - 1022;
u.s.u_exp = 1022;
return(u.v);
} else {
*eptr = 0;
return((double)0);
}
}
Zwracany wykadnik
Wykadnik
Zachowanie wartoci
Pobranie i ustawienie wykadnika ze znakiem
Wykadnik zerowy
Zwrcenie znormalizowanej mantysy
netbsdsrc/lib/libc/arch/i386/gen/frexp.c: 48 72.
93
43
}
[...]
free((char *)RRlen);
return (n);
94
Kiedy pami zostanie wyczerpana, program nie moe zbyt wiele zrobi. W wikszoci
sytuacji jedyn skuteczn strategi postpowania jest wywietlenie komunikatu o bdzie i wyjcie. Z tego wzgldu, oraz w celu uniknicia koniecznoci sprawdzania po
kadym wywoaniu wartoci zwrconej przez funkcj malloc, moe zosta ona otoczona funkcj (zwykle noszc nazw xmalloc), ktra wykonuje owo sprawdzenie i jest
zawsze wywoywana zamiast funkcji malloc, jak w poniszym przykadzie44.
void *
xmalloc(u_int size)
{
void *p;
netbsdsrc/usr.bin/sed/misc.c: 63 107.
45
46
}
rup_data[rup_data_idx].host = strdup(host);
rup_data[rup_data_idx].statstime = *st;
rup_data_idx++;
}
95
Dostosowanie przydziau
Przechowanie danych
Nowy indeks
Program skeyinit jest uywany do zmiany hasa lub dodania uytkownika w systemie
uwierzytelniania Bellcore S/Key. Wykonuje on swoje dziaania, a potem natychmiast
koczy dziaanie, zwalniajc zajmowan pami. Jednak taka nieostrona praktyka kodowania moe powodowa problemy, kiedy ten sam kod zostanie wykorzystany ponownie w programie o znacznie duszym czasie dziaania (na przykad jako cz systemu
oprogramowania routera). W takim przypadku program bdzie powodowa wycieki
pamici, co mona sprawdzi korzystajc z polecenia przegldania procesw, takiego
jak ps lub top w systemach uniksowych albo menedera zada w systemie Windows.
Warto zauway, e w powyszym przypadku mona by po prostu uy rozwizania,
gdzie program ustawiaby warto skey.val tak, aby zmienna wskazywaa na tablic
o staym rozmiarze, ktrej pami przydzielono jako zmiennej lokalnej na stosie.
main (int argc, char *argv[])
{
char valspace[16 + 1];
[...]
skey.val = valspace;
netbsdsrc/bin/mv/mv.c: 260.
48
netbsdsrc/usr.bin/skeyinit/skeyinit.c: 34 233.
96
W kilku przypadkach mona spotka programy, ktre zawieraj mechanizm przywracania pamici (ang. garbage collector), automatycznie zwalniajcy nieuywan pami.
Taka technika jest czsto wykorzystywana w sytuacji, gdy przydzielone bloki pamici
jest trudno ledzi, poniewa na przykad s one wspuytkowane przez rne zmienne.
W takich sytuacjach z kadym blokiem zostaje zwizany licznik referencji (ang. reference count). Jego warto jest zwikszana za kadym razem, gdy zostaje utworzone
nowe odwoanie do bloku49:
req.ctx = ctx;
req.event.time = time;
ctx->ref_count++;
Kiedy licznik referencji osiga warto 0, blok nie jest duej uywany i moe zosta
zwolniony51.
if (--ctx->ref_count == 0 && ctx->free_when_done)
XtFree((char*)ctx);
Blok zwrcony przez funkcj alloca jest automatycznie zwalniany, kiedy nastpuje
powrt z funkcji, w ktrej j przydzielono. Nie ma potrzeby wywoywania funkcji free
w celu zwolnienia przydzielonego bloku. Oczywicie, adres pamici przydzielonej przez
funkcj alloca nie powinien nigdy by przekazywany do podprogramw wywoujcych
funkcj, w ktrej j przydzielono, gdy w momencie wyjcia z funkcji adres ten staje si
49
50
51
52
97
niepoprawny. Funkcji alloca nie naley uywa w pewnych rodowiskach programistycznych, takich jak FreeBSD, gdy jest ona uwaana za nieprzenon i zalen od
danej maszyny. W innych przypadkach, takich jak rodowisko GNU, jej uywanie
jest zalecane, gdy redukuje liczb przypadkowych wyciekw pamici.
/* in 4-byte units */
Tablica data element struktury jest uywana jako miejsce na faktyczne dane.
W momencie przydziau pamici przechowujcej struktur jej rozmiar jest rozszerzany
w oparciu o liczb elementw w tablicy data. Od tego momentu element tablicowy
jest uywany tak, jakby zawiera przestrze dla tych elementw55.
if ((np = malloc(sizeof(NAMES) +
ulen + glen + flen + 3)) == NULL)
err(1, "%s", "");
np->user = &np->data[0];
(void)strcpy(np->user, user);
XFree86-3.3/xc/include/extensions/record.h: 99 108.
54
netbsdsrc/bin/ls/ls.h: 69 74.
55
98
widzc zapis cc_t naley go odczytywa jako unsigned char. Programy pisane w jzyku
C uywaj deklaracji typedef w celu zapewnienia obsugi abstrakcji, zwikszenia czytelnoci kodu, zapobieenia problemom zwizanym z przenonoci oraz emulowania
mechanizmu deklaracji klas znanego z jzykw C++ oraz Java.
Wsplne uycie typw przedrostkowych i przyrostkowych w deklaracjach jzyka C
czasem sprawia, e odczytanie deklaracji typedef staje si utrudnione57.
typedef char ut_line_t[UT_LINESIZE];
Jednake rozszyfrowanie takich deklaracji nie nastrcza wikszych problemw. Wystarczy traktowa zapis typedef jako specyfikator przechowywania klasy, podobny do
extern lub static, i odczytywa deklaracj jako definicj zmiennej.
static char ut_line_t[UT_LINESIZE];
56
netbsdsrc/libexec/telnetd/defs.h: 124.
57
netbsdsrc/libexec/rpc.rusersd/rusers_proc.c: 86.
99
W poniszym przykadzie DBT definiuje baz danych thang, struktur zawierajc klucz
lub element danych58.
typedef struct {
void *data;
size_t size;
} DBT;
/* data */
/* data length */
Po tej deklaracji wszystkie podprogramy obsugi dostpu do bazy danych s definiowane jako dziaajce na obiektach DBT (i innych zdefiniowanych za pomoc deklaracji
59
typedef) .
int
int
int
int
u_int);
u_int);
*, u_int);
DBT *);
__signed
unsigned
short
unsigned
int
unsigned
long
unsigned
char
char
short
int
long
int8_t;
u_int8_t;
int16_t;
u_int16_t;
int32_t;
u_int32_t;
int64_t;
u_int64_t;
albo tworzc abstrakcyjne nazwy dla wielkoci o znanej reprezentacji sprztowej, przy
uyciu jednej z wczeniej zadeklarowanych nazw61.
typedef u_int32_t in_addr_t;
typedef u_int16_t in_port_t;
58
netbsdsrc/include/db.h: 72 75.
59
netbsdsrc/lib/libc/db/recno/extern.h: 47 50.
60
netbsdsrc/sys/arch/alpha/include/types.h: 61 68.
61
netbsdsrc/sys/arch/arm32/include/endian.h: 61 62.
62
netbsdsrc/sbin/mount_portal/conf.c: 62 63.
100
Dalsza lektura
Jeeli zagadnienia omwione w niniejszym rozdziale nie s znane Czytelnikowi, warto
poszerzy swoj znajomo jzyka C dziki lekturze pozycji [KR88]. Teoretyczne podstawy implementacji rekurencyjnych typw danych bez jawnego uycia wskanikw
przedstawiono w pozycji Hoareego [Hoa73]. Uycie wskanikw w jzyku C zostao
zwile przedstawione w pozycji Sethiego i Stonea [SS96], za wiele puapek zwizanych z ich uyciem omwiono w pozycji Koeniga [Koe88, s. 27 46]. W pozycji
[CWZ90] zawarto analiz wskanikw i struktur. Istnieje wiele interesujcych artykuw powiconych manipulowaniu strukturami danych opartymi na wskanikach
i wykorzystywaniu waciwoci wskanikw [FH82, Suz82, LH86]. Opis sposobu realizacji funkcji wirtualnych w implementacjach jzyka C++ (jak rwnie, zgodnie
z informacjami podanymi w niniejszym rozdziale, w jzyku C) mona znale w pozycji Ellis i Stroustrupa [ES90, s. 217 237]. Algorytmy zwizane z dynamicznym przydzielaniem pamici omwiono w pozycji Knutha [Knu97, s. 435 452], za zwizane
z nimi praktyczne implikacje w dwch innych rdach [Bru82, DDZ94]. Pojcie mechanizmu oczyszczania pamici z licznikiem referencji omwiono w pozycji Christophera
[Chr84], natomiast zarys implementacji konserwatywnego mechanizmu oczyszczania
pamici mona znale w pozycji Boehma [Boe88].