Kris Jamsa Wygraj Z C++ lekcja30


Lekcja 31LEKCJA 31
Korzystanie z wolnej pamięci w C++
Jak już mówiliśmy, gdy deklarujesz w programie tablicę, to kompilator języka C++
przydziela jej pamięć, w której mają być przechowywane elementy tej tablicy. Z
czasem może się zdarzyć tak, że rozmiar twojej tablicy nie będzie dostatecznie
duży, by mogła ona przechowywać potrzebne dane. Na przykład, załóżmy, że
utworzyłeś tablicę na ceny stu artykułów z magazynu. Jeśli później będziesz
musiał przechowywać ceny więcej niż 100 artykułów, to będziesz musiał
zmodyfikować program i ponownie go skompilować. Rozwiązanie alternatywne polega
na tym, by program przydzielał potrzebną ilość pamięci dynamicznie, w czasie
wykonywania się. Na przykład, jeśli program ma rejestrować ceny 100 artykułów,
to przydzieli tyle pamięci, ile zajmie 100 cen. Analogicznie, jeśli program ma
przechowywać ceny tylko 25 towarów, to przydzieli mniej pamięci. W ten sposób,
dzięki dynamicznemu przydzielaniu pamięci, program będzie się zmieniać w
zależności od aktualnych potrzeb bez konieczności dodatkowego programowania. Gdy
twój program przydziela (alokuje) pamięć dynamicznie w czasie wykonywania się,
to podaje ilość potrzebnej pamięci, a C++ zwraca wskaźnik do przydzielonej
pamięci. C++ przydziela pamięć z obszaru noszącego nazwę wolna pamięć (ang. free
store). Podczas tej lekcji przedstawimy kroki, jakie twój program musi wykonać,
aby przydzielać i zwalniać pamięć dynamicznie w czasie wykonywania programu. Pod
koniec tej lekcji będziesz rozumiał następujące zagadnienia podstawowe:
Do przydzielania (alokowania) pamięci podczas wykonywania programu służy
operator new języka C++.
Gdy używasz operatora new, musisz określać ilość potrzebnej pamięci.
Jeśli przy użyciu operatora new uda się przydzielić podaną ilość pamięci, to
zwraca on wskaźnik do początku obszaru pamięci.
Gdy new nie może spełnić wymagań programu co do pamięci (może nie być dość
wolnej pamięci), to operator new zwraca wskaźnik NULL.
Jeśli chcesz zwolnić pamięć przydzieloną przy użyciu operatora new, to
powinieneś użyć operatora delete języka C++.
Przydzielanie pamięci dynamicznie podczas wykonywania programu daje duże
możliwości. Na podstawie programów przedstawionych podczas tej lekcji możesz się
przekonać, że dynamiczne przydzielanie pamięci jest bardzo łatwe.


Używanie operatora new
Operator new języka C++ pozwala przydzielać pamięć podczas wykonywania programu.
Aby użyć tego operatora, musisz podać ile bajtów pamięci jest potrzebne. Na
przykład, załóżmy, że w programie jest potrzebna tablica wielkości 50 bajtów. W
następujący sposób możesz przy użyciu operatora new przydzielić pamięć tablicy:
char *bufor = new char[50];

