Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

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

Содержание:

Введение

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

Цель работы: изучить ссылочные типы данных в языке программирования (ЯП) Pascal и их использование при решении конкретных задач.

Задачи работы:

  • ознакомиться с ЯП Pascal;
  • рассмотреть основные типы данных ЯП Pascal;
  • дать понятия «ссылочный тип данных», «указатель» в ЯП Pascal;
  • рассмотреть основные процедуры и функции, используемые для создания ссылочных типов в ЯП Pascal;
  • привести примеры программ с использованием ссылочных типов данных на ЯП Pascal.

Объект исследования: типы данных в ЯП Pascal.

Предмет исследования: ссылочные типы данных в ЯП Pascal.

В первой главе представленной работы даны краткие сведения о ЯП Pascal, дано понятие типа данных, приведена классификация типов данных в ЯП Pascal и их основные характеристики.

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

В третьей главе рассмотрены примеры решения конкретных задач в ЯП Pascal (текст программы, результат выполнения программы) с использованием ссылочных типов данных, выделением для их размещения памяти, а также ее освобождения при отсутствии потребности в данных переменных.

В данной курсовой работе использовались труды авторов: Боженова М. М., Москвина Л. А. Демидов Д. В., Долинский М. С., Культин Н. А., Меженный О. А., Немнюгин С. А., Павловская Т.А., Попов В. П., Рапаков Г. Г., Ржецкая С. Ю., Семакин И. Г., Шестаков А.П., Фаронов В. В., Шпак Ю.А.

1. Язык программирования Pascal. Типы данных

Язык программирования Pascal является  одним из наиболее известных языков программирования, который с успехом используется для обучения программированию в школе и ВУЗах.

ЯП Pascal создан Н. Виртом в 1968-1969 гг. после участия в работе комитета по разработке стандарта языка Алгол-68. ЯП получил название в честь французского физика, математика, литератора и философа Б. Паскаля, создавшего первую в мире механическую машину, которая складывает два числа. [20]

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

  • возможные значения переменных, функций, констант, выражений, которые принадлежат к данному типу;
  • внутреннюю форму представления данных в компьютере;
  • операции и функции, которые можно выполнять над величинами, которые принадлежат к данному типу. [15]

В программа на ЯП Pascal тип величины задается заранее, т.е. все переменные, которые используются в программе, необходимо объявить в разделе описания с указанием типа переменных. Подобное обязательное описание типа переменных приводит к избыточности в тексте программы, но данная избыточность является важным вспомогательным средством при разработке программ и рассматривается в качестве необходимого свойства современных алгоритмических языков высокого уровня. [10]

В ЯП Pascal выделяют простые и структурированные типы данных (рис.1).

Рис. 1 – Типы данных в Pascal

Простые типы данных делятся на:

  • порядковый тип;
  • вещественный тип.

Порядковый тип данных делится на следующие типы:

  • целый (целочисленный): выделяют пять целых типов, которые определяются в зависимости от знака переменной и значения, которое будет принимать переменная (byte, integer и др.);
  • логический (boolean): переменные принимают только два значения: true (истина) или false (ложь);
  • символьный (char): значение символьного типа – это множество всех символов компьютера, каждому из которых присваивается целочисленный код от 0 до 255. [8]

Для порядковых типов значения всегда сопоставляются с рядом целых чисел и представляются в компьютере абсолютно точно. В отличие от порядковых типов значения вещественных типов определяют произвольное число с некоторой конечной точностью, которая зависит от внутреннего формата вещественного числа. [2]

Рис. 2 – Диапазоны значений простых типов данных

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

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

Существует различные методы структурирования.

По способу организации и типу компонент в структурированных типах данных выделяют следующие разновидности:

  • регулярный тип (массив);
  • множественный тип (множество);
  • комбинированный тип (запись);
  • файловый тип (файл);
  • строковый тип (строка). [11]

Кроме того, в ЯП Turbo Pascal (версия 6.0 и выше) введен объектный тип данных (т.е. объекты).

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

Массив – это множество однотипных элементов, которые объединены общим именем и занимают в компьютере определённую область памяти. Количество элементов в массиве всегда конечно.

В общем случае массив – это структурированный тип данных, который состоит из фиксированного числа элементов одинакового типа.

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

