Программалау іі» пәні бойынша 050111 «Информатика»



бет5/29
Дата14.06.2016
өлшемі2.7 Mb.
#135126
түріПрограмма
1   2   3   4   5   6   7   8   9   ...   29

Әдебиеттер:


1.А.Г.Гольцев «Объектно-ориентированное программирование и его реализация в языке Паскаль», Москва-2005

2. В.В.Фараонов «Турбо Паскаль 7.0», Москва-2001

3. Электронный учебник: «Введение в объектно-ориентированное программирование», 2006
1. Берілгендер массивін орналастыру үшін динамикалық жадыны пайдалану

Өте үлкен берілгендер массивін орналастыру үшін динамикалық жадыны қалай пайдалануға болатындығын қарастырайық. Айталық EXTENDED типтегі тікбұрышты 100х200 матрицаның элементтерін пайдалануға мүмкіндік туғызуымыз керек. Мұндай массивті орналастыру үшін 200000 байт жады қажет (100*200*10).

Бұл мәселені келесі жолмен шешуге болатындай көрінеді:
var

i, j : integer;

PtrArr : array [1..100, 1..200] of ^real;

begin


for i:=1 to 100 do

for j:=1 to 200 do

new(PtrArr[i,j]);

……

end.



Енді құрылған динамикалық массивтің әрбір элементін адресі арқылы қолдануға болады, мысалы:
PtrArr[i,j]^ :=0;

If PtrArr[I,j*2]^ > 1 then ….

Алайда, көрсеткіш жадыдан 4 байт орын алатыны белгілі, сондықтан PtrArr массивін орналастыру үшін 100*200*4=80000 байт қажет. Бұл берілгендерді статикалық орналастыруға арналған берілгендер сегментінің (65536) көлемінен асып кетеді.

Бұл жағдайдан шығудың бір жолы адрестік арифметиканы, яғни көрсеткіштер үстінен жүргізілетін арифметиканы қолдану болар еді. Өйткені PtrArr массивін құрмай-ақ матрицаның кез-келген элементінің пайдаланудан алдын адресін есептеп шығаруға болар еді. Алайда Турбо Паскальда көрсеткіштер үстінде меншіктеу мен қатынас амалдарынан басқа ешқандай амал анықталмаған.

Солай бола тұрса да, берілген мәселені шешуге болады. Кез-келген көрсеткіш WORD типті екі сөзден (сегмент және жылжу) құрылатыны белгілі. Турбо Паскалда осы сөздердің мәнін алуға мүмкіндік беретін WORD типті екі функция анықталған:

SEG(X) – адрестің сегмент бөлігін қайтарады;

OFS(X) – жылжуды қайтарады;

Х аргументі ретінде кез-келген айнымалыны алуға, соның ішінде көрсеткіш көрсетіп тұрған айнымалыны да алуға болады. Мысалы,

var

p: real;


begin

……

new(p);



p^:=3.14;

……

end.


берілген болсын, онда SEG(P) функциясы 4-байтты P көрскеткіші орналасқан адрестің сегментті бөлігін қайтарады, ал SEG(P^) – 3.14 саны сақталынған үйменің 6-байтты сегменті.

PTR(SEG, OFS: WORD) :POINTER функциясы арқылы көрсеткіштің кез-келген типті көрсеткішпен үйлесімді болатын мәнін құруға болады. Сонымен, мынадай әрекеттер тізбегі мүмкін болады. Алдымен, GETMEM процедурасы арқылы үймеден керекті ұзындықтағы бірнеше фрагменттер иеленеді(процедураны бір шақырғанда динамикалық жадыны 65521 байттан артығын иеленуге болмайды). Қарастырылып отырған мысал үшін(матрицаның қатарлары сыиа алатындай) мына ұзындықтағы фрагментті иеленген дұрыс: 200*10=2000 байт. Әрбір фрагменттің басы, яғни әрбір қатардың жадыда орналасу басы 100 көрсеткіштен құрылған PTRSTR массивінде сақталады. Енді қатардың кез-келген элементін пайдалану үшін осы элементтің қатар басына қатысты жылжуын есептеп шығару және сәйкес көрсеткішті құру керек:


var

i, j : integer:

PtrStr : array [1..100] of pointer;

pr : ^ real;

const

SizeOfReal = 6;



begin

for i:= 1 to 100 do

GetMem (PtrStr[i], SizeOfReal*200);

……..


{[i,j] матрица элементтерін пайдалану}

pr :=ptr(seg(PtrStr[i]^), ofs (PtrStr[i]^)+(j-1)*SizeOfReal);

if pr^>1 then

……..


end.

Адресті есептеу PR:=PTR… операторы программада бірнеше рет орындалатын болғандықтан, матрица элементінің мәнін қайтаратын GETR функциясын және элементтің жаңа мәнін беретін PUTR процедурасын енгізген ыңғайлы. Бұлардың әрқайсысы өз кезегінде адресті есептеу үшін ADDRR функциясынан пайдаланады.


2. Матрица элементтерінің орташа мәнін есептеу мысалы

Төмендегі мысалда кездейсоқ сандардың n x m матрицасын құрып жадыға жазатын және элементтерінің орташа мәнін есептейтін программа келтірілген.


const

sizeOfReal =6; {real типті айнымалының ұзындығы}

n= 100; {бағандар саны}

m = 100; {қатарлар саны}

var

i,j : integer;



PtrStr: array [1..10] of pointer;

s : real;

type

RealPoint = ^real;


Function AddrR(i, j:word) : RealPoint; {нақты мәнді айнымалы үшін і сегменті, j жылжуы бойынша адрес береді }

begin


AddrR := ptr(seg(PtrStr[i]^), ofs(PtrStr[i]^)+(j-1)*SizeOfReal);

end; {AddrR}

Function GetR (i, j : integer): real; {адресінің i сегменті және j жылжуы бойынша

айнымалының мәнін береді}

begin

GetR := AddrR(i, j) ^



end; {GetR};

procedure PutR (i,j : integer; x: real); {cегменті і, жылжуы j болған адрестегі айнымалыға х нақты мәнін жазып қояды}


begin

AddrR(i, j)^ := x;

end; {PutR}
begin {Main}

for i:=1 to n do

begin

GetMem(PtrStr[i], m*SizeOfReal);



For j:=1 to m do Putr(i, j, Random)

end;


s:=0;

for i:=1 to n do

for j:=1 to m do

s:= s+GetR(i, j);

writeln(s / (n*m) :12:10)

end. {Main}

Бұл мысалда әрбір қатар үймеде параграф шекарасынан бастап орналасады деп ұйғарылған, және әрбір PTRSTR көрсеткіші үшін жылжу 0-ге тең. Ал шын мәнінде GetMEM процедурасын кейінгі шақыруларында кезекті фрагменттің басы алдыңғысынан кейін бірден басталады және сегменттің шекарасына сәйкес түспеуі мүмкін. Нәтижеде максимал ұзындықтағы(65521) фрагменттерді орналастырған кезде соңғы байттың жылжуын есептеген кезде асып кетуі мүмкін.


Лекция 5

Динамикалық жадымен жұмыс істеуге арналған процедуралар мен функциялар. Үйме администраторы.

Жоспар:


1. Динамикалық жадымен жұмыс істеуге арналған процедуралар мен функциялар

2. Үйме администраторы


Әдебиеттер:


1.А.Г.Гольцев «Объектно-ориентированное программирование и его реализация в языке Паскаль», Москва-2005

2. В.В.Фараонов «Турбо Паскаль 7.0», Москва-2001

3. Электронный учебник: «Введение в объектно-ориентированное программирование», 2006
1. Динамикалық жадымен жұмыс істеуге арналған процедуралар мен функциялар

Динамикалық жадымен жұмыс істеуге арналған тағы бір қатар процедуралар мен функциялармен танысалық.



ADDR функциясы. Аргумент адресі сақталған POINTER типті көрсеткіштің нәтижесін қайтарады.

ADDR (X)

Мұнда Х – программаның кез-келген объекті(кез-келген айнымалының, процедураның, функцияның атауы). Қайтарылатын адрес кез-келген типтегі көрсеткішпен үйлесімді. Осындай нәтижені @ операциясы да қайтарады.


