Файл: Основы программирования на языке Pascal ( Ссылочные типы в языке программирования Pascal. Создание ссылочных типов ).pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 01.04.2023

Просмотров: 104

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Пример 1.

Type mas = array[1..50] of real;

mas1: ^mas; {mas1 – указатель}

Пример 2.

Var t: ^integer;

s: ^string;

a: mas1;

Для объявления переменных не связанных с типом данных можно использовать указатель без типа (pointer).

Пример 3.

Var  p: pointer; 

где

pointer  – это не типизированный указатель, занимающий в памяти 4 байт (2-байта сегмент, 2байта смещение). [4]

Существует такое понятие, как переменная с указателем. Например: А^ - переменная с указателем, с ней можно обращаться также как с переменной указанного типа.

Пример 4.

t^ := 15;

t^ mod 2;

Память под динамическую величину, которая связана с указателем, выделяется в результате выполнения процедуры New(). Формат обращения к данной процедуре следующий:

New(<переменная-указатель>);

Пример 5.

New(t);

Динамические объекты не должны храниться в памяти компьютера вечно. Существует стандартная функция, уничтожающая динамический объект и освобождающая память компьютера, которую объект занимал. Формат обращения к данной процедуре следующий:

DISPOSE(<указатель>);

Пример 6.

DISPOSE(T)

Чередование обращений к процедурам New() и Dispose() обычно приводит к фрагментации, т.е. образованию ячеистой структуры памяти. При очередном обращении к процедуре New() отыскивается наименьший свободный фрагмент, в котором может поместиться новая переменная. [17]

Для освобождения целого фрагмента «кучи», перед началом выделения динамической памяти текущее значение указателя на границу незанятой динамической памяти запоминается в переменной указателе с помощью процедуры MARK().

Формат обращения к данной процедуре следующий:

MARK(<переменная-указатель>);

Для того чтобы освободить фрагмент «кучи» используется процедура RELEASE().Формат обращения к данной процедуре следующий:

RELEASE (<указатель>);

Пример 7.

Пример использования процедур New(),Mark(),Release() и Dispose().

var

t, t1, t2: ^integer;

begin

New(t1);

Mark(t);

New(t2);

Release(t);

Dispose(t1);

Dispose(t2);

end.

При работе с указателями и динамическими переменными важно помнить:

  • Необходимо освобождать выделенные области сразу после того, как потребность в них отпадает, во избежание «засорения» памяти компьютера.
  • В связи с использованием процедуры New() возможно возникновение проблемы исчерпания области памяти компьютера, которая отведена для динамических переменных. В случае если при выполнении процедуры New() выяснится, что для размещения новой динамической переменной не хватает памяти, то значение указателя, которое передано в параметре процедуры не изменится. При этом выполнение программы не приостановится и никаких сообщений пользователю выдано не будет. Следовательно, работа с таким указателем может привести к различным непредсказуемым последствиям.
  • После выполнения процедуры Dispose() указатель на освобожденную область памяти не обнуляется (значение указателя остается доступным в программе), что означает сохранение возможности доступа к формально уже недоступной области памяти, которая к моменту такого ошибочного доступа уже может быть выделена для других переменных обращением к процедуре New().
  • Необходимо учитывать проблему, которая связана с противоречием между стековым принципом размещения статических переменных, а также произвольным характером создания и удаления этих переменных. Существует правило, согласно которому, все локальные переменные подпрограммы перестают существовать после завершения подпрограммы. [17]

Пример 8.

Program primer;

Uses crt;

procedure PrProc;

var

а: integer;

t: ^string;

begin

new(t);

...

end;

begin

clrscr;

writeln(memAvail);

someProc;

writeln(memAvail);

readkey;

end. [4]

Пример показывает, что исчезает локальная переменная tоднако область памяти, которая отведена под эту переменную, остается поскольку освободить ее можно только явно путем использования процедуры Dispose().

В примере используется следующая функция:

  • MemAvail: LongInt (без параметров), которая возвращает размер (в байтах) общей свободной области памяти в текущий момент.

Кроме того, существует функция:

  • MaxAvail: LongInt (без параметров), которая возвращает размер максимальной свободной области памяти в текущий момент.

Для выделения и освобождения области динамической памяти без учета типа используются следующие процедуры:

  • GetMem (<нетипизированный указатель>, <размер памяти в байтах>) – выделение памяти;
  • FreeMem (<нетипизированный указатель>, <размер памяти в байтах >) – освобождение памяти.

Данные процедуры имеют два параметра:

  • указатель типа;
  • размер выделяемой либо освобождаемой памяти в байтах.

Важно помнить, что освобождать необходимо ровно столько байтов, сколько было выделено, и по тому же самому указателю. [6]

Пример 9.

type tRec = record

a: real;

b: real;

end;

tPrim = ^tRec;

tInt=^integer;

Var

p: pointer;

rc: tRec;

q: tInt;

begin

rc.a := 0.3;

rc.b := 0.16;

GetMem(p, SizeOf(tRec));

new(q);

p := q;

tInt(p)^ := q^;

tPrim(p)^ := rc;

end.

Применение нетипизированных указателей позволяет осуществлять неявное преобразование типов «по одному и тому же адресу можно размещать различные типы данных, главное чтобы они занимали одинаковый объем памяти».

Чтобы получить доступ к значению некоторой динамической переменной необходимо выполнить операцию разыменования указателя. [19]

Пример 10.

t^:=3;

Использование операции разыменования допускается для любых ссылочных типов. Однако важно помнить, что разыменование является некорректным в случае, если ссылочная переменная имеет значение nil. В данном случае не существует переменной, на которую ссылается указатель, следовательно, последовательность операторов

t := nil;

t^ := 3;

является недопустимой.

Дальнейшее использование динамических переменных происходит так же, как со статическими переменными соответствующих типов. Динамическим переменным можно присваивать значения, их можно использовать в качестве операндов в выражениях, параметров подпрограмм и т.д.


Пример 11.

t^ := 51;

s^ := 'Read';

for i := 1 to 50 do

pm^[i] := i + 3;

Динамические переменные, как правило, используют в связанных структурах данных. Простейшим способом описания связанных объектов является однонаправленный список (например, очередь). [3]

Выделяют пять основных операций над списками:

  • формирование списка;
  • просмотр элементов списка;
  • поиск элемента в списке;
  • удаление элементов звена в списке;
  • вставка элементов звена в списке. [9]

Далее приведем примеры использования операций над однонаправленными списками (рис. 3-7). [17]

Рис. 3 – Формирование списка

Рис. 4 – Процедура просмотра элементов списка

Рис. 5 – Функция поиска элементов в списке

Примечание к рисунку:

Функция poisk возвращает значение true, если элемент С принадлежит списку и значение false, если элемент С не принадлежит списку. Кроме того, функция ищет значение AD (адрес ссылки звена элемента С). [17]

Рис. 6 – Процедура удаления звена из списка

Рис. 7 – Процедура вставки элементов звена в список

3. Примеры программ ЯП Pascal с использованием ссылочных типов

Далее приведем примеры программ для работы с ссылочными типами данных.

Пример 12.

Создать одномерный массив случайных чисел, разместить его в динамической памяти, вычислить сумму элементов и удалить массив из памяти.

Текст программы:

Program Primer12;
Uses crt;
Const

t1 = 100;

t2 = 2*t1+1;
Type

Item = Real;

DMas = array[1..$FFF0 div SizeOf(Item)] of Item;

DPrt = ^DMas;
Var

mas: DPrt;

i, n: Word;

sum, f: Real;

{$R-} 
Begin
clrscr;
randomize;
writeln('Введите количество элементов массива: ');
readln(n);
GetMem(mas,n*SizeOf(Item));
for i:=1 to n do
begin
f := random(t2);
mas^[i] := t1-f;
end;
writeln;
s:=0;
for i:=1 to N do sum:=sum+mas^[i];
writeln(^G'Результат:',^J^M,^G'Сумма = ',sum);
FreeMem(mas,n*SizeOf(Item));
repeat until KeyPressed
end.

Результат выполнения программы:

Введите количество элементов массива: 200
Результат:
Сумма = -1.8610000000E+03

Пример 13.

Создать двумерный массив случайных чисел, разместить его в динамической памяти, вычислить сумму элементов и удалить массив из памяти.


Текст программы:

Program Primer13;

Uses crt;

Type

Item = Real;

DMas = array[1..$Ff div sizeof(item),1..$ff div sizeof(item)] of Item;

DPrt = ^DMas;

Var

Mas: DPrt;

k, j, l, i, n: Word;

sum: Real;

{$R-}
Begin
clrscr;
randomize;
writeln('Введите количество строк массива: '); 

readln(i);

writeln('Введите количество столбцов массива: '); 

readln(j);

n := i*j;

GetMem(Mas,n*SizeOf(Item));
for k:=1 to i do

for l:=1 to j do  Mas^[k,l] := random(2000);

sum := 0;

for k:=1 to i do

for l:=1 to j do sum := sum+Mas^[k,l];

writeln(^G'Результат:',^J^M,^G'Сумма = ',sum);

FreeMem(Mas,n*SizeOf(Item));
repeat until KeyPressed

end.

Результаты выполнения программы:

Введите количество строк массива: 1000

Введите количество столбцов массива: 1000