Jak krótko wspominaliśmy, jeśli operatorowi new udało się przydzielić pamięć, to
zwróci on wskaźnik do początku przydzielonego obszaru pamięci. W naszym
przykładzie, ponieważ program przydziela pamięć, w której ma być przechowywana
tablica znaków, to zwrócony wskaźnik będzie przypisany zmiennej zdefiniowanej
jako wskaźnik do typu char. Jeśli operator nie może przydzielić żądanej ilości
pamięci, to zwróci wskaźnik NULL zawierający wartość o. Za każdym razem, gdy
twoje programy przydzielają dynamicznie pamięć przy użyciu operatora new,
powinny sprawdzać, czy new nie zwrócił wartości NULL.
Dlaczego przydzielać pamięć dynamicznie przy użyciu operatora new?
Wiele programów szeroko wykorzystuje tablice do przechowywania wielu wartości
jednego typu. Programiści podczas projektowania swoich programów starają się
deklarować tablice dostatecznie duże, by sprostały one przyszłym potrzebom
programów. Niestety, gdyby wymagania pamięciowe programu przekroczyły rozmiary
tablic przewidziane przez programistów, to trzeba będzie zmodyfikować i ponownie
skompilować program.
Zamiast wracać do edycji i kompilacji programu, któremu jest tylko potrzebne
więcej pamięci, powinieneś w taki sposób pisać programy, by przydzielały one
potrzebną pamięć dynamicznie podczas wykonywania się przy użyciu operatora new.
W ten sposób twoje programy mogą dostosowywać wykorzystanie pamięci do
zmieniających się potrzeb eliminując konieczność modyfikowania i powtórnego
kompilowania programów.
Na przykład, przedstawiony poniżej program UZ_NEW.CPP przy użyciu operatora new
przydziela pamięć na tablicę zawierającą 100 znaków:
#include

void main(void)
{
char *wskaznik = new char[100];

if (wskaznik != NULL)
cout << "Przydzielono pamięć" << endl;
else
cout << "Błąd podczas przydzielania pamięci" << endl;
}

Jak widzimy, program sprawdza wartość, którą operator new przypisał zmiennej
wskaźnik bezpośrednio po użyciu operatora. Jeśli wskaźnik zawiera NULL, to
znaczy, że operator new nie mógł przydzielić żądanej ilości pamięci. Jeśli
zwrócony wskaźnik jest różny od NULL, to znaczy, że operator new przydzielił
potrzebną pamięć i wskaźnik zawiera adres początku zaalokowanego bloku pamięci.
Jeśli operator new nie może przydzielić żądanej ilości pamięci, to zwraca NULL
Jeśli przydzielasz pamięć przy użyciu operatora new, to może się niekiedy
zdarzyć tak, że twoje żądania pamięci nie mogą zostać spełnione, ponieważ nie ma
dostatecznie dużo pamięci w wolnym obszarze. Jeśli operator new nie może
przydzielić wymaganej pamięci, to przypisuje zwracanemu wskaźnikowi wartość
NULL. Możesz zbadać, czy została przydzielona pamięć, sprawdzając wartość
wskaźnika, tak jak to przedstawiliśmy. Na przykład poniższa instrukcja przy
użyciu operatora new przydziela pamięć tablicy 500 liczb zmiennoprzecinkowych:
float *tablica = new float[1OO];
Aby sprawdzić, czy operator new przydzielił wymaganą pamięć, powinieneś w
programie porównywać wartość zwracanego wskaźnika z NULL w następujący sposób:
if (tablica != NULL)
cout << "Udana alokacja pamięci" << endl;
else
cout << "Operator new nie przydzielił pamięci" << endl;
W przedstawionym wyżej programie wykorzystywaliśmy operator new do przydzielenia
100 bajtów pamięci. Ponieważ ilość potrzebnej pamięci została "na sztywno"
zakodowana w programie, to w celu alokacji większej lub mniejszej jej ilości
należałoby zmodyfikować program i ponownie go skompilować. Jak już
wspominaliśmy, jednym z celów dynamicznego przydzielania pamięć jest
wyeliminowanie konieczności powrotu do edycji kodu i powtarzania kompilacji, gdy
wymagania pamięciowe się zmieniają. Przedstawiony poniżej program PYTAOPAM.CPP
wyświetla prośbę do użytkownika o podanie potrzebnej ilości pamięci, a następnie
przydziela pamięć przy użyciu operatora new:
#include

void main(void)
{
int rozmiar;
char *wskaznik;

cout << "Wprowadź rozmiar tablicy nie większy niż 30000: " << endl;
cin >> rozmiar;

if (rozmiar <= 30000)
{
wskaznik = new char[rozmiar];

if (tablica != NULL)
cout << "Udana alokacja pamięci" << endl;
else
cout << "Nie można przydzielić pamięci" << endl;
}
}

Gdy programy przydzielają pamięć dynamicznie przy użyciu operatora new, to na
ogół potrafią same ustalić, ile pamięci trzeba zaalokować. Jeśli na przykład
twój program przydziela pamięć, w której mają być przechowywane informacje o
pracownikach, to może on zapisywać na pliku liczbę zatrudnionych. Na początku
program wczytuje z pliku liczbę pracowników, a następnie przydziela odpowiednią
ilość pamięci.
Przedstawiony poniżej program BRAKPAM.CPP przydziela pamięć po 100000 znaków
przy użyciu operatora new do chwili, gdy alokacja przestanie być możliwa. Innymi
słowy, program kontynuuje przydzielanie pamięci aż do wyczerpania wolnej
pamięci. Po udanej alokacji pamięci jest wyświetlany komunikat informujący o
tym. Gdy próba przydziału pamięci zakończy się niepowodzeniem, program wyświetli
odpowiedni komunikat i zakończy swoje działanie:
#include

void main(void)
{
char *wskaznik;

do {
wskaznik = new char[10000];
if (wskaznik != NULL)
cout << "Przydzielono 10 000 bajtów pamięci" << endl;
else
cout << "Nie udało się przydzielić pamięci" << endl;
} while (wskaznik != NULL)
}

Uwaga: Jeśli kompilujesz i uruchamiasz programy w środowisku MS-DOS, to możesz
być zaskoczony, gdy brakuje pamięci w wolnym obszarze, jeśli przydzieliłeś już
64 kB. Standardowo większość kompilatorów C++ działających w oparciu o MS-DOS
korzysta z małego modelu pamięci, który daje do dyspozycji 64 kB wolnej pamięci.
Analogicznie, gdy pracujesz w środowisku MS-DOS, to największy obszar pamięci
dostępny dla twoich programów może być ograniczony do 64 kB. Aby dowiedzieć się,
w jaki sposób można zwiększyć rozmiar dostępnej wolej pamięci, zajrzyj do
książki Jamsa's 1001 C/C++ Tips, wydawnictwa Jamsa Press, 1993.
Znaczenie obszaru wolnej pamięci
Za każdym razem, gdy twój program jest uruchamiany, kompilator języka C++
wydziela obszar niewykorzystywanej pamięci zwany wolną pamięcią. Program może
przy użyciu operatora new przydzielać pamięć z tego obszaru podczas wykonywania
się. Dzięki wykorzystywaniu pamięci z wolnego obszaru w ten sposób twoje
programy nie są ograniczone stałymi rozmiarami tablic. Rozmiar wolnego obszaru
pamięci jest różny w różnych systemach operacyjnych i przy różnych modelach
pamięci kompilatora. Przy pisaniu programów, które wykorzystują dużo pamięci z
wolnego obszaru musisz pamiętać o ograniczeniach jego wielkości.


Zwalnianie pamięci, która jest już niepotrzebna
Jak już wiesz, operator new języka C++ umożliwia programom dynamiczne
przydzielanie pamięci podczas ich wykonywania. Gdy w twoim programie
przydzielona pamięć nie jest już potrzebna, należy ją zwalniać przy użyciu
operatora delete. W tym celu musisz po prostu w następujący sposób podać
wskaźnik do obszaru pamięci:
delete wskaznik;

Przedstawiony poniżej program DEL_PAM.CPP korzystając z operatora delete zwalnia
pamięć przydzieloną przy użyciu operatora new:
#include
#include

void main(void)
{
char *wskaznik = new char[100];

strcpy (wskaznik, "Wygraj z C++");

cout << wskaznik << endl;

delete wskaznik;
}