CSEG функциясы. Микропроцессордің СS регистрінде сақталынған мәнді қайтарады (программаның жұмысы басында СS регистрінде программа кодының басы сақталынады). Шақырылуы: СSEG

WORD типіндегі сөзді қайтарады.



DISPOSE процедурасы. Үймеге динамикалық жадының бұдан бұрын типтендірілген көрсеткішке бөлінген фрагментті қайтарады. Шақырылуы: DISPOSE (TP)

Мұнда ТР – типтендірілген көрсеткіш . Процедура босатылып қойған фрагмет үшін қайта шақырылатын болса программа орындалу кезінде қателік туындайды.



DSEG функциясы. Микропроцессордың DS регистрінде сақталған мәнді қайтарады(DS регистрінде программа жұмысының басында программа берілгендерінің бастамасының сегменті бар болады). Шақырылуы:

DSEG


Нәтиже WORD типті сөзде қайтарылады.

MAXAVAIL функциясы. Үйменің ең үлкен үздіксіз бөлігінің көлемін байт бірлігінде қайтарады. Шақырылуы:

MAXAVAIL


Нәтиже LONGINT типті болады. NEW және GETMEM процедураларын бір шақыруда осы функцияның қайтаратын мәнінен артық жады иеленуге болмайды.

MEMAVAIL функциясы. Үйменің жжалпы бос кеңестігін байт өлшемінде қайтарады. Шақырылуы:

MEMAVAIL

Нәтиже LONGINT типті.

OFS функциясы. Көрсетілген объекттің адресінің жылжуына ие WORD типті мәнді қайтарады. Шақырылуы:

OFS(X)


Мұнда Х – кез-келген типтегі өрнек, немесе процедура атауы.

PTR функциясы. Берілген сегмент SEG және жылжу OFS бойынша POINTER типті мән қайтарады. Шақырылуы:

PTR(SEG, OFS)

Мұнда SEG – сегмент сақталған WORD типіндегі өрнек;

OFS – жылжу сақталған WORD типіндегі өрнек.


2. Үйме администраторы

Айтылып кеткеніндей, үйме администраторы – пайдаланушы программасының үймемен өзара әрекетін қамтамасыз ететін қызметші ішкі программа. Үйме администраторы NEW, GETMEM, DISPOSE, FREEMEM, т.б. процедуралардың сұранысын өңдейді және HEAPРTR, FREELIST көрсеткіштерінің мәнін өзгертеді. HEAPРTR көрсеткішінде үйменің бос бөлігінің төменгі шекара адресі сақталады. Ал, FREELIST көрсеткішінде бос блоктың сипаттаушысының адресі сақталады. SYSTEM модулінде FREELIST көрсеткіші POINTER ретінде сипатталған, алайда, іс-жүзінде ол берілгендердің келесі құрылымына нұсқайды:

Type

PFreeRec = ^ TFreeRec;



TFreeRec record

Next : pointer;

Size : pointer;

End;


Бұл тізімдік қрылым HEAPPTR шекарасынан төмен орналасқан жадының барлық бос блоктарын сипаттауға арналған. Блоктардың пайда болуы кезекпен NEW-DISPOSE не GETMEM-FREEMEM процедураларын кездейсоқ қолданудан болады. (Үйменің «ұяшықтық» құрылымы). PFREEREC жазбасындағы NEXT өрісі тізім бойынша келесі үйменің бос блогын немесе егер тізімде соңғы болса, онда HEAPEND-ке сәйкес келетін адресті өзінде сақтайды. SIZE өрісі нормал емес бос блоктың ұзындығын немесе егер, HEAPРTR-гі адрестен төмен бос блок жоқ болса, онда 0 мәніне ие болады. Нормал емес ұзындық былай анықталады: осы өрістің жоғарғы (старший) сөзінде бос параграфтардың шамасы сақталады. Ал, кішісінде – 0...15 диапазонындағы бос байттар көлемі сақталады. Келесі функция SIZE өрісінің мәнін бос блоктың нақты (іс-жүзіндегі) ұзындығына түрлендіреді:

Function BlbckSize(Size: pointer): Longint;

