Программада анықталған барлық айнымалылар жедел жадтын берiлгендерінін сегментi деген бiр үздiксiз аумағында орналасады. Сегмент ұзындығы жалпы микропроцессордын архитектурасына байланысты болады және 65536 байттан аспайды, сол себептен үлкен көлемдi массивтермен жұмыс атқарылғанда әртүрлi қиындықтар туындайды. Осындай қиындықтан шығу үшiн динамикалық жадты (немесе үйменi) пайдалану қолайлы деп саналады.
Берiлгендердiн динамикалық жадта орналасуын және онын босатылуын программа орындалу барысында пайдаланады, ал берiлгендердiн статикалық орналастантыруын Delphi ортасынын компиляторы орындайды. Берiлгендердi динамикалық жадқа орналастырғанда алдын ала олардын түрi және ұзындығы белгiсiз, сондықтан оларға аты арқылы қол жеткiзуге болмайды.
8.1 Сiлтемелер
Дербес компьютердiн жедел жады ақпаратты сақтауға арналған қарапайым ұяшықтар – байттар тiзбегiнен тұрады. Әр байттын тiзбектегi өз нөмiрi – адресi болады. Байтқа қол жеткiзу – онын адресi арқылы орындалады. Object Pascal тiлiнде динамикалық жадты басқару үшiн сiлтемелер тәсiлi қолданылады. Сiлтеменiн мәнi – жадтағы байттын адресi.
Сiлтемелер арқылы динамикалық жадқа Object Pascal тiлiнде қолданылатын кез келген түрдегi берiлгендердi орналастыруға болады. Byte, Char, Shortint, Boolean түрдегi берiлгендер бiр байт орын алады, басқа түрдегiлер – бiрнеше көршi байттарда орналасады, сондықтан сiлтеме тек қана бiрiншi байттын адресiн көрсетедi.
Әдетте сiлтемелер берiлгендердiн бiр түрiмен байланысты болады. Оларды типтiк сiлтемелер деп атайды. Типтiк сiлтеменi анықтау үшiн “ ^ ” белгiсi қолданылады және сiлтеме келесi түрде сиптталады:
Type
<сiлтемелiк шаманын аты >= ^ <кез келген түр>; немесе
Var
<сiлтемелiк шаманын аты >:^ <кез келген түр>;
Мысалы,
Var
P1: ^ integer; // P1 - бүтiн шамаға сiлтемеле деп анықталған;
P2: ^ real; // P2 - нақты шамаға сiлтеме деп анықталған;
P3: array[1..100,1..200] of ^ real;
//P3 - нақты шамалардын сiлтемелерiнен құрылған массив деп анықталған.
Программада сiлтемелiк түрдiн анықтамасын келтiрiп, жалпы ережелер бойынша осы түрге жататын айнымалыларды сипттауға болады. Мысалы,
Type
P: ^intеger;
PersonPointer=^ PersonRecord;
PersonRecord= record
Name:string[20];
Job: string [20];
Next: PersonPointer ;
End;
Var
P1,P2:P;
…………………..
Бұл мысалда P – бүтiн шамалардын, ал PersonPointer әлi анықталмаған PersonRecord түрiндегi берiлгендердiн сiлтемелер жиыны. PersonRecord келесi қатарда жазбалар түрiнде сиптталып, онын құрамына кiретiн өрiстерi анықталады: Name және Job жолдар түрiнде сиптталған PersonRecord жазбанын өрiстерi, ал Next жазбанын PersonPointer тегiндегi берiлгендерге сiлтеме түрiнде сиптталған өрiсi.
P1,P2 айнымалылар P типiнде анықталған бүтiн шамаларға сiлтемелер ретiнде қолданылады.
Жалпы ережелер бойынша программадағы кездесетiн барлық айнымалылар, түрлер және тұрақтылар алдын ала сиптталып, сонан кейiн программа денесiнде әртүрлi операторлар құрамында қолданылады. Ал сiлтемелiк түрдегi шамалардын ерекшелiгi – әлi анықталмаған берiлгендер түрiнде сипттау болатындығы. Бiрақ ондай анықтама сонынан келтiрiлуi тиiс.
Болуы мүмкiн сiлтемелiк шамалардын арасындағы жадтын арнайы бос адресi – NIL сiлтемесi деп аталады және ол адрес бойынша ешбiр шама жазылмайды. NIL сiлтемесi кез келген түрдегi сiлтемелiк шамамен тiркеседi деп саналады, яғни NIL сiлтемесiн кез келген түрдегi сiлтемеге тенестiруге болады.
Object Pascal тiлiнде типсiз сiдтемелермен қатар типсiз сiлтемелiк шамалар қолданылады. Осы түрдегi шамаларды анықтау үшiн стандартты Pointer түрi пайдаланылады.
Type
<сiлтемелiк шаманын аты >= < Pointer >; - типсiз сiлтеме.
Мысалы,
Var
P: Pointer;
Pointer түрiндегi сiлтемелер ешбiр типпен байланыспайды, сол себептен оларды программа орындалу барысындағы құрылымы және түрi өзгерiп тұратын шамалар ретiнде қолдануға өте ынғайлы болып келедi.
Жоғарыда айтылғандай, сiлтемелiк шаманын мәнi жадтағы орналасқан мәлiметтердiн адресi. Object Pascal-да мәлiмет алмасу әрекеттерi тек қана бiр түрдегi сiлтемелiк шамалар арасында орындалады. Мысалы, келесi үзiндi берiлген:
Var
P1,P2: ^integer;
P3 : ^real;
PP : Pointer;
Begin
P1:=P2; { дұрыс қолданылған оператор }
P1:=P3; { дұрыс қолданылмаған оператор}
……………….
End.
Мысалда P1:=P2 операторы дұрыс қолданылған, ал P1:=P3 операторын қолдануға болмайды, себебi P1 және P3 әртүрлi тектегi анықталған сiлтемелер. Бұл шектеу типсiз сiлтемелiк шамаларға қатысы жоқ болғандықтан, P3-тiн мәнiн P1-ге меншiктеу үшiн PP типсiз сiлтеменi келесi түрде пайдалануға болады:
PP:=P3;
P1:=PP;
8.2 Динамикалық жадты бөлу және босату
Object Pascal-да кез келген типтелген динамикалық айнымалыларға жадта орын New процедурасы арқылы бөлiнедi. Процедурадағы көрсетiлген параметр типтiк сiлтемелiк шама болуы қажет. Процедура шақырылғанда параметрiнде көрсетiлген айнымалы сыйатын жадтын ен кiшi бос үзiндiсiнiн адресiн табады, ендi осы табылған адрестi сiлтеменiн мәнiне қайтарады, яғни орналастыруға болатын бастапқы динамикалық адреске тенестiредi. Мысалы,
Var
I, j:^ integer;
R : ^real;
Begin
New(I); New(R);
…………..
Ендi сiлтеме белгiлi бiр мәндi - адрестi қабылдағаннан кейiн жадтын осы адресi бойынша көрсетiлген тектегi кез келген мәндi орналастыруға болады. Динамикалық жадқа берiлгендердiн мәнiн жазу, меншiктеу операторы келесi түрде келтiрiледi: анықталған сiлтеменiн мәнiнен - адрестен сон сiлтемелiк белгiсi “^“ қойылады, одан кейiн меншiктеу амаланын белгiсi “:= ‘”тұрады, ал он жақта өрнек немесе белгiлi бiр мән жазылады.
Мысалы,
I^ :=20;
{Жадтын I адресiнен бастап 20 - бүтiн сан орналасады }
R^ :=2*pi; // Жадтын R адресiнен бастап
//6.28 - нақты сан орналасады
R^ := sqr(R^)+ I^ -17; { Жадтын R адресiнiн мазмұны 6.28 2+ 20 -17 }
Сонымен, сiлтеме арқылы анықталған берiлгендiн мәнiн көрсету үшiн сiлтемеден кейiн “^ “ танбасын қою керек
Динамикалық жадқа берiлгендердiн мәнiн жазу, меншiктеу операторын қолданғанда жиi кездесетiн қателiктер:
Бұрыннан бөлiнген динамикалық жадты босатуға, яғни үймеге қайтаруға болады. Ол үшiн Dispose процедурасы қолданылады. Мысалы,
Dispose (I);
Dispose (R);
Бұл процедура сiлтемелердiн мәнiн өзгертпейдi, тек бұрыннан жадтан бөлiнген аумақ үймеге қайтарылады. Сонымен қатар, босатылған сiлтемеге аталған процедураны қайтадан қолдану орындалу кезенiнiн қатесiнiн пайда болуына себеп болады. Босатылған сiлтемеге NIL деген арнайы сөздi орналастырады, осы әрекеттi белгiлеу деп атайды. Сiлтеменiн белгiленгенiн анықтау үшiн келесi тексеру қолданылады:
Const
PR: ^ Real= Nil;
Begin
………………..
if pR =Nil then
New(pR);
…………………
Dispose(pR);
PR:=Nil;
………………….. end;
Жалпы сiлтеменiн бастапқы мәнi (айнымалылар бөлiмiнде анықталғанда) кез келген болуы мүмкiн. Егер сiлтемеге New процедурасы арқылы бастапқы мәнi анықталмаса, онда осындай жағдайлар жүйелi түрде бақыланбайды және әртүрлi қателiктерге алып келуi мүмкiн.
Жоғарыда айтылғандай New процедурасы тек қана типтелген сiлтемелерге қолданылады. Типсiз сiлтемелермен жұмыс атқару үшiн GetMem және FreeMem процедуралары қолданылады:
GetMem (P,Size); // жадты бөлу
FreeMem(p,Size); // жадты босату
Мұнда, Р – типсiз сiлтеме, Size – байт өлшемiндегi жадтағы аумақтын ұзындығы.
GetMem және FreeMem процедуралардын қолданылуы келесi талаптарға сай болуын қажет етедi: босатылған және бөлiнген аумақтын адресi, онын ұзындығы бiрдей болуы тиiс.
8.3 Динамикалық жадпен жұмыс атқаратын процедуралар мен функциялар
Динамикалық сiлтемелерге қолданылатын процедура және функцияларды қарастырайық.
Addr (x) – x аргументтiн Pointer типтегi адресiн анықтайтын функция. Мұндағы х программанын кез-келген объектiсi (айнымалы, процедура немесе функциянын аты). Қайтарылған адрес кез-келген типтегi сiлтеме болуы мүмкiн.
Dispose (TP) – бұрыннан бөлiнген жадтын үзiндiсiн үймеге қайтаратын процедурасы. TP – типтiк шама. Dispose процедурасы босатылған үзiндiге қайтадан қолданылғанда орындалу кезенiнiн қатесi пайда болады.
FreeMem (P,Size) – типсiз сiлтемелiк шамаға бұрыннан бөлiнген жадтын үзiндiсiн үймеге қайтаратын процедурасы. P – типсiз сiлтемелiк шама. Size – босатылатын немесе үймеге қайтарылатын жадтын ұзындығы.
GetMem (P,Size) – типсiз сiлтемелiк шамаға жадтын үзiндiсiн бөлетiн процедура. P – типсiз сiлтемелiк шама. Size – қажеттi жадтын ұзындығы.
New (TP) – типтiк сiлтемелiк айнымалыға жадтын үзiндiсiн бөлетiн процедура. TP – типтiк сiлтемелiк шама. New процедурасы бiр рет орындалғандағы нәтижесi 65521 байттан аспайды. Егер үймеде бос орын жоқ болса, онда орындалу кезенiнiн қатесi пайда болады.
SizeOf (x) – объектiнiн iшкi көрiнiсiнiн ұзындығын (байт) қайтарады. Мұнда x – айнымалынын, функциянын немесе түрдiн аты.
Жалпы динамикалық сiлтемелердiн қолданылуы мұқият қадағалауды қажет етедi, себебi сiлтемелiк шамалармен жұмыс атқарылғандағы кеткен көптеген қателердi компилятор сезбеуi мүмкiн. Жиi кететiн қателердiн бiрi – “салбырап қалған сiлтемелер” мәселесi, олар кейбiр жағдайларда үйменiн басқа бiр аумағынын адресiн көрсетiп тұруы ықтимал.
Төменде осындай бiр жағдайдын үлгiсi келтiрiлген:
Var
P11, p12:^ integer;
Pr:^Real;
Begin
New(p11);
P12:=p11;
P12^:=2;
Dispose(p11);
New(pr);
Pr^:=2*pi;
Label1.Caption:= p12^; // ???
End;
Бұл мысалда алдын ала Label1-дiн мазмұны неге тең екендiгiн айта алмаймыз, себебi р11 сiлтемесiн босатқанда, бұл аумақ үймеге қайтарылды. Сонымен қатар келесi болжамға келуге болады. Егер Windows ортасынан тағы да бiр программа iске қосылмаған болса, онда босатылған 4 байт орын Pr-ге бөлiнiп, онын мазмұны 2* π = 6, 28318…болуы ықтимал.
Р12 сiлтемесi р11-дiң адресiн сақтағандықтан, ол сiлтеме 2* π мәнi орналасқан аумақты көрсетiп тұр. Сондықтан экран бетiне 626908797 мәні шығып тұрады.
Жалпы Windows ортасынынын динамикалық жадпен жұмыс атқаратын көптеген құралдары бар. Ендi бiрнеше мысал келтiрейiк.
8.4 Динамикалық жадқа тiзiмдi орналастыру
TList класы арқылы ұзындығы кез келген тізбекті динамикалық жадқа орланастыруды және онын элементтеріне индексі арқылы қол жеткізуді ұйымдастыруға болады. Осындай тізбекті тізім деп атайды және олармен жұмыс істеу массивпен жұмыс істеуiмен ұқсас болып келеді. Ал екеуінін айырмашылығы келесіде: тізімнін элементтер саны программа орындалу барысында өзгеріп отыратындығы және тізімнін құрамындағы берілгендердін тегі әртүрлі болуы.
Шынында, тізім дегеніміз динамикалық жадта орналасқан элементтерге типсіз сілтемелердін массивтері деп саналады. Бұл массивтер үймеде орналасады, сондықтан тізімнін ұзындығын динамикалық түрде өзгертуге болады. Ал тізімнін құрамындағы типсіз сілтемелер кез келген түрдегі берілгендерге сілтеуге мүмкіндік туғызады.
TList класынын бірнеше қасиеттерін ажыратады: Count : Integer – тізімдегі элементтер саны. Ол қасиеттін мәні элемент қосылғанда немесе жойылғанда өзгереді; Capasity: Integer – тізімдегі сілтемелер массивінін ұзындығы. Әрқашанда Capasity > Count. Егер келесі элемент қосылғанда Count = Capasity болса, онда тізімге 16 элемент автоматты түрде қосылып отырады; Items (Index:Integer): Pointer – көрсеткішті индексіне сәйкес элементке жылжытады. Тізімдегі алғашқы элементтін индексі – 0-ден басталады; List:pPointerList – көрсеткішті тізімнін элементтер массивіне қайтарады.
PPointerList тегі келесі түрде анықталған:
Type
PPointerList =^ TPointerList;
TPointerList= array [0.. MaxListSize) of Pointer;
MaxListSize мәні жадтағы бос кеністігімен шектеледі. Count -тізімге орналастырылған элементтер санын анықтайды, ал Capasity – тізімнін ағымдағы ұзындығын көрсетеді. Егер тізімге келесі элемент қосылғанда тізімнін сонына жетсек, онда тізімнін ұзындығы өсіп отырады (Count<5 үшін 4 элементке, 4< Count<8 – 8-ге, Count >7-ге 16 элементке).
Кластын әдістері:
Бақылау сұрақтары: