5 WSKAŹNIKI I TABLICE______—--
alloc i afree jest stosem lub listą LIFO (ang. last-in, first-out: ostatni przychodzi, pierwszy wychodzi). Biblioteka standardowa oferuje analogiczne funkcje o nazwach malloc i free, które nie wprowadzają takich ograniczeń; w p. 8.7 pokażemy, jak można je zrealizować.
W najprostszej wersji funkcja alloc przydziela części dużej tablicy znakowej, którą nazwiemy allocbuf. Jest ona prywatną własnością funkcji alloc i afree. Funkcje te będą używać wskaźników, a nie indeksów, nazwa tablicy nie musi więc być znana innym podprogramom. Tablicę możemy zatem zadeklarować jako static w tym pliku źródłowym, w którym mieszczą się obie funkcje - w ten sposób stanie się niedostępna dla otoczenia. W praktyce tablica ta nawet nie musi w ogóle mieć nazwy: możemy ją otrzymać wywołując funkcję malloc lub żądając od systemu operacyjnego wskaźnika do pewnego nie nazwanego bloku pamięci.
Potrzebna jest również informacja o tym, jak duża część tablicy allocbuf jest już zajęta. W tym celu użyjemy wskaźnika allocp, który wskazuje na następny wolny element. Funkcja alloc przed przydzieleniem n znaków sprawcza, czy w tablicy allocbuf jest wystarczająco dużo wolnego miejsca. Jeżeli tak, to funkcja wraca z bieżącą wartością wskaźnika allocp (to znaczy adresem początku wolnego obszaru), jednocześnie zwiększając ten wskaźnik o n, aby wskazywał na kolejny wolny obszar. Jeśli w tablicy nie ma wolnego miejsca, to funkcja alloc zwraca zero. Funkcja afree(p) po prostu wstawia wartość wskaźnika p do allocp, jeżeli p wskazuje na wnętrze tablicy allocbuf.
Oto rysunek, na którym przedstawiono działanie dystrybutora pamięci: przed wywołaniem funkcji alloc:
allocp: ^ |
4 — | ||||
allocbuf: |
*-zajęte-- wolne
po wywołaniu funkcji alloc:
allocbuf:
allocp: > |
4 —— | ||||
zajęte-
-> <-
wolne
#define ALLOCSIZE 10000 /* rozmiar dostępnej pamięci */
static char allocbuf [ALLOCSIZE]; /* pamięć dla alloc */ static char *allocp = allocbuf; /* następna wolna pozycja */
5.4 ARYTMETYKA NA ADRESACH_____
powered by
Mi sio"!
char *alloc(int n) /* podaj wskaźnik do n znaków */
if (allocbuf + ALLOCSIZE - allocp >= n) { /* wystarczy miejsca */ allocp 4= n;
return allocp - n; /* zwróć starą wartość allocp */
} else I* za mało miejsca */ return 0;
void afree(char *p) /* zwolnij pamięć wskazaną przez p */
{
if (p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p;
Ogólnie wskaźnikom można nadawać wartości początkowe tak samo, jak innym zmiennym. Jednak zwykle jedynymi znaczącymi wartościami są zero oraz wyrażenia zawierające adresy uprzednio zdefiniowanych danych właściwego typu. Deklaracja
static char *allocp = allocbuf;
definiuje allocp jako wskaźnik do znaków i inicjuje go tak, aby wskazywał na początek tablicy allocbuf, czyli na pierwszą wolną pozycję przy starcie programu. Można to również napisać inaczej:
static char *allocp = &allocbuf[0];
ponieważ nazwa tablicy jest adresem jej zerowego elementu.
Test
if (allocbuf + ALLOCSIZE - allocp >= n) {/* wystarczy miejsca */
sprawdza, czy w tablicy jest dostatecznie dużo wolnego miejsca, aby spełnić żądanie przydzielenia n znaków. Jeżeli tak, to nowa wartość allocp może wskazywać na miejsce przekraczające koniec obszaru allocbuf co najwyżej o jeden znak. Funkcja alloc zwraca wówczas wskaźnik do początku bloku znaków (zwróć uwagę na deklarację samej funkcji). Jeśli nie, to alloc musi zwrócić jakiś sygnał o braku pamięci. Język C gwarantuje, że zero nigdy nie jest poprawnym adresem danych, a więc może być sygnałem zajścia nienormalnego zdarzenia - w tym przypadku braku miejsca w pamięci.
Wskaźniki i liczby całkowite nie są wymienne. Zero jest jedynym wyjątkiem: stalą zero można przypisać wskaźnikowi, można też porównać wskaźnik ze stałą zero. Często zamiast zera używa się stałej symbolicznej NULL, by podkreślić, że chodzi
141