22.Prednaska: Rozdiel medzi revíziami
(Vytvorená stránka „{{Nadpis0nu|22. Prednáška|21.Prednaska|23.Prednaska}} <!-- -------------------------------------------------------------------------------------------- --> == Dynamic...“) |
(→Dynamické premenné) |
||
Riadok 69: | Riadok 69: | ||
S1^ := 17; | S1^ := 17; | ||
New(S2); | New(S2); | ||
− | {{Blue|S2}} : | + | {{Blue|S2}} := {{Blue|S1}}; |
Pravdepodobne v programe chceli dosiahnuť, aby druhá dynamická premenná (prístupná cez '''S2''') mala rovnakú hodnotu ako prvá premenná (prístupná cez '''S1'''). V tomto prípade sa príkazom '''New(S2)''' vytvorila nová dynamická premenná, a hneď ďalším príkazom '''S2 := S1;''' sa adresa na ňu zahodila a teraz máme v '''S2''' rovnakú adresu ako v '''S1'''. Pričom táto druhá premenná (vytvorená '''New(S2)''') stále existuje, v pamäti má vyhradené miesto, ale stratili sme na ňu prístup. Program by mal správne vyzerať asi takto: | Pravdepodobne v programe chceli dosiahnuť, aby druhá dynamická premenná (prístupná cez '''S2''') mala rovnakú hodnotu ako prvá premenná (prístupná cez '''S1'''). V tomto prípade sa príkazom '''New(S2)''' vytvorila nová dynamická premenná, a hneď ďalším príkazom '''S2 := S1;''' sa adresa na ňu zahodila a teraz máme v '''S2''' rovnakú adresu ako v '''S1'''. Pričom táto druhá premenná (vytvorená '''New(S2)''') stále existuje, v pamäti má vyhradené miesto, ale stratili sme na ňu prístup. Program by mal správne vyzerať asi takto: | ||
Riadok 78: | Riadok 78: | ||
S1^ := 17; | S1^ := 17; | ||
New(S2); | New(S2); | ||
− | {{Blue|S2}}{{Red|^}} : | + | {{Blue|S2}}{{Red|^}} := {{Blue|S1}}{{Red|^}}; |
Aktuálna revízia z 23:05, 3. december 2012
22. Prednáška |
Dynamické premenné
Doteraz sme sa väčšinou stretali s premennými, ktorým hovoríme statické - ich veľkosť a adresa v pamäti boli určené už počas kompilácie. Statickými premennými sú buď globálne premenné alebo lokálne:
- globálne premenné (premenné hlavného programu, resp. všetkých programových jednotiek) sú kompilátorom vyhradené v jednom dátovom segmente, tieto premenné automaticky vznikajú pri štarte programu a sú inicializované hodnotou 0;
- lokálne premenné (aj hodnotové parametre) podprogramov sa automaticky vyhradia pri volaní podprogramu (automaticky sa zrušia pri ukončení podprogramov) ale ich veľkosť a pozícia v pamäti (t.j. v systémovom zásobníku) sa určí počas kompilácie - už vieme, že majú neinicializovanú hodnotu.
Statické premenné nemôžu počas behu zmeniť svoju veľkosť a ani adresu (počas tzv. Run Time) - o ich vytvorenie a rušenie sa stará systém (pri spustení programu, resp. podprogramu).
Na rozdiel od týchto statických premenných má Pascal aj mechanizmus na vytváranie dynamických premenných:
- vytváranie a rušenie takýchto premenných je v rukách programátora - do programu musí zapísať príkazy na vytváranie a rušenie premenných,
- kým takúto premennú programátor nevytvorí, premenná neexistuje a zrejme sa s ňou nedá ani pracovať,
- počas behu programu (Run Time) sa špeciálne na to určenými príkazmi môžu vytvárať a rušiť, sú to tzv. dynamické premenné,
- takéto premenné sa nevytvárajú ani v dátovom segmente programu ani v zásobníku ale v špeciálne na to určenom segmente, v ktorom budú všetky dynamické premenné - tento dátový segment nazývame heap (zriedkavejšie aj po slovensky ako halda), jeho veľkosť závisí od OS - často je to niekoľko 100 MB.
S niektorými dynamickými premennými sme už pracovali aj doteraz, aj tu bol v rukách programátora mechanizmus na vytváranie a rušenie takýchto premenných, ale zatiaľ sme na to takto nepozerali:
- dynamické pole (napr. var A: array of Integer;) - premennú vytvoríme napr. pomocou SetLength(A, 10); a zrušíme napr. príkazom A := nil;
- znakový reťazec (napr. var S: string;) - vytvoríme napr. pomocou S := 'abcd'; a zrušíme napr. S := '';
- objekt (napr. var R: TRobot;) - vytvoríme pomocou R := TRobot.Create; a zrušíme pomocou R.Free;
Pri dynamických poliach a znakových reťazcoch má Free Pascal mechanizmus, pri ktorom vie pracovať aj s prázdnou premennou, t.j. hoci sme premennú zrušili, môžeme ju používať, napr. S := ''; S := S + '*';
Aby sme v našich programoch mohli pracovať s dynamickými premennými, ktoré budú vznikať a počas behu sa rušiť, budeme na to používať odkazy na premenné. Už vieme, že sú to smerníky hoci sme zatiaľ s nimi pracovali len v prípadoch, keď odkazovali na nejaké statické premenné.
Štandardné procedúry New a Dispose
Uvidíme, že smerníkové premenné sa najčastejšie využívajú na prístup k dynamickým premenným. Využijeme štandardnú procedúru New, ktorá vytvorí novú dynamickú premennú a jej odkaz priradí do smerníkovej premennej. A tiež štandardnú procedúra Dispose, ktorá má opačnú funkciu ako procedúra New: zruší dynamickú premennú, na ktorú bol odkaz prostredníctvom nejakého smerníka. Obe tieto procedúry majú jeden formálny var-parameter, ktorým musí byť smerníková premenná.
Procedúra New
Volanie vyzerá, napr. takto
var
SI: ^Integer;
begin
New(SI);
Týmto volaním sa vytvorila nová celočíselná premenná (zatiaľ má nedefinovaný obsah) a jej odkaz sa priradil do smerníkovej premennej SI. Teraz môžeme pracovať s touto novou premennou prostredníctvom smerníka, napr. jej priradiť hodnotu, alebo jej hodnotu vypísať, napr.
SI^ := 37; WriteLn('hodnota novej premennej = ', SI^);
Ak už predtým bola v tejto premennej priradená nejaká hodnota (odkaz na nejakú premennú), tak táto hodnota sa príkazom New zabúda. Aj smerníková premenná (tak ako aj každá iná) môže obsahovať jedinú hodnotu a každé ďalšie priradenie do tejto premennej starú hodnotu zabúda. So smerníkmi je to o to horšie, že ak v ňom máme uchovaný odkaz na nejakú dynamickú premennú, tak priradením inej hodnoty do tohto smerníka strácame možnosť pracovať s touto dynamickou premennou a už nikdy sa k nej nedostaneme. Týmto môžeme stratiť nielen nejaké údaje, ale aj časť pamäte, ktorá nám neskôr môže niekde chýbať. Napr.
var SI: ^Integer; begin New(SI); SI^ := 37; New(SI); SI^ := 13;
V programe sa postupne vytvorili dve dynamické premenné: najprv prvá a do nej sa priradila hodnota 37, potom druhá a do nej sa priradila hodnota 13. Tým, že sme smerník SI použili aj pre odkaz na druhú premennú, pôvodne zapamätaný odkaz na premennú s 37 sa zabúda (táto premenná stále existuje) a až sa k nej nikdy nedostaneme.
Činnosť príkazu New(Smernik), pre var Smernik: ^Typ; môžeme zhrnúť takto:
- zabudne pôvodnú hodnotu premennej Smernik,
- vo voľnej časti pamäti (heap) vyhradí úsek veľkosti SizeOf(Typ), t.j. toľko bajtov, koľko bude zaberať táto nová dynamická premenná
- vždy je to trochu viac (závisí od organizácie správy pamäti, ktorá sa stará o heap),
- do premennej Smernik priradí adresu tejto novej dynamickej premennej.
Začiatočníci niekedy robia takúto chybu:
var S1, S2: ^Integer; begin New(S1); S1^ := 17; New(S2); S2 := S1;
Pravdepodobne v programe chceli dosiahnuť, aby druhá dynamická premenná (prístupná cez S2) mala rovnakú hodnotu ako prvá premenná (prístupná cez S1). V tomto prípade sa príkazom New(S2) vytvorila nová dynamická premenná, a hneď ďalším príkazom S2 := S1; sa adresa na ňu zahodila a teraz máme v S2 rovnakú adresu ako v S1. Pričom táto druhá premenná (vytvorená New(S2)) stále existuje, v pamäti má vyhradené miesto, ale stratili sme na ňu prístup. Program by mal správne vyzerať asi takto:
var S1, S2: ^Integer; begin New(S1); S1^ := 17; New(S2); S2^ := S1^;
Procedúra Dispose
Štandardná procedúra Dispose slúži na rušenie dynamickej premennej. Procedúra má jeden formálny var-parameter, ktorým musí byť smerníková premenná. V tejto premennej musí byť odkaz na nejakú dynamickú premennú (vytvorenú pomocou New).
Činnosť príkazu Dispose(Smernik), pre var Smernik: ^Typ; môžeme zhrnúť takto:
- zaradí do voľnej pamäte (heap) premennú Smernik^,
- premenná Smernik má odteraz nedefinovanú hodnotu a preto by sme s touto hodnotou nemali ďalej referencovať (kým do nej nepriradíme inú vhodnú hodnotu),
- všetky smerníky, ktoré odkazovali na túto istú dynamickú premennú, majú odteraz tiež nedefinovanú hodnotu
Dve dynamické premenné celé číslo:
var S, P: ^Integer; I: Integer; begin New(S); S^ := 0; for I := 1 to 10 do begin New(P); P^ := I * I; S^ := S^ + P^; Dispose(P); end; Writeln('sucet = ', S^); Dispose(S);
Pomocná premenná P v tomto príklade je len na ilustráciu použitia New a Dispose. Program postupne vytvorí a aj zruší 11 dynamických premenných.
Príklady smerníkov na štruktúry
Dynamický záznam:
type TZaznam = record X, Y: Integer; end; var Z: ^TZaznam; begin New(Z); Z^.X := 100; Z^.Y := 200; with Z^ do Image1.Canvas.MoveTo(X, Y); Inc(Z^.X, 100); Dec(Z^.Y, 50); Image1.Canvas.LineTo(Z^.X, Z^.Y);
Dynamické jednorozmerné pole:
type TPole = array [1..10] of Real; var P: ^TPole; I: Integer; begin New(P); Write('zadaj prvky pola: ') for I := 1 to 10 do Read(P^[I]); ReadLn; for I := 9 downto 1 do P^[I] := P^[I] + P^[I+1];
Problém s veľkým poľom ako lokálnou premennou:
type TPole = array [1..100000000] of Integer; procedure Test; var P: TPole; begin P[1] := 1; end;
Takéto pole chce vzniknúť na zásobníku počas volania tejto procedúry - systém má ale problém s tak veľkým poľom. Preto zadefinujme veľké pole v lokálnej premennej pomocou smerníka. Volanie tejto testovacej procedúry už prejde.
type TPole = array [1..100000000] of Integer; var N: Integer; procedure Test; var P: ^TPole; begin New(P); Inc(N); P^[1] := N; WriteLn(P^[1], '. procedura Test'); // Dispose(P); end; begin while True do Test; end.
Problém vznikne, ak sa toto veľké pole pred koncom procedúry neuvoľňuje (chýba volanie Dispose). Program vtedy po niekoľkých prechodoch spadne na správe "Out of memory". Takto ale môžete otestovať veľkosť heapu ...
Ďalší príklad ilustruje funkciu, ktorá vracia smerník. Táto funkcia generuje náhodnú hodnotu:
type PInteger = ^Integer; function Daj: PInteger; begin if Random(6) = 0 then Result := nil else begin New(Result); Result^ := Random(100); end; end;
Smerník na objekt:
type TTrieda = class A: Integer; constructor Create(AA: Integer); end; constructor TTrieda.Create(AA: Integer); begin A := AA; end; var S: ^TTrieda; begin New(S); S^ := TTrieda.Create(8); Writeln('hodnota = ', S^.A); S^.Free; Dispose(S); end;
Vytvoríme jednorozmerné pole smerníkov (každý prvok poľa je smerník na množinu):
type TMnozina = set of Byte; // 32 bajtov TPole = array [1..1000] of ^TMnozina; // 4000 bajtov -- inak bez ^ by bolo 32000 bajtov var Data: TPole; I: Integer; begin for I := 1 to 1000 do New(Data[I]); // vytvorilo sa 1000 nedefinovaných množín for I := 1 to 1000 do Data[I]^ := [Random(256)]; ... for I := 1 to 1000 do Dispose(Data[I]); // uvoľnený heap môže systém ďalej v tomto projekte používať end;
Príklady jedno- a dvoj-rozmerných polí smerníkov:
type TPole = array [1..10] of Integer; PPole = ^TPole; TPole2 = array [1..20] of TPole; // obyčajné 2-rozmerné pole PPole2 = ^TPole2; TPole2PPole = array [1..20] of TPPole; var A: TPole2PPole; B: PPole2; I, J: Integer; begin for I := 1 to 20 do New(A[I]); for I := 1 to 20 do for J := 1 to 10 do A[I]^[J] := I + J; New(B); for I := 1 to 20 do for J := 1 to 10 do B^[I][J] := I + J; ... end;
Smerník, ktorý odkazuje na pole:
type TSPole = array [1..10] of ^Integer; // pole smerníkov na Integer PSPole = ^TSPole; // smerník na pole smerníkov TPolePSPPole = array [1..20] of PSPole; // pole smerníkov na pole smerníkov PPolePSPPole = ^TPolePSPPole; // smerník na pole smerníkov na pole smerníkov var C: TPolePSPPole; D: PPolePSPPole; I, J: Integer; begin for I := 1 to 20 do // SizeOf(C) = 20*4 New(C[I]); for I := 1 to 20 do // SizeOf(C[I]) = 4 for J := 1 to 10 do // SizeOf(C[I]^) = 10*4 begin New(C[I]^[J]); C[I]^[J]^ := I + J; end; New(D); for I := 1 to 20 do // SizeOf(D) = 4; SizeOf(D^) = 20*4 New(D^[I]); for I := 1 to 20 do // SizeOf(D^[I]) = 4 for J := 1 to 10 do // SizeOf(D^[I]^) = 10*4 begin New(D^[I]^[J]); D^[I]^[J]^ := I + J; end; ... end;
Záznamy, polia a smerníky:
type TPole = array [1..10] of Integer; PPole = ^TPole; TZaznam = record X: TPole; Y: PPole; Z: array [1..10] of ^Integer; end; PZaznam = ^TZaznam; TPolePZaznam = array [1..20] of PZaznam; PPolePZaznam = ^TPolePZaznam; var A: PZaznam; B: TPolePZaznam; C: PPolePZaznam; I, J: Integer; begin New(A); for I := 1 to 20 do New(B[I]); New(C); for I := 1 to 20 do New(C^[I]); for I := 1 to 20 do begin for J := 1 to 10 do C^[I]^.X[J] := I + J; New(C^[I]^.Y); for J := 1 to 10 do C^[I]^.Y^[J] := I + J; for J := 1 to 10 do New(C^[I]^.z[J]); for J := 1 to 10 do C^[I]^.z[J]^ := I + J; end; ... end;
Postupne prečítame premennú C^[I]^.Y^[J] - zistíme, či je zapísaná správne a akého je typu:
C // je smerník na TPolePZaznam C^ // je pole s prvkami PZaznam C^[I] // je smerník na TZaznam C^[I]^ // je záznam TZaznam C^[I]^.Y // je smerník na TPole C^[I]^.Y^ // je pole s prvkami Integer C^[I]^.Y^[J] // je celočíselná premenná
Smerník na smerník
Nasledujúci príklad len ilustruje nezvyčajné použitie smerníkov:
type PInt = ^Integer; PPInt = ^PInt; PPPInt = ^PPInt; var I: PInt; J: PPInt; K: PPPInt; begin New(I); I^ := 123; New(J); New(J^); J^^ := 345; New(K); New(K^); New(K^^); K^^^ := 567;
Netypový smerník - typ Pointer
Pomocou netypového smerníka nemôžeme vytvárať dynamické premenné procedúrou New, lebo táto by nevedela, koľko bajtov zaberá takáto premenná. Pre vyhradenie pamäte pre netypový smerník môžeme použiť štandardnú procedúru, resp. funkciu GetMem. Na uvoľnenie dynamickej premennej môžeme použiť štandardný podprogram FreeMem. Príklad ilustruje použitie týchto procedúr:
var P: Pointer; SR: ^Real; begin GetMem(P, 8); SR := P; SR^ := 3.14; FreeMem(P);
GetMem môžeme volať aj ako funkciu, napr.
P := GetMem(8);
GetMem funguje analogicky ako New:
- volanie GetMem(Smerník, počet_bajtov) pre zadaný smerník vyhradí dynamickú premennú zadanej veľkosti
- takto môžeme vyhradiť pamäť aj pre typovú smerníkovú premennú, napr. GetMem(SI, 4), resp. GetMem(SI, SizeOf(SI^))
Typovému smerníku môžeme vyhradiť aj viac pamäte, ako zaberá samotný typ. Potom vďaka smerníkovej aritmetike môžeme pracovať s celým poľom:
var SI, S: PInteger; I: Integer; begin GetMem(SI, 100 * SizeOf(Integer)); for I := 0 to 99 do (SI + I)^ := Sqr(I + 1); S := SI; for I := 0 to 99 do begin Write(S^, ' '); Inc(S); end; WriteLn; FreeMem(SI);
Program najprv vyhradí dynamickú premennú veľkosti 100 celých čísel (400 bajtov), pričom na prvé z nich odkazuje smerník SI. Ku ďalším číslam sa dostaneme buď zápisom (SI + I)^ alebo pomocným smerníkom S, ktorý posúvame pomocou Inc. Pascal správne pochopí, keď namiesto (SI + I)^ zapíšeme SI[I]. Toto zvláda pre všetky typové smerníky.
Reprezentácie rôznych typov
Niektoré z doteraz používaných typov sú reprezentované pomocou dynamickej pamäti a smerníkov. Uvedieme niekoľko informácií o dynamických poliach, znakových reťazcoch a inštanciách tried.
Dynamické polia
Sú reprezentované smerníkom na jednorozmerné pole (v dynamickej pamäti heap). Deklarácia dynamického poľa zatiaľ nealokuje žiadnu pamäť. Vtedy má pole nedefinovanú dĺžku (mali by sme napr. priradiť nil).
Volanie SetLength vyhradí pamäť (niečo ako GetMem): ak už premenná mala vyhradené nejaké pole, tak toto sa automaticky uvoľní (niečo ako FreeMem).
Ak X a Y sú premenné rovnakého typu dynamické pole, potom X := Y spôsobí, že X referencuje na to isté pole ako Y (netreba alokovať pamäť pre X). Pascal si teraz pamätá, že na toto pole sa odkazuje dvomi premennými a pamäť uvoľní, až keď sa zmenia referencie oboch polí. Pre dynamické polia nepoužívajte ani procedúry New, GetMem a pod. a ani operátor ^. Pozrite nasledujúci príklad:
var A, B: array of Integer; begin SetLength(A, 4); A[0] := 1; B := A; // teraz sú obe polia na tom istom mieste v pamäti B[0] := 2; // aj hodnotou A[0] je 2 SetLength(B, 3); // teraz sú obe polia v pamäti na rôznych miestach
Všimnite si posledný príkaz SetLength(B, 3), ktorý z poľa B uberie jeden prvok. Vďaka tomuto sa pre B vyhradí nová pamäť, pričom prvé tri prvky budú mať obe tieto polia rovnaké.
Pri porovnávaní premenných typu dynamické pole sa porovnávajú ich referencie a nie hodnoty polí (ako pri statických poliach). Napr.
var A, B: array of Integer; begin SetLength(A, 1); SetLength(B, 1); A[0] := 2; B[0] := 2; if A = B then ...
Porovnanie A = B vráti False ale test A[0] = B[0] vráti True. Na skrátenie dynamického poľa sa môže použiť aj funkcia Copy, napr. A := Copy(A, 5, 10); - funguje rovnako, ak so znakovými reťazcami.
Premenná typu dynamické pole zaberá 4 bajty - je to smerník na dynamicky alokované pole v heap. Buď je to nil alebo je to smerník na blok pamäti, ktorý je o 8 bajtov dlhší ako vyhradená veľkosť poľa:
- 4 bajty použité ako počítadlo referencií
- 4 bajty na počet prvkov poľa (Length)
- za tým nasledujú prvky poľa
Viacrozmerné dynamické pole je reprezentované úplne rovnako -- je to dynamické pole smerníkov na dynamické polia.
Znakové reťazce
Znakové reťazce (t.j. štandardný typ string) sú podobné dynamickým poliam: tiež sú to smerníky na polia znakov. Podobne sa pamätá aj počet referencií a aktuálna dĺžka reťazca (Length). Za posledným znakom v poli je vždy #0 (hoci tento sa nedá indexovať). Vďaka tomu je použiteľný aj ako #0 ukončený reťazec. Prázdny reťazec je uchovaný ako nil (ale napriek tomu do stringovej premennej nemôžeme priradiť nil). Nemôžeme používať ani New ani Dispose a ani iné procedúry správy pamäti. Teraz by ste už mohli správne rozumieť tomuto príkladu:
var S: string; begin S := 'ahoj pascal'; WriteLn('dlzka = ', StrLen(@S[1]));
Inštancie tried
Každá objektová premenná, t.j. inštancia nejakej triedy, je v skutočnosti smerník na dynamicky alokovaný blok pamäti. Treba na to myslieť pri porovnávaní aj priraďovaní, napr.
- C := Image1.Canvas; // zapamätám si smerník na objekt
- if C.Pen = Form1.Canvas.Pen then ... // tu netestujem, či majú rovnaké pero
Všetky stavové premenné objektu sú uchované veľmi podobne ako položky v type záznam. Už vieme, že informácie o metódach sa ukladajú do tabuľky VMT (virtual method table) - táto tabuľka je jediná pre všetky inštancie jednej triedy (každá trieda má svoju VMT tabuľku). V pamäti pre objekt je len smerník na túto VMT. VMT okrem metód obsahuje aj iné informácie o inštancii, napr. informácie o dĺžke, triede a pod.
Asi by vám malo byť jasné, prečo je nezmysel namiesto R := TRobot.Create použiť R.Create. Premenná R je na začiatku nedefinované alebo má možno hodnotu nil a teda ňou sa nemôžeme odkazovať (referencovať) na metódu Create neexistujúcej inštancie.
Netypový formálny parameter
Podprogramy v Pascale môžu mať definované formálne parametre, ktoré nemajú uvedený typ. S takýmito parametrami ale treba v tele podprogramu pracovať špeciálnym spôsobom. Možností je niekoľko:
- pretypovaním na konkrétny typ
- pomocou direktívy absolute (je to pretypovanie počas deklarácií)
- poslať ako netypový parameter do inej procedúry
- napr. štandardná procedúra Move(odkiaľ, kam, koľko_bajtov)
- alebo zápis, resp. načítanie do/z netypového súboru – budeme vidieť neskôr
Pozrime procedúru na výmenu obsahov dvoch ľubovoľných (rovnako veľkých) premenných:
procedure Vymen(var A, B; Dlzka: Integer); var T: Pointer; begin GetMem(T, Dlzka); Move(A, T^, Dlzka); Move(B, A, Dlzka); Move(T^, B, Dlzka); FreeMem(T); end;
Použitie operátora @ a smerníkovej aritmetiky Inc:
procedure Dump(const A; Dlzka: Integer); var P: ^Byte; begin P := @A; while Dlzka > 0 do begin Write(IntToHex(P^, 2), ' '); Inc(P); Dec(Dlzka); end; WriteLn; end; var s: array [0..15] of Char; I: Integer; begin s := 'Ahoj Pascal'; Dump(S, SizeOf(S)); I := 12345; Dump(I, SizeOf(I)); end;
Pretypovanie
Vyriešme Dump pomocou pretypovania:
procedure Dump(const A; Dlzka: Integer);
type
TPole = array [1..MaxInt] of Byte;
var
I: Integer;
begin
for I := 1 to Dlzka do
Write(IntToHex(TPole(A)[I], 2), ' ');
WriteLn;
end;
Direktíva absolute
Táto direktíva je tiež veľmi nebezpečná (podobne ako pretypovanie). Už pri deklarovaní premennej označíme, že sa nachádza na mieste inej premennej:
procedure Dump(var A; Dlzka: Integer); var P: array [1..MaxInt] of Byte absolute A; I: Integer; begin for I := 1 to Dlzka do Write(IntToHex(P[I], 2), ' '); WriteLn; end;
Správa pamäti
Správa pamäti (Memory management) sa stará o udržiavanie obsadených a uvoľnených častí dynamickej pamäti (heap). Pracujeme s ňou štandardnými procedúrami: New, Dispose, GetMem a FreeMem. Každý vyhradený pamäťový blok (napr. pomocou New) má dĺžku zaokrúhlenú na najbližší násobok 4 a obsahuje ešte 4-bajtovú hlavičku - táto obsahuje dĺžku bloku a iné stavové informácie.
Od správy pamäti sa môžeme dozvedieť niektoré užitočné informácie (napr. na ladenie problémových častí programu) pomocou funkcie GetHeapStatus. Napr.
- GetHeapStatus.TotalAllocated - veľkosť použitej pamäte
- GetHeapStatus.TotalFree - veľkosť voľnej pamäte
Detaily si pozrite v Helphe.
Rezervované slovo nil je špeciálna smerníková konštanta - vnútorne je reprezentovaná 4 bajtami s hodnotou 0.
Smerníkový operátor @premenná vráti smerník (referenciu - adresu) na danú premennú (neskôr uvidíme aj smerník na procedúru). Výsledkom je smerník typu ^Typ, ak je Typ typom premennej.