Dynamicky alokovan promnn v Pascalu Typ ukazatel pro

  • Slides: 18
Download presentation
Dynamicky alokované proměnné v Pascalu Typ ukazatel – pro uložení jedné paměťové adresy proměnná

Dynamicky alokované proměnné v Pascalu Typ ukazatel – pro uložení jedné paměťové adresy proměnná může ukazovat vždy jen na data určitého typu type T = … ; Uk = ^T; var P, Q: Uk; {co chceme dynamicky alokovat} {typ „ukazatel na T“} {proměnné typu „ukazatel na T“} Lze psát přímo var P, Q: ^T; například type T 1 var P 1: type T 2 var P 2: = record X: real; Y: char end; ^T 1; = array[1. . 10] of integer; ^T 2; from Pavel Töpfer's presentation 1

Alokace paměti – procedura new(P) - velikost požadované paměti je určena datovým typem, pro

Alokace paměti – procedura new(P) - velikost požadované paměti je určena datovým typem, pro který je deklarován ukazatel P (doplní překladač) - pozor – původní hodnota proměnné P se ztratí Uvolnění paměti – procedura dispose(P) - velikost uvolňované paměti je určena datovým typem, pro který je deklarován ukazatel P (doplní překladač) Operace s ukazateli - získání nové hodnoty (dosavadní hodnota ukazatele se ztratí) * voláním new(P) * dosazením Q: =P dosazovat lze jen mezi ukazateli téhož typu - konstanta nil = neukazuje nikam Q: =nil kompatibilní se všemi ukazatelovými typy význam: speciální hodnota těch ukazatelů, které momentálně nikam neukazují prevence před možným chybným odkazem, „zakončení“ dynamických datových struktur (bude později) from Pavel Töpfer's presentation 2

-porovnání ukazatelů – relační operátory =, <> if P = Q then … while

-porovnání ukazatelů – relační operátory =, <> if P = Q then … while P <> nil do … porovnávat lze jen ukazatele téhož typu - přístup k dynamicky alokované proměnné, na kterou ukazuje P: P^ P 1^. X : = 3. 14; write(P 1^. Y); for I: =1 to 10 do read(P 2^[I]); - k jedné dynamicky alokované proměnné můžeme přistupovat zároveň (nebo postupně) pomocí více různých ukazatelů new(P); Q : = P; Q^. A : = 15; write(P^. A); {vypíše „ 15“} dispose(Q); {uvolní paměť alokovanou pomocí new(P)} from Pavel Töpfer's presentation 3

Rozlišujte: Q : = P; dosazení hodnoty ukazatele P do ukazatele Q, tzn. Q

Rozlišujte: Q : = P; dosazení hodnoty ukazatele P do ukazatele Q, tzn. Q začne ukazovat na tu proměnnou, na kterou právě ukazuje P (neprovádí se nová alokace) Q^ : = P^; dosazení hodnoty dynamicky alokované proměnné P^ do proměnné Q^ (ta musí být naalokována), hodnoty ukazatelů P, Q se tím nezmění if Q = P then … test, zda ukazatele P, Q ukazují na stejné místo v paměti if Q^ = P^ then … test, zda hodnoty dynamicky alokovaných proměnných P^, Q^ jsou stejné (mohou to ale být různé proměnné) from Pavel Töpfer's presentation 4

Pozor na ztrátu přístupu k dynamicky alokované proměnné! Provedením new(P) nebo P: =Q se

Pozor na ztrátu přístupu k dynamicky alokované proměnné! Provedením new(P) nebo P: =Q se ztratí dosavadní hodnota proměnné P. Pokud na proměnnou P^ neukazuje ještě jiný ukazatel, stane se proměnná P^ nedostupnou – nelze nadále pracovat s její hodnotou, ale není ani možné uvolnit ji ( „smetí“ v paměti). from Pavel Töpfer's presentation 5

Dynamické datové struktury Vytvářejí se během výpočtu z dynamicky alokovaných proměnných, průběžně mohou podle

Dynamické datové struktury Vytvářejí se během výpočtu z dynamicky alokovaných proměnných, průběžně mohou podle potřeby měnit svoji velikost. Realizace: dynamicky se alokuje záznam, který má mezi svými položkami jednu nebo více položek typu ukazatel, ty ukazují na další dynamicky alokované záznamy, atd. (ukončení pomocí nil). Příklady: lineární spojový seznam, binární strom, obecný strom, graf. from Pavel Töpfer's presentation 6

Lineární spojové seznamy type Uk = ^Uzel; Uzel = record Info: integer; Dalsi: Uk

Lineární spojové seznamy type Uk = ^Uzel; Uzel = record Info: integer; Dalsi: Uk end; {uložená informace} {propojení seznamu} var P, Q, R: Uk; P 10 20 30 40 NIL from Pavel Töpfer's presentation 7

Vytvoření LSS odzadu = přidávání uzlů na začátek seznamu seznam s N uzly, v

Vytvoření LSS odzadu = přidávání uzlů na začátek seznamu seznam s N uzly, v nich rostoucí hodnoty od 1 do N P: =nil; {na začátku prázdný seznam} while N > 0 do begin new(Q); {nový uzel} Q^. Info: =N; Q^. Dalsi: =P; {zapojit na začátek seznamu} P: =Q; {nový začátek seznamu} N: =N-1 end; {P ukazuje na vytvořený seznam} from Pavel Töpfer's presentation 8

Vytvoření LSS odpředu = přidávání uzlů na konec seznamu seznam s N uzly, v

Vytvoření LSS odpředu = přidávání uzlů na konec seznamu seznam s N uzly, v nich rostoucí hodnoty od 1 do N - první prvek seznamu vytvořit zvlášť mimo cyklus (ukazatel P) - udržovat pomocný ukazatel na dosud poslední prvek seznamu (Q) new(P); P^. Info: =1; Q: =P; for I: =2 to N do begin new(R); R^. Info: =I; Q^. Dalsi: =R; Q: =R end; Q^. Dalsi: =nil; {první uzel seznamu} {je zároveň zatím posledním} {nový uzel} {zapojit na konec seznamu} {bude zatím posledním prvkem} {seznam zakončit pomocí nil} from Pavel Töpfer's presentation 9

Průchod seznamem - v každém uzlu provést akci A s uloženou hodnotou procedure Pruchod(P:

Průchod seznamem - v každém uzlu provést akci A s uloženou hodnotou procedure Pruchod(P: Uk); begin while P <> nil do begin A(P^. Info); {volání procedury A představuje libovolnou požadovanou akci} P: =P^. Dalsi {posun P na další uzel seznamu} end; Parametr P je předáván hodnotou vytvoří se lokální kopie ukazatele na začátek procházeného LSS, v proceduře můžeme P měnit, aniž by se v programu ztratil ukazatel na začátek seznamu. from Pavel Töpfer's presentation 10

Nalezení hodnoty v LSS - v seznamu hledáme uzel obsahující zadanou hodnotu X -

Nalezení hodnoty v LSS - v seznamu hledáme uzel obsahující zadanou hodnotu X - průchod seznamem až do nalezení prvního uzlu s hodnotou X, příp. až na konec seznamu (pokud tam X vůbec není) function Hledej(P: Uk; X: integer): Uk; {Pokud X v seznamu není, vrací funkce hodnotu nil, jinak vrací ukazatel na první prvek s hodnotou X. } begin while (P <> nil) and (P^. Info <> X) do P: =P^. Dalsi; {posunutí P na další uzel} Hledej: =P end; from Pavel Töpfer's presentation 11

Vložení prvku do seznamu za daný uzel - ukazatel P ukazuje na uzel, za

Vložení prvku do seznamu za daný uzel - ukazatel P ukazuje na uzel, za který chceme vložit nový uzel obsahující hodnotu X new(Q); Q^. Info: =X; Q^. Dalsi: =P^. Dalsi; P^. Dalsi: =Q; P 2 {nový uzel} {obsahuje hodnotu X} {1} {2} pozor na pořadí operací 1 -2 ! X 1 Q from Pavel Töpfer's presentation 12

Vložení prvku do seznamu před daný uzel - ukazatel P ukazuje na uzel, před

Vložení prvku do seznamu před daný uzel - ukazatel P ukazuje na uzel, před který chceme vložit nový uzel obsahující hodnotu X Trik: nový uzel se zapojí za P^ a vzájemně se vymění informace uložené v těchto dvou uzlech (pozn. : nešlo by použít, pokud by do seznamu vedly ještě nějaké další odkazy odjinud). new(Q); {nový uzel} Q^: =P^; {kopie celého P^ včetně ukazatele Dalsi} P^. Info: =X; {vložení nové hodnoty} P^. Dalsi: =Q; {zapojení nového uzlu} from Pavel Töpfer's presentation 13

Vypuštění prvku z LSS 1. Máme ukazatel P na předchůdce rušeného prvku (rušíme tedy

Vypuštění prvku z LSS 1. Máme ukazatel P na předchůdce rušeného prvku (rušíme tedy následníka uzlu P^) - snadné, potřebujeme ale pomocný ukazatel Q na „přidržení“ rušeného uzlu Q: =P^. Dalsi; P^. Dalsi: =Q^. Dalsi; dispose(Q); P {rušený uzel} {obnova propojení seznamu} {uvolnění paměti} Q from Pavel Töpfer's presentation 14

2. Máme ukazatel P na rušený prvek (rušíme tedy uzel P^) Opět podobný trik:

2. Máme ukazatel P na rušený prvek (rušíme tedy uzel P^) Opět podobný trik: vypustíme ze seznamu následníka uzlu P^ (to už umíme) a jeho hodnotu přesuneme do uzlu P^ - nelze použít pro poslední prvek seznamu (nemá následníka) - nelze použít, vedou-li do seznamu nějaké další odkazy Pomalejší řešení: - musíme znát ukazatel na začátek seznamu - projdeme seznam od začátku a najdeme předchůdce uzlu P^, jeho následníka pak vypustíme (to už umíme) - použitelné vždy, jenom první prvek seznamu je třeba vypouštět odlišně (nemá předchůdce) from Pavel Töpfer's presentation 15

procedure Zrus(var Z: Uk; P: Uk); {Z – ukazatel na začátek seznamu, P –

procedure Zrus(var Z: Uk; P: Uk); {Z – ukazatel na začátek seznamu, P – ukazatel na rušený prvek} var Q: Uk; {předchůdce rušeného uzlu} begin if P = Z then {ruší se první prvek seznamu} begin Z: =Z^. Dalsi; {nový začátek seznamu} dispose(P) end else begin Q: =Z; while Q^. Dalsi <> P do Q: =Q^. Dalsi; {předchůdce} Q^. Dalsi: =P^. Dalsi; dispose(P) end; from Pavel Töpfer's presentation 16

Zrušení uzlu s danou hodnotou Úkol: zrušit v seznamu první uzel obsahující hodnotu X

Zrušení uzlu s danou hodnotou Úkol: zrušit v seznamu první uzel obsahující hodnotu X - máme ukazatel na začátek seznamu (parametr procedury) - zvlášť ošetřit případ, že X je v prvním prvku seznamu – v tom případě se změní začátek seznamu (parametr procedury je proto třeba předávat odkazem!) - průchod seznamem s vyhledáním uzlu, který obsahuje hodnotu X, zaznamenat si ukazatel na předchůdce tohoto uzlu - nezapomenout ošetřit případ, že X v seznamu vůbec není - pomocí známého předchůdce lze uzel s hodnotu X snadno vypustit from Pavel Töpfer's presentation 17

procedure Vypust(var Z: Uk; X: integer); var P: Uk; {ukazatel na rušený prvek} Q:

procedure Vypust(var Z: Uk; X: integer); var P: Uk; {ukazatel na rušený prvek} Q: Uk; {předchůdce rušeného uzlu} begin if Z = nil then exit; {prázdný seznam} if Z^. Info = X then {X v prvním prvku seznamu} begin P: =Z; Z: =Z^. Dalsi; {nový začátek seznamu} dispose(P); {zrušit první prvek} exit end; Q: =Z; P: =Z^. Dalsi; while (P <> nil) and (P^. Info <> X) do begin Q: =P; P: =P^. Dalsi end; if P <> nil then {nalezena hodnota X} begin Q^. Dalsi: =P^. Dalsi; dispose(P) {zrušit prvek s X} end; from Pavel Töpfer's presentation 18