Viidad e. osutid

Free Pascal toetab viitmuutajate kasutamist. Viitmuutuja sisaldab mäluaadressi, kus andmed või mõni teine muutuja salvestatakse.  

Nagu sellelt diagrammilt näha, on viidad kindlat tüüpi, mis tähendab, et nad osutavad teatud liiki andmetele. Nende andmete tüüp peab olema teada kompileerimise ajal. Viit (tähistatakse ^-märgiga ) käitub nagu muutuja. Muutuja tüübiks on viida deklaratsioonis näidatud tüüp ja muutuja salvestatkse viitmuutuja poolt osutataval aadressil. Vaadame järgnevat näidet:

Program osutid;
 type
   Buffer = String[255];
   BufPtr = ^Buffer;
 Var B  : Buffer;
     BP : BufPtr;
     PP : Pointer;
 jne..

Selles näites on BP viit Buffer tüübile; samas on B Buffer tüüpi muutuja . B kasutab 256 B mälu ja BP 4 B mälu (sellest piisab aadressi salvestamiseks). 

Kui soovitakse omistada viitmuutujale mõne olemasoleva muutuja X aadressi, siis on selleks kaks võimalust: 

lp := Addr(X); (* funktsioon Addr tagastab muutuja X aadressi *)

või 

lp := @X; (* @ - aadressi määramise operaator *)

Märkus: Free Pascal kastab viitasid samal moel nagu programmeerimiskeel C. See tähendab, et teatud tüüpi muutujale osutavat viitmuutujat võib käsitleda, kui massiivi seda tüüpi muutujatest. Viit osutab sellisel juhul massiivi nullindale elemendile.Seega järgnevat viida deklaratsiooni

Var p : ^Longint;

võib lugeda samaväärseks järgmise massiivi deklareeringuga:

Var p : array[0..Infinity] of Longint;

Erinevus on selles, et esimese deklaratsiooniga eraldatakse mälu ainult viida jaoks (mitte massiivi jaoks) ja teise deklaratsiooniga eraldatakse mälu terve massiivi jaoks. Kui kasutada esimest, peab mälu eraldama käsitsi kasutades Getmem funktsiooni. Viit P^ on sama, mis p[0]. Järgnev programmilõik iseloomustab öeldut ehk veidi selgemini:

program PointerArray;
 var i : Longint;
     p : ^Longint;
     pp : array[0..100] of Longint;
 begin
   for i := 0 to 100 do pp[i] := i; 
(* Massiiviliikmete väärtustamine *)
   p := @pp[0];                     
(* Viidaku p pp aadressile *)
   for i := 0 to 100 do
     if p[i]<>pp[i] then
       WriteLn ('Siin on mingi probleem !')
 end.

Free Pascal toetab viidaaritmeetikat nagu C-gi. See tähendab, et kui P on viidatüüp, siis

Inc(P);
Dec(P);

esimene lause suurendab ja teine vastavalt vähendab viida aadressi tüübi P suuruse võrra. Näiteks

Var P : ^Longint;
 ...
  Inc (p);

suurendab P 4 võrra. Võib kasutada ka normaalseid aritmeetilisi operaatorieid, seega järgnevad on täiesti kehtivad aritmeetikatehted viitadega:

var  p1,p2 : ^Longint;
      L : Longint;
 begin
   P1 := @P2;
   P2 := @L;
   L := P1-P2;
   P1 := P1-4;
   P2 := P2+4;
 end.

Nagu ülal, nii ka siin väärtus, mis liidetakse või lahtatakse korrutakse tüübi suurusega, millele viit osutab. Siintoodud näites P1 vähendatakse 16 B võrra ja P2 suurendatakse 16 võrra.

Vaatame veel üht näidet:

var
p : ^integer;
m, n : integer;
begin
m:=10; n:=15;
p:=@m;
(* p on viit m aadressile ehk osuti muutujale m *)
p^:=12;
(* see on täpselt sama, kui omistustehe m:=12; *)
p:=@n;
(* p osutab nüüd muutujale n *)
p^=m;
(*see on täpselt sama, kui omistamine n:=m; *)
writeln('m = ',m,', n = ',n);
(* tulemus on: m = 12, n = 12 *)
end.