{Функция преобразует ненормализованную длину свободного блока в байты}



type

PtrRec = record

Lo, Hi : word

end; 


var

LengthBlock: Longint; 

begin

BlockSize := Longint(PtrRec(Size).Hi)*16 + PtrRec(Size).Lo 



end;

Программаны іске қосқан соң HEAPPTR және FREELIST көрсеткіштерінде үйме басына сәйкес келетін адрес (екеуінде де) сақталады(бұл адрес HEAPORG көрсеткішінде сақталынады). Үйменің бірінші 8 байтында TFREEREC типті жазба сақталады(NEXT өрісінде HEAPEND мәнімен сәйкес келетін адрес, ал SIZE өрісінде 0 бар болады, бұл динамикалық жадыда “ұяшықтар” жоқ екендігінің қосымша белгісі). Үймемен жұмыс істеу барысында HEAPPTR көрсеткішіндегі адрестен (шекарадан) төмен жерде кемінде бір бос блок пайда болмайынша HEAPPTR мен FREELIST көрсеткіштері бірдей мәнге ие бола береді. Пайда болған жағдайда FREELIST көрсеткіші осы блоктың басына нұсқайды, ал босатылған блоктың бірінші 8 байтында жазбасы орналастырылады. FREELIST тізімінің басы ретінде пайдаланып, пайдаланушының программасы әрдайым барлық бос блоктардың тізімін қарап шыға алады және қажет болса оны модификациялай алады.

Баяндалған бұл механизм үйме администраторымен жұмыс істеуге байланысты бір кемшілікті тудырады: администратор кез-келген босатылған блокқа осы блоктың сипаттаушысын орналастыруы керек, ал бұл блоктың ұзындығы 8 байттан кем болмауы керек дегенді білдіреді. Үйме администраторы әрдайым жадыны өлшемі 8 байтқа(TFREEREC жазбасының көлеміне бөлінетін) бөлінетін блок ретінде (сұрағандарға) ажыратады. Егер программа 1 байт сұраса да, администратор оған іс-жүзінде 8 байт ажыратады. Сол сияқты 2,3,...8 байт сұралған жағдайлардың барлығында да 8 байттан ажыратылады. Ал, 9 байт сұралған жағдайда 16 байтты блок ажыратылады. Динамикалық жадыны үнемдегіміз келсе, бұл жағдайды ескеруіміз керек.

Егер NEW не GETMEM функцияларын кезекті рет шақырған кезде администратор үймеде қажетті бос блокты таба алмаса, ол HEAPERROR айнымалысында сақталған адрестегі функцияны шақырады. Бұл функция келесі процедуралық типке сәйкес келеді:

Type

HeapErrorFun = function (Size : word): integer;



Мұнда Size – ол үшін бос динамикалық жады жоқ болған айнымалының көлемі. Стандартты функция (адресі программаны іске қосқан кезде HEAPERROR айнымалысында сақталатын) 0 мәнін қайтарады, бұл программаны 203 кодты(203 Heap overflow error-Үйменің толып кеткен) қателік бойынша программаның тоқтатылуына әкеледі. Бұл функцияны қайтаанықтап, программа тоқтатылуының алдын алуға болады. Бұл үшін өз функциямызды жазып, оның адресін HEAPERROR көрсеткішіне орналастыру керек. Мысалы:

Function HeapFunc(Size: Word): Integer; far; 

begin

HeapFunc := 1 end; 



begin {Негізгі программа}

HeapError := @HeapFunc;

.......

end.


Айта кетейік, HEAPERRORFUN типті функция динамикалық жадыдан орын сұрау қанағаттандырылмаған жағдайда ғана шақырылады. Ол 3 мәннің бірін қатаруы мүмкін:

0 – программа жұмысын тоқтату;

1 – сәйкес көрсеткішке NIL мәнін меншіктеп, программа жұмысын жалғастыра беру;

2 – жады бөлуді қайталау; әрине, бұл жағдайда HEAPERRORFUN типті функцияның ішінде керекті өлшемдегі жадыны босату қажет.





Достарыңызбен бөлісу:
1   2   3   4   5   6   7   8   9   ...   29




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет