340 likes | 496 Views
Typy wskaźnikowe, dynamiczne struktury danych. Sterta. przeznaczenie stosu (przypomnienie) sterta. Typ wskaźnikowy. type typ_wskaznikowy = ^typ_wskazywany; np.: type tpr = ^real; { zmienne typu pr będą } { wskaźnikami na zmienne real } var
E N D
Sterta • przeznaczenie stosu (przypomnienie) • sterta
Typ wskaźnikowy type typ_wskaznikowy = ^typ_wskazywany; • np.: type tpr = ^real;{ zmienne typu pr będą} { wskaźnikami na zmienne real } var pr : tpr;{ można było też od razu napisać pr: ^real }
Wskaźnik • wskaźnik jak każda zmienna jest określony przez: • typ • rozmiar – niezależny od typu zmiennejwskazywanej • adres • zbiór operacji – silnie ograniczony • zmienna wskazywana to również zmienna • własności wynikają z jej typu
Wskaźnik • Rozmiar zmiennej wskaźnikowej: 4 bajty • kompilator 32-bitowy: 4B adres • kompilator 16-bitowy: 2B segment + 2B offset • kompilator 16-bitowy, „mały” model pamięci: 2B offset, segment domyślny • Adres bezwzględny w pamięci fizycznej a przestrzeń adresowalna wskaźnikiem
Najważniejsze, oczywiste • Wskaźniki należy inicjalizować przed odwołaniem do zmiennych wskazywanych! • alokacja nowej zmiennej (nie zapomnij o dealokacji) • przypisanie adresu istniejącej zmiennej
Inicjalizacja wskaźnika • Procedury new i dispose var pr : ^real; begin new(pr); { rezerwacja obszaru pamięci (6 bajtów - tyle ma real) } { Teraz można używać zarezerwowanego obszaru } pr^ := 2.718; { tak się odwołujemy do zarezerwowanego obszaru } pr^ := 2*pr^; { identycznie jak wyżej, pr^ jest typu real } writeln(pr^); { identycznie jak wyżej, pr^ jest typu real } { Po zakończeniu używania zarezerwowanego obszaru pamięci} {należy go koniecznie zwolnić!!! } dispose(pr); { Od tej chwili nie należy używać pr^ } end.
Zwolnienie wskaźnika • Nie należy zwalniać niezarezerwowanego obszaru pamięci ani zwalniać ponownie tego samego (już zwolnionego) obszaru pamięci! • Jeśli programista nie zwolni zarezerwowanego przez siebie obszaru pamięci, to nie będzie on dostępny dla następnych wywołań procedury new. Jednak po zakończeniu wykonywania programu wszystkie zarezerwowane obszary zostaną automatycznie zwolnione. • Podobny efekt, ale przed zakończeniem programu, można uzyskać wykorzystując parę procedur mark i release.
nil • wyróżnina, specjalna wartość zmiennej wskaźnikowej każdego typu wskaźnikowego • nil w innych językach
Operator @(TP) type tpr = ^real; var pr : tpr; x : double; t : array [0..5] of byte; begin pr = @x; pr^ := 2.718; {Nie wykonujemy dispose!!! } end. • Operator @ zwraca typ wskaźnikowy nie związany z konkretnym typem bazowym (pointer). Dlatego możliwe jest „mieszanie typów” i odwoływanie się do tego samego obszaru pamięci traktując go jak zmienne różnych typów.
Wskaźniki do funkcji i procedur • Po co nam wskaźniki do funkcji i procedur?
To się przydaje: wskaźniki do struktur zawierających wskaźniki • czy na każdą zmienną alokowaną dynamicznie musi przypadać jeden wskaźnik? type tr2 = ^telem; telem = record{ taki typ będzie czytelniejszy } { po narysowaniu } dana: real; { ew. inne pola } nast: tr2 end;
type tr2 = ^telem; telem = record dana: real; { ew. inne pola } nast: tr2 end; var glowa, p1 : tr2; procedure wypisz(el: tr2); begin while el<>nil do begin writeln(el^.dana); el:=el^.nast end end; Lista dynamiczna
begin new(p1); glowa := p1; { utwórz pierwszy element} p1^.dana := 10.0; p1^.nast := nil; {kolejny element} new(p1); { nowa zmienna dynamiczna! } p1^.nast := glowa; { wstawimy na początek listy } glowa := p1; { czyli to jest nowy początek listy } p1^.dana := 8.0; wypisz(glowa); { usun(glowa); } {wypada posprzątać } end.
Usuwanie listy procedure usun(el: tr2); var p: tr2; begin while el<>nil do begin p:=el; el:=el^.nast; dispose(p); { UWAGA: po tym nie można byłoby } { wykonać el:=el^.nast ! } end end;
Struktury dynamiczne • Lista jednokierunkowa • Lista dwukierunkowa • Listy cykliczne • Drzewo binarne • Inne drzewa • Lista a tablica – kiedy wybrać listę?
Wskaźniki (16-bit TurboPascal) • Funkcja Addr(X):pointer. X może być zmienną lub identyfikatorem programu. • Funkcja Ofs(X):word zwraca offset zmiennej (podprogramu) X. • Funkcja Seg(X):word zwraca segment zmiennej (podprogramu) X. • Funkcja Ptr(Seg, Ofs: Word): Pointer tworzy wskaźnik na podstawie podanych wartości segmentu i offsetu. • Adresy wirtualne (kod 16-bit, maszyna 32-bit).
Wskaźniki (16-bit TurboPascal) • Przypisania dozwolone pomiędzy różnymi typami wskaźników (nie sprawdzane)! • funkcja memavail • funkcja maxavail • typ pointer
Wskaźniki (16-bit TurboPascal) • Tablice predefiniowane mem* i port*(mem, memw, meml) mem[$1234:$5678]:=85 • Słowo kluczowe absolute var a: real; { nowa zmienna } b: integer absolute $b000:0000; { określenie konkretnego } {miejsca w pamięci } c: integer absolute a; { zmienna c w tym samym miejscu} { pamięci co zmienna a }
type tekran = array[0..24,0..79] ofrecord znak, atrybut: byte end; tel = record k: integer; { klucz } wyst: integer; { liczba wystąpień } lewy,prawy: pel end; var ekran: tekran absolute $b800:0 { mode co80; dla mode mono: $b000:0 } e2: tekran; { np. do zapamiętywania zawartości ekranu } begin ekran[0,10].znak:=65; { litera A na 11 pozycji górnego wiersza ekranu } e2:=ekran; { zapamiętanie zawartości całego ekranu } {.....} ekran:=e2; { odtworzenie zawartości całego ekranu } end.
Wskaźniki • UWAGA: struktura s zmniejszy dostępny obszar sterty o nie mniej niż sizeof (s) • Fragmentacja pamięci – to jest problem: użyj maxavail
Przykłady • Wstawianie elementu na koniec listy • Lista dwukierunkowa. • Wyszukiwanie elementu w liście. • Lista cykliczna. • Testowanie cykliczności listy. • Drzewo binarne. • Stos, kolejka. • Uporządkowane drzewo binarne
Listy dynamiczne • Uwaga na przypadki szczególne: • lista jest pusta • lista ma tylko jeden element • jesteśmy na początku/końcu listy • Uwaga na przypadek typowy: • nie początek i nie koniec nie pustej listy
Przykład • Uporządkowane drzewo binarne type pel = ^tel; tel = record k: integer; { klucz } wyst: integer; { liczba wystąpień } lewy,prawy: pel end; var korzen: pel;
procedure wstaw(var p:pel; dana: integer); begin if p=nil then begin new(p); with p^ do{ problematyczne to with! } begin k:=dana; wyst:=1; lewy:=nil; prawy:=nil end end else if dana=p^.k then inc(p^.wyst) else if dana<p^.k then wstaw(p^.lewy,dana) else wstaw(p^.prawy,dana) end;
procedure wypisz(p:pel); begin if p<>nil then begin wypisz(p^.lewy); writeln(p^.k:5,' (',p^.wyst,')'); wypisz(p^.prawy); end end; begin wstaw(korzen,3); wstaw(korzen,1); wstaw(korzen,4); { ....... } wstaw(korzen,5); wypisz(korzen); end.
Przykład • Uporządkowane drzewo binarne – zalety tej struktury dynamicznej type pel = ^tel; tel = record k: integer; { klucz } wyst: integer; { liczba wystąpień } lewy,prawy: pel end; var korzen: pel;
Przykład • Sortowanie listy przez proste wstawianie type tw = ^tel; tel = record dana: real; nast: tw end; const glowa: tw = nil; • Mniej przypadków szczególnych: pomocniczy element na początku listy
type tw = ^tel; tel = record dana: real; nast: tw end; const glowa: tw = nil; procedure wstaw(co: tw); { wyszukuje miejsce i wstawia } procedure wypisz(p: tw); { wypisuje całą listę } procedure usun(var p: tw); { usuwa całą listę } function nowy(x:real):tw; { alokuje element i zwraca jego adres }
procedure wstaw(co: tw); { wyszukuje miejsce i wstawia } var p,p1:tw; begin { Zakładamy, że mamy na początku wartownika!!! } p1:=glowa; p:=glowa; while (p<>nil) and (p^.dana<co^.dana) do begin p1:=p; p:=p^.nast; end; { tutaj p=nil albo p^.dana>=co^.dana, czyli musimy wstawić PRZED p^, czyli ZA p1^ } wstaw_za(co,p1) end;
Jak wstawić za danym elementem? procedure wstaw_za(co, za_czym: tw); { wstawia co^ za za_czym^ } var p:tw; begin if za_czym = nil then begin writeln('Za nilem nie wstawiam'); halt end; co^.nast:=za_czym^.nast; za_czym^.nast:=co end;
procedure wypisz(p: tw); begin writeln('-----------'); while p<>nil do begin writeln(p^.dana:10:5); p:=p^.nast end end; procedure usun(var p: tw); var p1: tw; begin while p<>nil do begin p1:=p^.nast; dispose(p); p:=p1 end end;
function nowy(x:real):tw; var p: tw; begin if maxavail<sizeof(p^) then begin writeln('Zabrakło pamięci'); halt end; new(p); p^.dana:=x; p^.nast:=nil; nowy := p end;
begin writeln('Początek: ',memavail); new(glowa); { to będzie element pusty} glowa^.dana:=-9999;{ zakładamy, że nikt nie wstawi -10000 } glowa^.nast:=nil; wstaw(nowy(2)); { wstawianie w odpowiednie miejsce listy } wstaw(nowy(-333)); wstaw(nowy(4)); wypisz(glowa); usun(glowa); writeln('Koniec: ',memavail); end.