120
ROZDZIAŁ 17. WSKAŹNIKI
Wskaźniki na stalą wartość są przydatne między innymi w sytuacji gdy mamy duży obiekt (na przykład strukturę z kilkoma polami). Jeśli przypiszemy taką zmienną do innej zmiennej, kopiowanie może potrwać dużo czasu, a oprócz tego zostanie zajęte dużo pamięci. Przekazanie takiej struktury do funkcji albo zwrócenie jej jaki* wartość funkcji wiąże się z takim samym narzutem. W takim wypadku dobrze jest użyć wskaźnika na stalą wartość.
void funkcja(const duza.struktura *ds)
{
/• czytany z ds i wykonujemy obliczenia */
funkcja(Adane); /• many pewność, że zmienna dane nie zostanie zmieniona •/
Mając styczność z tablicami można się zastanowić, czy nie dałoby się mieć tablic, których rozmiar dostosowuje się do naszych potrzeb a nie jest na stałe zaszyty w kodzie programu. Chcąc pomieścić więcej danych możemy po prostu zwiększyć rozmiar tablicy ale gdy do przechowania l>ędzie mniej elementów okaże się, że marnujemy pamięć. Język C umożliwia dzięki wskaźnikom i dynamicznej alokacji pamięci tworzenie tablic takiej wielkości, jakiej akurat potrzebujemy.
0 co chodzi
Czym jest dynamiczna alokacja pamięci? Normalnie zmienne programu przechowywane są na tzw. stosie (ang. stark) - powstają, gdy program wchodzi do bloku, w którym zmienne są zadeklarowane a zwalniane w momencie, kiedy program opuszcza ten blok. Jeśli deklarujemy tak tablice, to ich rozmiar musi być znany w momencie kompilacji — żeby kompilator wygenerował kod rezerwujący odpowiednią ilość pamięci. Dostępny jest jednak drugi rodzaj rezerwacji (czyli alokacji) pamięci. Jest to alokacja na stercie (mig. tuap). Sterta to olnzar pamięci wspólny dla całego programu, przechowywane są w nim zmienne, których czas życia nie jest związany z poszczególnymi blokami. Musimy sami rezerwx>wać dla nich miejsce
1 to miejsce zwalniać, ale dzięki temu możemy to zrobić w dowolnym momencie działania programu.
Należy pamiętać, że rezerwowanie i zwalnianie pamięci na stercie zajmuje więcej czasu niż analogiczne działania na stosie. Dodatkowo, zmienna zajmuje na stercie więcej miejsca niż na stosie sterta utrzymuje specjalną strukturę, w której trzymane są wolne partie (może to być np. Itsta). Tak więc używajmy dynamicznej alokacji tam, gdzie jest potrzebna dla danych, których rozmiaru nie jesteśmy w stanie przewidzieć na etapie kompilacji lub ich żywotność ma być niezwiązana z blokiem, w którym zostały zaalokowane.
Obsługa pamięci
Podstawową funkcją <k> rezerwacji pamięci jest funkcja malloc. Jest to niezbyt skomplikowana funkcja podając jej rozmiar (w bajtach) potrzebnej pamięci, dostajemy wskaźnik do zaalokowanego oliszaru.
Załóżmy, że clicemy stworzyć tablicę liczb typu float:
int rozmiar; float ‘tablica;
rozmiar ■ 3;
tablica ■ aalloc(rozmiar • sizeof ‘tablica); tablica[0] = 0.1;