Описание массива выполняется в ЯП Pascal следующим образом:

<имя типа> = array [<список индексных типов>] of <тип>;

где

<имя типа> – идентификатор;

array и of – зарезервированные слова;

<список индексных типов> – список из одного или нескольких индексных типов, которые разделены запятыми;

квадратные скобки – требование синтаксиса ЯП Pascal;

<тип> – любой тип ЯП Pascal.

В качестве индексных типов в ЯП Pascal могут использоваться любые порядковые типы, за исключением типа LongInt и типов-диапазонов с базовым типом LongInt. [5]

Запись – это структура данных, которая состоит из фиксированного числа компонентов, называющихся полями записи.

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

Описание записи выполняется в ЯП Pascal следующим образом:

<имя типа> = record <список полей> end;

где

<имя типа> – идентификатор;

record и end – зарезервированные слова (запись, конец);

<список полей> – список полей, который представляет собой последовательность разделов записи, отделяемых точкой с запятой. [12]

Множество – это набор однотипных объектов, которые логически связаны друг с другом. Количество элементов, которые входят в множество, может изменяться от 0 до 256. Множество, которое не содержит элементов, называется пустым множеством.

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

Описание множества выполняется в ЯП Pascal следующим образом:

<имя типа> = set of <базовый тип>;

где

<имя типа> – идентификатор;

set и of – зарезервированные слова (множество, из);

<базовый тип> – базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, за исключением integer, word, LongInt. [7]

Файл – это либо именованная область внешней памяти компьютера, либо логическое устройство, т.е. потенциальный источник или приемник данных.

Любой файл имеет три характеристики:

  • имя файла;
  • файл содержит компоненты одного типа (тип может быть любым, кроме файлов);
  • длина создаваемого файла не оговаривается при его объявлении и ограничивается емкостью устройств внешней памяти.

Описание файлового типа выполняется в ЯП Pascal следующим образом:

<имя> = file of <тип>;

или

<имя> = text;

или

<имя> = file;

где

<имя> – имя файлового типа (идентификатор);

file и of – зарезервированные слова (файл, из);

text – имя стандартного типа текстовых файлов;

<тип> – любой тип ЯП Pascal, кроме файлового типа. [1]

В зависимости от способа объявления файла выделяют три вида файлов:

  • типизированные файлы (задаются предложением file of …);
  • текстовые файлы (тип text);
  • нетипизированные файлы (тип file). [14]

2. Ссылочные типы в языке программирования Pascal. Создание ссылочных типов

Все перечисленные ранее типы данных ЯП Pascal относятся к так называемым статическим типам данных. Это означает, что место в памяти под данные переменные компилятором отводится до запуска программы (т.е. во время компиляции).

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

Для переменных динамических типов отводится динамическая память, имеющая стековую структуру и называемая «кучей». Размер «кучи» задается директивой {$M<размер памяти>}.

Доступ к статическим переменным осуществляется через их имена. 
Доступ к динамическим переменным осуществляется через указатель на место их расположения в памяти компьютера. [16]

Стоит отметить, что многие практические задачи сложно или невозможно решить без обращения к динамической памяти. Например, задача обработки массива большого объема (более 65536 байт).

В ЯП Pascal нет прямого доступа к динамическим объектам. Для доступа к динамическим объектам используют указатели, которые еще называют ссылками (ссылочными типами). [24]

Указатель – это переменная, содержащая адрес другой переменной (байта памяти). 

Указатель – это статическая переменная, которая предназначена для хранения адреса некоторой переменной, в том числе и адреса динамической переменной в «куче». В памяти компьютера любой указатель занимает фиксированный объем памяти, равный 4 байта (т.е. 2 слова):

  • 1-ое слово – сегмент памяти;
  • 2-ое слово – смещение.

Адрес – это номер 1-го байта поля памяти компьютера, в котором располагает

Значение указателя – это беззнаковое целое число, которое сообщает о том, где размещается переменная, но при этом ничего не говорит о самой переменной. [19]

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

Важно помнить, что ввод и вывод значения указателя выполнить не возможно.

Стоит отметить, что динамические величины не требуют описания в программе на ЯП Pascal, так как во время компиляции память под эти величины не выделяется. Однако во время компиляции программы память выделяется под статические величины, а указатели являются статическими величинами, следовательно, указатели требуют описания.