Результат:
Сумма = 5.7820716634E+32

Пример 14.

Создать запись, разместить ее в динамической памяти, удалить запись из памяти.

Текст программы:

Program Primer14;

Uses crt;

Type

FRec = Record

FName: string[50];

FAge: Byte;

end;
FPtr = ^FRec;

Var

t : FPtr;

Begin
clrscr;
GetMem(t, SizeOf(FRec)); {Выделение памяти в куче}

write('Введите имя: ');  Readln(t^.FName);

write('Введите возраст: ');  Readln(t^.FAge);

writeln;
writeln('Память для записи распределена в  куче');

writeln;
writeln('Имя: ',t^.FName);

writeln('Возраст: ',t^.FAge);

writeln;
FreeMem(t, SizeOf(FriendRec));   {Освобождение памяти}

writeln('Память освобождена');

repeat until KeyPressed;

End.

Результат выполнения программы:

Введите имя: Степанов Олег Игоревич

Введите возраст: 24

Память для записи распределена в  куче

Имя: Степанов Олег Игоревич

Возраст: 24

Память освобождена

Пример 15.

Дан файл целых положительных чисел, который состоит из нескольких последовательностей чисел. Каждая из последовательностей заканчивается числом -1. Необходимо вывести в выходной файл данные последовательности чисел, однако числа внутри каждой последовательности должны быть расположены в обратном порядке.

Пояснение:

147 225 2168 -1 (входной файл);

741 522 8612 -1 (выходной файл)

Длина данных последовательностей заранее неизвестна, а значит, переменные в которые будут считываться числа должны иметь динамическую структуру.

Текст программы:

Program Primer15;

Uses crt;

Type

Ptr = ^DSet;

DSet = record

RData: integer;

RPtr: Pointer

end;

Var

t1, t2: Ptr;

i: integer;

In, Out: file of integer;

Begin

clrscr;

reset(In);

rewrite(Out);

while not Eof(In) do

begin

t1 := nil;

read(In, i);

while I <> -1 do

begin

new(t2);

t2^.RData := I;

t2^.RPtr := t1;

t1 := t2;

read(In, i);

end;

t2 := t1;

while t2 <> nil do

begin

write(Out, t2^.TData);

t2 := t2^.Ptr;

end;

write(Out, '-1');

end;

readln;

End.

Результат выполнения программы:

147 225 2168 -1

741 522 8612 -1


4. Циклические программы

Наиболее часто в практике программирования встречаются циклические программы. В циклических программах какой-либо алгоритм повторяется многократно, при этом один из параметров изменяется. Например, описанная в п. 3.3 программа Рrim 4 является классическим примером циклического алгоритма. Операторов цикла в Паскале три: for, repeat, while.

4.1. Оператор for

Оператор состоит из заголовка, в котором определяется порядок изменения переменной параметра цикла и тела цикла, являющегося многократно повторяющимся алгоритмом. Общий вид оператора:

for – параметр цикла: = начальное значение to, конечное значение do {заголовок}; оператор; {тело цикла}. Этот оператор применяется, если начальное значение < конечного значения;

for – параметр цикла:=начальное значение downto, конечное значение do; оператор; применяется, если начальное значение > конечного значения.

Пример: найти сумму квадратов целых чисел от 8 до 1.24.

Program Prim12; Var i,s:integer; BEGIN s:=0; for i:= 8 to 124 do s:=s+sqr(i); writeln('s=',s); readln;

END.

Работа программы. В разделе Var выделяется ячейка памяти с именем i и s для хранения величин. Поскольку в S мы должны накапливать сумму, то вначале занесем в S ноль. Затем заголовок цикла присваивает i=8. далее выполняется тело цикла: извлекается содержимое ячейки S (а там у нас 0) и к этому содержимому прибавляется sgr(i), т.е. i2=82. Результат присваивается ячейке S, т.е. в S теперь 82.

Проверяется, не стал ли параметр цикла больше конечного значения параметра 128. Это не произошло, поэтому i присваивается следующее значение равное 9 и вновь выполняется тело цикла, т.е. S:=82+92. Можно сказать так: S присвоить тому S, которое было раньше, + следующее i2. Так как цикл еще не закончился, то i станет равным 10, а S присвоится тому S, которое было раньше, т.е. 82+92, и к нему прибавится еще текущее i2, т.е. 102. Этот процесс повторяется до тех пор, пока параметр цикла не станет равным 124. Тогда в последний раз 1242 прибавляется к накапливаемой сумме.

Итак: выполнение цикла значения i значения S

1 8 82

2 9 82+92

3 10 82+92+102

– – – – – – – – – – – – – – – – – – – – – – – – – – – – – –

116 117 82+92+102+…+1232