Pascal pamiec dynamiczna

background image

Pascal

Wskaźniki, procedury

dynamicznego

przydziału pamięci

background image

Wskaźniki

Wskaźniki to adresy pamięci,

które pokazują komputerowi
miejsce gdzie znajdują się
jakieś dane. Możesz
przykładowo przechowywać
w pamięci wskaźnik na
obrazek bitmapowy, podczas
gdy przechowywanie obrazu
w tablicy byłoby niezbyt
praktyczne. Pamięć do której
odnoszą się wskaźniki można
w każdej chwili zwalniać,
deklarować i zmieniać.

background image

Budowa wskaźników*

Ze względu na ryzoko zawieszenia programu wskaźniki są

traktowane jako typy, które należy umiejętnie stosować.
Wskaźniki na ogół zbudowane są z 4 bajtów. Pierwsze 2
określają segment danych, kolejne 2 ofset.

Pamięć konwencjonalna (pierwszy 1MB pamięci
komputera) podzielony jest na segmenty po 16KB każdy.
Tak więc 1 segment ma adres 0, drugi 1, trzeci 2, itd...
Ofset to przemieszczenie względem segmentu. Dzięki
ofsetowi możemy dojść do każdego jednego bajtu pamięci.
Adres typu pointer składa się z dwóch bajtów segmentu,
dwóch ofsetu i jest najczęściej zapisywany w kodzie
szesnastkowym, dzięki czemu jest bardziej czytelny, np.

 $a000:00, $b800:00

background image

Typ pointer

Typ wskaźników nazywa się

pointer. Gdy jakąś zmienną
zadeklarujesz jako poiner, będzie
ona wskaźnikiem. Wskaźniki
mogą adresować różne typy
zmiennych. Mogą adresować
liczby, napisy, tablice, itd. Gdy
adresują konkretny typ, nie
deklaruje się ich jako pointer,
tylko tworzy swój własny typ

background image

Wskaźnik do zmiennej jest po prostu jej adresem, czyli liczbą
opisującą jej położenie w pamięci. Różnicę pomiędzy zmienną
statyczną a wskazywaną ilustruje poniższy rysunek

Sposób obsługi zmiennych statycznych (a) i

wskazywanych (b)

background image

Podstawianie wskaźników

Żeby wskaźnik wskazywał adres

jakiejś zmiennej, możemy
napisać:

var
  wskaznik : pointer; {lub
podobny typ}

{...}

wskaznik :=
@nazwa_zmiennej;

Znacznik @ oznacza pobranie adresu
zmiennej. Gdybyśmy chcieli teraz coś
zapisać pod wskazanym miejscem
wystarczy wywołać wskazywane miejsce,
za pomocą znaku ^

background image

wskaznik^ := nowa_wartosc;

W podanym przykładzie
trzeba by wskaźnik był
zadeklarowany

jako

wskaźnik

na

liczbę.

Gdybyśmy bez rzutowania
i bez określenia rodzaju
wkaźnika próbowali mu
podstawić

wartość

wystąpiłby błąd "illegal
assigment".

background image

Każdy typ danych w Pascalu ma
odpowiadający mu typ wskaźnikowy. Jest to
typ, który nazywa się tak samo jak typ,
któremu odpowiada, tylko poprzedzony jest
znakiem '^'. Na przykład dla
typu Integer jest to ^Integer. Zmienna
takiego typu może przechowywać adres
pewnych danych typu Integer w pamięci -
dalej będziemy pisać, że zmienna "wskazuje
na coś", ponieważ jest ona jakby strzałką
wskazującą jakieś miejsce w pamięci.
Zadeklarowanie zmiennej pamiętającej
adres oraz przypisanie jej adresu innej
zmiennej będzie wyglądać następująco:

var zmienna: Integer;
adres_zmiennej: ^Integer;

begin

zmienna := 15;

adres_zmiennej := @zmienna; { * }

end.

background image

Gdy już jakiejś zmiennej wskaźnikowej przypiszemy adres,
możemy tego adresu użyć, aby zobaczyć co pod tym adresem
się znajduje. Również do tego służy operator '^', jednak tym
razem stawiany jest za nazwą zmiennej. Tak więc zmienna
'adres_zmiennej' służy to zapamiętania gdzie w pamięci
znajduje się pewna zmienna, a za pomocą wyrażenia
'adres_zmiennej^' możemy sprawdzić jaka wartość się tam
znajduje (jaka jest wartość wskazywanej zmiennej).