В ЯП Pascal имеется два вида указателей:

  • указатель на объект некоторого  типа (т.е. типизированный указатель; ссылаются на область данных определенного типа);
  • указатель, не связанный с типом (задает только место (адрес ОЗУ), без конкретизации типа значения, которое содержится по данному адресу). [19]

Ссылочный тип – это неограниченный набор переменных одного типа.

Переменные ссылочного типа в ЯП Pascal можно описать следующими способами:

  • в разделе описания типов;
  • в разделе описания переменных. [17]

Описание указателя выполняется в ЯП Pascal следующим образом:

<имя переменной-указатемя>: ^<базовый тип данных>;

Пример 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

117 124 82+92+102+…+1232 +1242

При i=125 происходит выход из цикла и переход к оператору writeln ('S=' ,S);

Эта, казалось бы, правильно написанная программа дает результат S=-12250. Как может получиться отрицательный результат при суммировании квадратов – заведомо положительных чисел? Такой результат мы получаем путем суммирования и занесения результатов в ячейку S типа integer, а в integer могут быть помещены числа в диапазоне -32768 +32767, поэтому после переполнения ячейки памяти в знаковый разряд переносится 1, которая интерпретируется машиной как '-'. Чтобы этого избежать, можно описать S как longint. Тогда результат работы программы будет правильный и равен 643110.

Program Prim12a; Var i,s:longint;

BEGIN s:=0;

for i:= 8 to 124 do

s:=s+sqr(i);

writeln('s=',s);

readln; END.

Можно также описать S как real. Program Prim12b; Var i:integer; s:real;

BEGIN s:=0;

for i:= 8 to 124 do

s:=s+sqr(i);

writeln('s=',s);

readln;

END.

Результат работы этой программы будет равен 6.4311000000E+05, что то же самое, что и 643110, если считать, что запись 6.4311000000E+05 равносильна математической записи 6,431110+5.

Правила применения оператора цикла for

1.Тело цикла - всегда один оператор. Если нужно, чтобы тело цикла состояло из нескольких операторов, то их объединяют в один с помощью операторных скобок begin, end.

for i:=l to n do

Begin S:=S+sin(x)/cos(x);

x=x+0.01;

writeln('S=' ,S)

end.

Операторы, взятые в операторные скобки begin, end, считаются одним составным оператором.

2. Начальное и конечное значения параметра цикла в общем случае являются выражениями. Тип этих выражений и тип параметра цикла должны совпадать. Здесь допускается применение любых типов, которые упорядочены с каким-либо фиксированным шагом, это может быть любой скалярный тип, кроме вещественного, т.е. байтовый, целый, символьный, перечисляемый, интервальный, булевский. на практике чаще всего применяют целочисленный тип.

3. В теле цикла параметр цикла не должен изменяться.

4. Нельзя с помощью оператора перехода goto войти в тело цикла, минуя заголовок.

5. Выйти из тела цикла в программу можно по if..goto, не дожидаясь полного перебора параметров цикла.

Пример: написать программу, выводящую буквы латинского алфавита в одну сторону, с пробелом между ними.

Применяем здесь в качестве параметра цикла символьную переменную, в качестве начального значения параметра – константу 'A', конечного – 'Z'.

Program Prim13;

Var

i:char;

BEGIN

for i:='a' to 'z' do

write(' ',i);

readln; END.

Пример: вычислить значение определенного интеграла на участке a,b для функции cos х, т.е. определить площадь, заштрихованную на рис. 4.1.

Х

b

а

Рис. 4.1

0

Y

Применим метод трапеций, суть которого заключается в том, что область делится на ряд трапеций, площади которых вычисляются и суммируются. Чем на большее количество трапеций мы разделим область, тем точнее получим результат (см.рис.4.2).

Х

b

а

Рис. 4.2

0

Y

Program Prim14;

Var i,n:integer; a,b,x,h,s:real;

BEGIN writeln(' введите к-во точек разбиения n и величины а, b');

readln(n,a,b);

h:=(b-a)/n; s:=0; x:=a;

for i:=1 to n do

Begin s:=s+(abs(cos(x))+abs(cos(x+h)))/2*h;