üllatav! Kuidas see saab nii olla? Kui p osutab m (tähistatakse p:=@m;), siis pöördumine p^ poole on sama, mis pöördumine m poole kõikide operatsioonide puhul (omistamine, võrdlemine, maemaatikatehted, jne.). Viitade (viitmuutujate) poole pöördumine on lihtne - tuleb viidale anda nimi ja selle järele paigutada märk (^) ning käituda nendega nagu tavaliste muutujatega. Päris lihtne, mis? ;-) 

Viitmuutuja kasutamise praktiliseks näiteks võiks olla veel järgmine programmijupp: 

Program viit;
Type PInt = ^Integer;
(* viida tüübi definitsioon *)
Var A, B : Integer;
P : PInt;
begin
P := @A;
(* viida väärtuseks omistatakse muutuja A aadress *)
Write('Palun sisestage arv : ');
Read(A);
Writeln(' Arv salvestatud ',P^);
(* arvu saab kätte ka viida kaudu *)
Write('Palun sisestage teine arv : ');
Read(B);
if P^ > B then (* kui A > B *)
P := @B;
(* omistame viidale muutuja B aadressi, ehk jätame väiksema arvu aadressi meelde *)
Writeln(' Sisestatud kahest arvust väiksem oli ',P^);
end.

Vaatame üht pisut praktilisemat näidet: Sisestada rida teksti ja leida selles tekstireas olevate tühikute arv. 

Program Tyhikud;
Var
S : ^String;
(* viitmuutuja *)
i, n : Integer;
begin
Writeln('Palun sisestage üks rida teksti:');
GetMem(S, 81);
(* küsime 81 baiti mälu *)
Readln(S^);
(* kasutades viita loeme sellesse mällu sisestatava rea *)
n := 0;
(* n on tühikute loendur, algväärtuseks on 0 *) 
For i := 1 to Length(S^) do
(* vaatame kõiki märke reas *)
If S^[i] = ' ' then
(* ja kui märk on tühik *)
n := n + 1;
(* siis suurendame loendurit ühe võrra *)
FreeMem(S, 81);
(* vabastame eelnevalt hõivatud 81 baiti mälu *)
Writeln('Selles reas on ', n, ' tühikut.');
end.

Lõpetuseks üks näide, mis võimaldab vaadata massiivi jada liikmete mäluaadresse, selleks teisendatakse mäluaadress stringiks funktsiooniga Dec2Numb ja siis väljastatakse.

program viit;
uses crt,strutils;
(* kasutame stringiteisendusfunktsioone *)
var
jada:array[1..20] of LongInt;
(*jada liikmed on pikk täisarv tüüpi*)
i:SmallInt;
(*loendur on väike täisarv tüüpi *)
v:^LongInt;
(* defineerime viida jada liikmele, tyyp peab olema sama, mis jada liikmel endal *)
uv:LongInt;
(*vajalik teisendusfunktsiooni argumendiks*)
aadress:string;
(*Dec2Numb funktsiooni väljund on string!*) 
begin
clrscr;
(*v:=@jada[0];
(* viitmuutuja väärtuseks on esimese jadaliikme aadress *)
writeln(v);*)
(* esimese jadaliikme aadressi väljastamine *)
for i:=0 to 19 do
(* loenduri muutmine vastavalt jadaliikmele *)
begin
jada[i]:=i;
(* omistame jadaliikmele loenduri väärtuse *)
v:=@jada[i];
(* loeme jadaliikme mäluaadressi *)
uv:=LongInt(v);
(* teisendame aadressi pikaks täisarvuks *)
aadress:=Dec2Numb(uv,10,10);
(*3 argumenti: pikk täisarv tüüpi muutuja teisendatakse stringiks, kohtade arv, teisendusbaas(2/8/10/16) *)
writeln(aadress);
(* aadressi väljastus stringi kujul *)
end;
readln;
(* akna sulgumise vältimiseks oodatakse Enter klahvi vajutust*)
end.