var zmienna: Integer;
adres_zmiennej: ^Integer;
begin

zmienna := 15;
adres_zmiennej := @zmienna; { * }

writeln(zmienna);

writeln(adres_zmiennej^);

end.

Dwie ostatnie linijki wypiszą dokładnie to samo
(liczbę 15), ponieważ:
- zmienna ma wartość 15, gdyż taka liczba
została jej przypisana,
- adres_zmiennej pokazuje na miejsce w
pamięci, w którym znajduje się zmienna, a
operator '^' powoduje pobranie znajdującej się
tam wartości.

background image

Procedury

Turbo Pascal oferuje kilka metod tworzenia i usuwania
zmiennych dynamicznych, z których najpopularniejszą
realizuje para procedur new i dispose:

new(wskaźnik-do-zmiennej)

dispose(wskaźnik-do-zmiennej)

Procedura new wykonuje czynności związane z
utworzeniem zmiennej wskazywanej, natomiast dispose -
operacje związane z jej usunięciem. Drugą parę
zarządzającą dynamicznym przydziałem pamięci tworzą
procedury GetMem i FreeMem:

GetMem(wskaźnik, rozmiar-

bloku)

FreeMem(wskaźnik, rozmiar-

bloku)

W odróżnieniu od pary new-dispose, procedury te
wykorzystują wskaźniki amorficzne (typu pointer) i
służą do bardziej "wewnętrznego" manipulowania
pamięcią, tj. przydzielania i zwalniania bloków bajtów
(a nie zmiennych wskazywanych jakiegoś konkretnego
typu). Wielkość przydzielanego lub zwalnianego bloku
(w bajtach) określa parametr rozmiar-bloku.
Korzystając z obu grup procedur musisz pamiętać, że
pamięć przydzielona przez GetMem nie może być
zwolniona procedurą dispose i odwrotnie.

background image

mark(wskaźnik)

release(wskaźn

ik)

Wykonanie procedury mark nie powoduje
przydzielenia pamięci ani utworzenia zmiennej, a
jedynie zapamiętanie bieżącej "wysokości" sterty w
zmiennej wskaźnik. Zwolnienia całego obszaru sterty
leżącego powyżej wskaźnika dokonuje się za pomocą
procedury release. Obydwie procedury stosowane są -
podobnie jak GetMem i FreeMem - głównie w
programowaniu niskiego poziomu, do "masowego"
zwalniania pamięci przydzielonej na stercie

background image

Korzystając ze zmiennych dynamicznych
musisz pamiętać, że sterta nie jest
automatycznie porządkowana, toteż kolejne
operacje przydzielenia i zwolnienia bloków
pamięci (dowolną metodą) mogą
doprowadzić do tzw. fragmentacji
, czyli
rozbicia wolnego jeszcze obszaru pamięci
na mniejsze, rozłączne bloki. Ponieważ
rozmiar tworzonej zmiennej dynamicznej
nie może być większy od rozmiaru
największego wolnego bloku pamięci, może
się okazać, że próba utworzenia zmiennej
skończy się niepowodzeniem, mimo iż
wielkość dostępnej pamięci będzie
wystarczająca. Dlatego też właściwą miarą
możliwości utworzenia większej struktury
danych na stercie jest nie
funkcja 

MemAvail

 (zwracająca sumaryczny

rozmiar wolnej pamięci),
lecz 

MaxAvail

 (zwracająca rozmiar

największego wolnego bloku).

background image

program ZmienneDynamiczne; 
 type  TabReal = array[1..5000] of real;{ tablica
liczb }   { rzeczywistych }  
PString = ^string; { wskaźnik do łańcucha }   
var  

s : PString; { zmienna typu wskaźnik do
łańcucha }  
TabTabReal : array[1..100] of ^TabReal;
{ tablica }     { wskaźników }  

Sterta : pointer; { wskaźnik wysokości sterty }  
i : integer; { pomocniczy licznik }   
procedure IlePamieci;  
  begin  

writeln('Wolne: ', MemAvail, ' max. blok: ',

MaxAvail,     '

bajtow.'); 

end;

background image

begin  
writeln(s^);{ zmienna nie utworzona }  
new(s);{ więc ją tworzymy }  
writeln(s^);{ utworzona, lecz nie zainicjalizowana }  
s^ := 'No wreszcie!'; { inicjalizujemy }  
writeln(s^);{ teraz jest OK }  
dispose(s);{ usuwamy }  
writeln(s^);{ zmienna nie została całkowicie zniszczona! }  
mark(Sterta);{ zaznaczamy 'poziom' sterty }  
i := 1;{ tworzymy tablicę tablic dynamicznych }  
while MemAvail > SizeOf(TabReal) do { tyle wierszy )       { ile się
da }   
begin    

IlePamieci; { ile mamy pamięci? }    
new(TabTabReal[i]); { tworzymy nowy wiersz }    
Inc(i); { zwiększamy indeks wiersza }   
end;  

dispose(TabTabReal[3]); { usuwamy jeden wiersz tablicy }  
IlePamieci;  
release(Sterta); { zwalniamy hurtem całą pamięć }  
IlePamieci; 
end.

background image

Pierwsza część programu demonstruje etapy tworzenia,
wykorzystania i usunięcia zmiennej wskazywanej (w
naszym

przypadku

łańcucha)

za

pomocą

procedur new i dispose.

Zauważ,

że

utworzenie

zmiennej wskazywanej nie jest równoznaczne z jej
inicjalizacją, a po wykonaniu procedury dispose treść
łańcucha nie jest niszczona, chociaż może być
niekompletna.
Druga część tworzy typową strukturę wielkiej tablicy,
przydzielając pamięć dla poszczególnych wierszy,
dopóki to jest możliwe. Zauważ, że po usunięciu
trzeciego wiersza tablicy na ogół okazuje się, że rozmiar
największego dostępnego bloku jest mniejszy od
całkowitego rozmiaru wolnego obszaru sterty, co
uniemożliwia

tworzenie

większych

struktur

dynamicznych. Wreszcie instrukcja release zwalnia całą
stertę "wzwyż" począwszy od miejsca zarejestrowanego
w zmiennej Sterta.

background image

Zapamietaj!

- Do przechowywania większych ilości danych możesz w
Pascalu wykorzystać zmienne wskazywane (dynamiczne).
- Zmienne wskazywane są umieszczane na tzw. stercie
(teoretycznie w dowolnym miejscu pamięci). Mogą one być
tworzone i niszczone dynamicznie, w zależności od potrzeb.
- Zmienna wskazywana lokalizowana jest za pomocą
wskaźnika, który zawiera jej adres (miejsce w pamięci).
Wskaźniki mogą wskazywać na zmienne konkretnego typu,
mogą też być wskaźnikami amorficznymi (pointer).
- Przed wykorzystaniem zmiennej dynamicznej należy ją
utworzyć (procedurą new), a po wykorzystaniu - usunąć
(procedurą dispose).
- Do przydzielania i zwalniania bloków pamięci na stercie
służą również procedury GetMem, FreeMem, mark i release.

background image

Autor:

Michał Lis

3si


Document Outline


Wyszukiwarka

Podobne podstrony:
Pamięci dynamiczne RAM, Szkoła, Systemy Operacyjnie i sieci komputerowe, utk, semestr I
Pamięci dynamiczne RAM, Szkoła, Systemy Operacyjnie i sieci komputerowe, utk, semestr I
F2 66A Pamięci dynamiczne Odczyt
pamięci dynamiczne (10 str)
Przykład klasa dynamicznie alokująca pamięć (wielomian)
10 Dynamiczne przydzielanie pamieci
10 Dynamiczna alokacja pamiecii Nieznany (2)
Dynamiczne zarządzanie pamięcią new i delete, Programowanie, wykłady C++
Nowicka, Magdalena Dynamika pamięci publicznej Debata wokół książek Jana Tomasza Grossa a wybrane s
03 Odświeżanie pamięci DRAMid 4244 ppt
wykład 12 pamięć
Wykład z Pascala 2
8 Dzięki za Pamięć

więcej podobnych podstron