x:=x+h; end;

writeln('s=',s);

readln; END.

Замечание: при вычислении S (так как функция пересекает ось OX) часть площадей может иметь отрицательное значение, поэтому мы берем abs(f(x)).

Усложним задачу: пусть нужно найти площадь криволинейной трапеции (интеграл), где функция от точки а до b sin(x), а после b до с cos(x).

Program Prim15;

Var i,n:integer; a,b,c,x,h,s,fxpred,fxposl:real;

BEGIN writeln('введите к-во точек разбиения n и величины а, b, c');

readln(n,a,b,c);

h:=(c-a)/n; s:=0; x:=a;

for i:=1 to n do

Begin

{ определимся, по какой из функций считать стороны трапеций }

if (x>=a) and (x<=b) then fxpred:= abs(sin(x)) else

fxpred:=abs(cos(x));

if (x+h>=a) and (x+h<=b) then fxposl:= abs(sin(x+h)) else

fxposl:=abs(cos(x+h));

s:=s+(fxpred+fxposl)/2*h;

x:=x+h; end;

writeln('s=',s);

readln; END.

В последних двух программах мы заменяем площадь криволинейной трапеции (интеграл) суммой трапеций. При этом нам неизвестно, какую ошибку мы допустим; знаем, что чем больше точек разбиения, тем точнее будет результат. Поставим задачу: совершенно точно знать, что мы посчитаем интеграл с точностью не менее заданной, например, что разность между площадью криволинейной трапеции и суммой прямоугольных трапеций будет не больше Е = 0.01. Для этого нам нужно посчитать результат для количества точек, например 10, затем – 100. Сравнить величины площадей, и, если их разность будет меньше Е, то можно с уверенностью сказать, что площадь для разбиения на 100 интервалов будет найдена с точностью не менее Е. Если разность между расчетами на 10 и 100 интервалов больше Е, то следует разбить область интегрирования на 1000 интервалов и сравнить сумму для 100 интервалов с суммой для 1000 интервалов, и если их разница будет меньше Е, то за результат принять площадь, посчитанную для 1000 разбиений и т.д. рано или поздно мы получим искомый результат. Сравнения сумм площадей трапеций будем производить с помощью оператора if.

Пусть функция будет cos(x). Напишем программу:

Program Prim16;

Label NAH,KON;

Var i,n:integer; a,b,x,h,spred,spos,e:real;

BEGIN writeln('введите точность е и границы a, b');

readln(e,a,b); spred:=9.9e+10; h:=0; n:=10;

NAH: spos:=0; h:=(b-a)/n; x:=a;

for i:=1 to n do

Begin spos:=spos+(abs(cos(x))+abs(cos(x+h)))/2*h;

x:=x+h; end;

if abs(spos-spred)<=e then Begin

writeln('s=',spos,' n=',n); goto KON; end

else spred:=spos; n:=n*10; x:=a; goto nah;

KON: readln; END.

Работа программы: for i: = i to n do, в первый раз оператор

Spоs: = spos+(abs(cos(x))+abs(cos(x+h)))/2h

посчитает значение интеграла для 10 точек разбиения. Затем if сравнит полученное значение spos с 9.9Е+10. Конечно, разница между этими величинами не будет меньше е, тогда мы забудем число 9.9Е+10, так как присвоим spred: = spоs, чтобы при следующем выполнении if сравнивать предыдущее и последующее значения интеграла. После этого увеличим количество точек разбиения n: = n10 и вернемся на начало вычисления spos, т.е. интеграла для 100 интервалов goto nach. После нового вычисления spos сравним 100 интервалов для 10 интервалов spred. Если разность между ними не меньше Е, то забудем значение s для 10 интервалов и занесем в spred значение для 100 интервалов. Затем вычислим значение интеграла для разбиения 1000 и результат занесем в spos, сравним spred и spos и т.д.

4.2. Оператор repeat. Общий вид

В отличие от for, оператор repeat (а также while) применяют тогда, когда неизвестно точно, сколько раз будет выполняться тело цикла. Общий вид оператора

repeat

оператор;

оператор;

– – – – – – – –

оператор until булевское выражение;

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