Standardowo, jeśli twoje programy nie zwalniają przed zakończeniem przydzielonej
pamięci, to system operacyjny automatycznie zwalnia pamięć po zakończeniu
wykonywania programu. Jeśli jednak zwalniasz pamięć w programach przy użyciu
operatora delete, gdy tylko przestaniesz jej potrzebować, to pamięć będzie
dostępna dla innych potrzeb (prawdopodobnie w twoim programie przy wywoływaniu
new albo systemowi operacyjnemu).
Drugi przykład

Przedstawiony poniżej program ALOKTAB.CPP przydziela pamięć, w której ma być
przechowywana tablica 1000 liczb całkowitych. Następnie przypisuje elementom
tablicy wartości od 1 do 1000 wyświetlając je na ekranie. Potem program zwalnia
przydzieloną pamięć i alokuje pamięć dla tablicy liczb zmiennoprzecinkowych o
2000 elementów i wstawia do niej wartości od 1.0 do 2000.0:
#include

void main(void)
{
int *tablica_int = new int[1000];
float *tablica_float;
int i;

if (*tablica_int != NULL)
{
for (i = 0; i < 1000; i++)
tablica_int[i] = i + 1;

for (i = 0; i < 1000; i++)
cout << tablica_int[i] << " " ;

deleta tablica_int;
}

if (*tablica_float != NULL)
{
for (i = 0; i < 2000; i++)
tablica_float[i] = (i + 1) * 1.0;

for (i = 0; i < 2000; i++)
cout << tablica_float[i] << " " ;

deleta tablica_float;
}
}

Przyjmij zasadę, że twoje programy zwalniają pamięć przy użyciu operatora
delete, gdy tylko przestają ją wykorzystywać.


Zapamiętaj
Podczas tej lekcji dowiedziałeś się, w jaki sposób możesz w programach
przydzielać pamięć dynamicznie ze zbioru niewykorzystywanych obszarów pamięci
noszących nazwę wolnej pamięci podczas wykonywania programu. Służy do tego
operator new. Dzięki dynamicznemu przydzielaniu pamięci w ten sposób programy
mogą dostosowywać się do zmieniających się potrzeb, bez konieczności
wprowadzania modyfikacji w kodzie i ponownego kompilowania. Podczas lekcji 32
nauczysz się, w jaki sposób można kontrolować przydzielanie wolnej pamięci i
zobaczysz, jakie operacje wykonuje operator new, gdy nie może spełnić żądania
przydziału pamięci. Zanim jednak przejdziemy do następnej lekcji upewnij się, że
opanowałeś już następujące zagadnienia podstawowe:
Dzięki możliwości przydzielania pamięci dynamicznie podczas wykonywania
programu możesz zlikwidować zależność programu od tablic o ustalonym
rozmiarze.
Jeśli operator new przydzieli pamięć w odpowiedzi na żądanie programu, to
zwraca on wskaźnik do przydzielonego obszaru pamięci.
Jeśli operator new nie może przydzielić potrzebnej ilości pamięci, to zwraca
wskaźnik NULL zawierający wartość 0.
Za każdym razem, gdy program dynamicznie przydziela pamięć przy użyciu
operatora new, powinien sprawdzać, czy wartość wskaźnika zwracanego przez ten
operator nie jest równa NULL, co oznacza, że new nie mógł przydzielić pamięci.

Program ma dostęp do pamięci przydzielonej przez operator new przy użyciu
wskaźnika do tablicy.
Operator new przydziela pamięć z bloku niewykorzystywanej pamięci, noszącego
nazwę wolna pamięć.
W zależności od systemu operacyjnego i modelu pamięci komputera rozmiar wolnej
pamięci będzie różny. W środowisku MS-DOS wolna pamięć może być ograniczona do
64KB.
Gdy twój program nie potrzebuje już przydzielonej pamięci, to powinien zwracać
ją z powrotem do wolnej pamięci przy użyciu operatora delete.



WsteczSpis treściDalej



Wyszukiwarka