Явного перебора параметров в repeat не предусмотрено, поэтому изменение параметров осуществляется в теле цикла. Здесь же мы должны позаботиться , чтобы рано или поздно булевское выражение стало истиной.

Замечания: 1.Тело оператора repeat выполняется по меньшей мере один раз. Оператор repeat еще называют циклом с послеусловием.

2. Перед until ';' не ставится.

Пример: найти сумму четных чисел в интервале от 0 до 100.

Program Prim17;

Var i,s:integer;

BEGIN i:=0; s:=0;

repeat

i:=i+2; s:=s+i

until i>100;

writeln('s=',s);

readln; END.

Пример: найти ток в цепи переменного тока в зависимости от частоты тока. Известно, что при последовательном соединении активной, емкостной и индукционной нагрузок сила тока может быть вычислена по формуле

Пусть U = 220, R = 100, L = 0.57, С = 3.210-3, Fначальное=10; F будем менять с шагом 0.5. Печатать все результаты. Результаты, когда i3, нас не интересуют.

Program Prim18;

Var i,r,f,l,c,u:real;

BEGIN writeln(' введите значения u r l c');

readln(u,r,l,c);

f:=50;

repeat i:=u/sqrt(sqr(r)+sqr(2*pi*f*l-1/(2*pi*f*c)));

f:=f-1;

writeln('f=',f,' i=',i)

until i>3;

readln; END.

4.3. Оператор While

Оператор While цикла с предусловием.

While – булевское выражение; do – тело цикла.

Работа оператора. Вначале вычисляется булевское выражение, и если оно истинно, то выполняется тело цикла; если оно ложно, то происходит выход из цикла.

Тело цикла – один оператор, как правило, составной. Мы должны позаботиться о том, чтобы булевское выражение в теле цикла на каком-то этапе стало ложным, иначе цикл никогда не закончится. Цикл можно сделать умышленно бесконечным, написав: while true do оператор;

Мы будем применять эту конфигурацию при написании программ обработки файлов.

Пример: найти сумму четных чисел в интервале от 0 до 100.

Program Prim19;

Var i,s:integer;

BEGIN i:=0; s:=0;

while i<100 do

Begin i:=i+2; s:=s+i; end;

writeln('s=',s);

readln; END.

Пример: в цепи переменного тока найти ток в зависимости от его частоты.

Program Prim20;

Var i,r,f,l,c,u:real;

BEGIN writeln('введите значения u,r,l,c');

readln(u,r,l,c);

f:=50;

while i<3 do

Begin i:=u/sqrt(sqr(r)+sqr(2*pi*f*l-1/(2*pi*f*c)));

f:=f-1;

writeln('f=',f,' i=',i); end;

writeln('i>3, i=',i);

readln; END.

4.4. Вложенные циклы

Поскольку тело любого цикла состоит из операторов или составного оператора, то ничто не помешает в теле цикла располагать другие операторы цикла. Рассмотрим работу программы:

Program Prim21;

Var

i,j:integer;

BEGIN

for i:=1 to 5 do

Begin writeln;

for j:=20 to 23 do

write('i=',i,' j=',j);

end;

readln; END.

Для цикла for i:=1 to 5 do телом цикла является

begin for j:=20 to 23 do

write(' i= ', i, ' , j = ', j);

writeln;

end;

Назовем этот цикл внешним. Кроме того, имеется внутренний цикл

for j: = 20 to 23 do с телом write (' i = ', i , j =', j);

Работа программы. Вначале машина встречает внешний цикл и начинает его выполнять: присваивает i=l, затем переходит к его телу, а здесь встречает внутренний цикл и присваивает j значение 20, после чего выполняет тело внутреннего цикла, т.е. выводит на экран i=l, j=20. Так как внутренний цикл еще не окончен, то машина продолжает его выполнять, т.е. присваивает j значение 21 и добавляет к уже выведенной строке i=l, j=21.

Заметим, что оператор write отличается от оператора writeln тем, что он не начинает вывод с новой строки, а продолжает писать в той же строке, т.е. после второго выполнения внутреннего цикла на экране появится

i= 1, j=20 i= 1, j=21.

Машина продолжит выполнение внутреннего цикла, и, когда он закончится (выполнится для j = 20.21.22.23), на экране будет строка

i = 1 j = 20 i =l j = 21 i = 1 j = 22 i = 1 j = 23.

Внутренний цикл закончится, однако тело внешнего цикла еще не закончилось, поэтому выполняется оператор writeln, который переводит курсор на новую строку. После этого тело внешнего цикла закончится, но сам цикл отработал только для i = 1. Поэтому внешний цикл продолжит работу, присвоив i: =2 и вновь начав выполнение своего тела. Встретив внутренний цикл j:=1, на экран с новой строки выведется: i=2, j=20, затем j:=2 и к этой строке добавится i=2, j=21 и т.д., пока не закончится внутренний цикл.

Таким образом, внешний цикл, изменяя i от 1 до 5, заставит каждый раз выполняться полностью внутренний цикл, и в результате работы программы на экране появится:

i=l, j=20 i=1, j=21 i=1, j=22 i=1, j=23

i=2, j=20 i=2, j=21 i=2, j=22 i=2, j=23

i=3, j=20 i=3, j=21 i=3, j=22 i=3, j=23

i=4, j=20 i=4, j=21 i=4, j=22 i=4, j=23

i=5, j=20 i=5, j=21 i=5, j=22 i=5, j=23

Вкладывать циклы друг в друга можно любое количество раз, необходимо лишь помнить, что количество выполнений самого внутреннего тела цикла при этом будет расти в геометрической прогрессии. Так, например,

for i:=l to 100 do

for j:=l to 100 do

for k:=l to 100 do

writeln (i, j, k);

дает столбик цифр:

111

112

113

114

– – – – –

121

122

123

124

– – – – –

211

212

213

– – – – –

100100100,

что составляет 1000000 строчек.

Пример: найти rez = f (х1) + f (х2 )+.. .+f (x6),

где

f(x) считать с точностью не менее e=0.01;

х1=0.1, затем xn=xn-l+0.2, пока х не станет равным 1.1.

Нам нужно вычислить rez. Поскольку это будет сумма, то вначале положим rez=0, затем вычислим f(х1) с нужной точностью и добавим к rez, и т.д., то есть:

Rez : = 0 ; x:=0.l;

for i:=l to 6 do

Begin Rez:=Rez+f(x);

x:=x+0.2;

end;

Теперь в данную программу нужно "вставить" вычисленные f(x) с необходимой точностью. Для этого найдем сумму, составляющую f(x) для n элементов, затем – для n+l элемента, сравним их по модулю и, когда разность между ними будет меньше Е, сумму для n+l элемента ряда примем за более точную и добавим к Rez. При вычислении f(x) количество членов ряда, которые нам придется суммировать, не известны, поэтому придется применять цикл типа repeat или while. Кроме того, в элементы ряда входит 2n! Факториал подсчитывается по схеме 1! = 1, 21 = 12; з! =123 и т.д., т.е. например, 8! = 12345678. Далее, при подсчете следующего значения ряда, можно величину 2n! найти по схеме: fak: = fak(n-l)n. Ряд у нас знакопеременный, т.е. меняется '+', '–' перед каждым членом ряда. Меняем знак с помощью схемы znak: = znak(-1). Если исходный знак = +l, то в ячейке znak будет храниться то +l, то -1.

С учетом сказанного выше f(x) с нужной точностью вычисляется:

fxpred: = 0; fxpos: =l, n: = 2; znak: = 1; fak: = 1;

while Abs(fxpos – fxpred) > = 0.01 do

Begin fxpred: = fxpos: znak: = znak(-1); fak: = fak(n-l)n;

fxpos: = fxpos+znakexp(nin (x))/fak;

n: = n+2;

end;

Соединив оба фрагмента программы и снабдив их описаниями, получим готовую программу:

Program Prim22;

Var n,fak,i,znak:longint;

rez,x,fxpred,fxpos:real;

BEGIN rez:=0; x:=0.1; writeln;

for i:= 1 to 6 do

Begin fxpred:=0; fxpos:=1; n:=2; znak:=1; fak:=1;

while abs(fxpos-fxpred)>=0.001 do

Begin znak:=znak*(-1); fak:=fak*(n-1)*n;

fxpred:=fxpos;

fxpos:=fxpos+znak*exp(n*ln(x))/fak; n:=n+2;

end; writeln('x=',x*180/pi,'(ја ¤) cos(x)=',fxpos);

x:=x+0.2; rez:=rez+fxpos; end;

writeln('rez=',rez);

readln; END.

Заключение

В курсовой работе рассмотрена тема «Создание ссылочных типов в языке программирования Паскаль». Информация по теме курсовой работы представлена в виде трех глав, которые в совокупности полностью раскрывают тему работы.

Первые две главы носят теоретический характер, раскрывают основные рассматриваемые понятия. Третья глава носит практический характер и представляет собой примеры решения конкретных задач в ЯП Pascal (текст программы, результат выполнения программы) с использованием ссылочных типов данных, выделением для их размещения памяти, а также ее освобождения при отсутствии потребности в данных переменных.

В ходе курсовой работы изучены ссылочные типы данных в ЯП Pascal и их использование при решении конкретных задач.

В ходе курсовой работы выполнены следующие задачи:

  • ознакомление с ЯП Pascal;
  • рассмотрение основных типов данных ЯП Pascal;
  • приведены понятия «ссылочный тип данных», «указатель» в ЯП Pascal;
  • рассмотрены основные процедуры и функции, используемые для создания ссылочных типов в ЯП Pascal;
  • приведены примеры программ с использованием ссылочных типов данных на ЯП Pascal.

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

Список используемых источников

  1. Боженова М.М., Москвина Л.А. Практическое программирование. Приемы создания программ на языке Паскаль. – М.: Высшая школа, 2017. – 367с.
  2. Демидов Д.В. Основы программирования в примерах на языке Паскаль. – М.: МИФИ, 2016. – 402с.
  3. Долинский М.С. Алгоритмизация и программирование на Turbo Pascal: от простых до олимпиадных задач. Учебное пособие. – СПб.: Питер, 2018. – 311с.
  4. Культин Н. Turbo Pascal в задачах и примерах. – СПб.: БХВ-Петербург, 2016. – 256с.
  5. Меженный О.А. Turbo Pascal. Самоучитель. – СПб.: Вильямс, 2015. – 336с.
  6. Немнюгин С.А. Turbo Pascal. Учебник. – СПб.: Питер, 2017. – 496с.
  7. Павловская Т.А. Паскаль. Программирование на языке высокого уровня. – СПб.: Питер, 2017. – 320с.
  8. Попов В. Паскаль и Дельфи. Самоучитель. – СПб.: Питер, 2018. – 190с.
  9. Рапаков Г.Г., Ржецкая С.Ю. Программирование на языке Pascal. – СПб.: БХВ-Петербург, 2017. – 249с.
  10. Семакин И.Г., Шестаков А.П. Основы программирования. – М.: Высшая школа, 2018. – 293с.
  11. Фаронов В.В. Turbo Pascal. – СПб.: БХВ-Петербург, 2016. – 497с.
  12. Фаронов В.В. Turbo Pascal 7.0. Начальный курс. Учебное пособие. – СПб.: Нолидж, 2016. – 576с.
  13. Фаронов В.В. Turbo Pascal 7.0. Практика программирования. Учебное пособие. – СПб.: Нолидж, 2017. – 452с.
  14. Федоренко Ю. Алгоритмы и программы на Turbo Pascal. Учебный курс. – СПб.: Питер, 2017. – 240с.
  15. Шелест В. Программирование. – СПб.: БХВ-Петербург, 2018. – 378с.
  16. Шпак Ю.А. – Turbo Pascal 7.0 на примерах. – СПб.: Питер, 2017. – 496с.
  17. Динамические списки. [Электронный ресурс]. URL: http://www.studfiles.ru/preview/5357642/page:14/ (Дата обращения 10.03.2020г.)
  18. Свободная энциклопедия ВикипедиЯ. [Электронный ресурс]. UPL: http://ru.wikipedia.org/wiki/ (Дата обращения 12.03.2020г.)
  19. Указатели в Паскале. Динамическая память на языке Паскаль. [Электронный ресурс]. URL: http://life-prog.ru/view_algoritmleng.php?id=122 (Дата обращения 13.03.2020г.)
  20. Учебник по Паскалю. [Электронный ресурс]. URL: http://www.studfiles.ru/preview/1825376/ (Дата обращения 05.03.2020г.)