- •Історична довідка
- •Характеристика й особливості мови
- •Алфавіт мови
- •Службові (зарезервовані) слова.
- •Структура програми мови Turbo Pascal
- •Розділ оголошень і угод
- •Розділ текстів процедур і функцій
- •Розділ основного блоку програми
- •Процедури введення-виведення. Деякі вбудовані функції Турбо-Паскаля.
- •Функції числових параметрів.
- •Базові управляючі конструкції Турбо-Паскаля Оператори умовного переходу.
- •1. Оператор if.
- •2. Оператор вибору (case)
- •Оператори циклів у Паскалі
- •1. Цикл із післяумовою (Repeat)
- •2. Цикл із предумовою (While)
- •3. Цикл із лічильником або параметром (For)
- •Концепція типів даних. Типи даних в мові Pascal
- •Дійсні типи
- •Бульовий (логічний) тип
- •Символьні і рядкові змінні
- •1. Символьний тип
- •2. Рядковий тип
- •Перерахований та обмежений типи
- •1. Перерахований тип
- •2. Обмежений тип
- •1. Поняття масиву. Одномірні масиви
- •2. Багатомірні масиви
- •3. Сортування і пошук
- •Множинний тип
- •Тип запис
- •Процедури і функції
- •Формальні і фактичні параметри. Механізм параметрів
- •Параметри - значення
- •Параметри-змінні
- •Безтипові параметри
- •Приведення типів.
- •Процедурні типи
- •Рекурсія Рекурсивні визначення
- •Рекурсивні підпрограми
- •Алгоритми з поверненням. Розв’язок задачі про рух коня
- •Алгоритми з поверненням. Розв’язок задачі про вісьмох ферзів
- •If підходить тнеn
- •Модулі в Турбо Паскалі
- •Модуль crt
- •1. Керування екраном
- •2. Робота з клавіатурою
- •3. Інші можливості
- •Графіка в Турбо Паскалі
- •1. Включення і вимикання графічного режиму.
- •2. Побудова елементарних зображень
- •3. Виведення текстової інформації.
- •Файли в мові програмування Pascal
- •Установчі і завершальні операції
- •Операції введення-виведення
- •Обробка помилок введення-виведення
- •Переміщення по файлу
- •Спеціальні операції
- •Текстові файли
- •1. Оголошення файлової змінної і прив'язка до файлу на диску
- •2. Читання даних з файлу
- •3. Запис даних у файл
- •Двійкові файли
- •1. Типізовані файли
- •2. Нетипізовані файли
- •Статичні і динамічні змінні
- •Покажчики
- •Стан покажчика
- •Установка розмірів динамічної пам'яті
- •Сумісність і перетворення посилкових типів
- •Динамічні структури даних
- •Динамічні змінні: інші види списків, стек і черга.
- •1. Інші види списків
- •2. Стек і черга
- •Дерева і пошук у деревах
- •1. Визначення й описи структур даних
- •1. Масив
- •2. Список
- •3. Дерево
- •2. Алгоритми
- •1. Лінійний пошук у масиві
- •2. Двійковий пошук
- •3. Лінійний пошук у списку
- •Змішані таблиці
- •Об’єктно-орієнтоване програмування. Що таке об’єктно-орієнтоване програмування
- •Інкапсуляція
- •Спадкування
- •Віртуальні методи і поліморфізм
- •Конструктори, динамічні об'єкти і деструктори
- •Поля і методи: сховані і загальнодоступні
- •Системно- залежні розширення
- •Налагодження змінних
- •Оверлеї
- •Переривання і системні виклики
- •Доступ до пам'яті і портів
- •Перевизначення переривань
Процедурні типи
Досі ми розглядали процедури і функції просто як текстові фрагменти програми, що позначені іменами. Таке положення підпрограм істотно відрізняє їх, наприклад, від змінних, котрі в різні моменти виконання можуть приймати різні значення. Однак, Turbo Pascal дозволяє вводити змінні спеціального виду, значеннями яких можуть служити підпрограми. Іншими словами. Turbo Pascal дозволяє інтерпретувати процедури і функції як значення, які можна присвоювати змінним і передавати як параметри. Підкреслимо, що мова тут йде про підпрограми як цілісні об'єкти, а не про значення, що виникають у результаті їхнього виконання.
Найпростіший випадок опису змінної, значеннями якої можуть бути процедури, приведений нижче.
var
Р: procedure;
Цей опис вводить змінну Р, можливими значеннями якої можуть бути будь-які процедури без параметрів. У більш загальному випадку процедурний тип задається конструкцією, дуже схожої на заголовок процедури чи функції, наприклад:
type
Func = function(х,у:integer):integer;
var
Fl, F2 : Func;
Змінним Fl і F2 можуть бути присвоєні як значення функції від двох цілих параметрів, що повертають цілий результат. Наприклад, якщо в цій же програмі мається опис функції
function Add( a,b:integer ) : integer;
begin
Add := a+b
end;
те припустиме присвоювання виду Fl := Add;
Зверніть увагу, що в останньому операторі змінній Fl як значення присвоюється ФУНКЦІЯ Fl; у даному випадку виконання цієї функції не відбувається. Після такого присвоювання мається можливість викликати функцію Add як безпосередньо по її імені, так і за допомогою вказівки змінної F 1. Так, наприклад виконання операторів
WriteLn(Add(l,2))
WriteLn(Fl(l,2))
надрукують число 3.
Отже, визначення процедурного типу аналогічно заголовку підпрограми з тією лише різницею, що ім'я підпрограми не задається.
Приклади:
type
Proc = procedure;
BinOperation = function (х, у: real) :real;
OnOperation = function(х:real):real;
Reading = procedure(var F:text;var Elem:char);
Імена формальних параметрів, що вказуються в процедурних типах, грають чисто ілюстративну роль і на зміст визначень ніякого впливу не роблять. Необхідними є тільки ідентифікатори типів параметрів і результатів (для функцій).
Таким чином. Turbo Pascal дозволяє визначати змінні, значеннями яких можуть бути процедури і функції. Для таких змінних припустимі оператори присвоювання, у правих частинах яких знаходяться ідентифікатори інших процедурних змінних чи ідентифікатори підпрограм. Змінна процедурного типу в різні моменти виконання програми може мати як значення різні підпрограми. Такі змінні можна, зокрема, використовувати для виклику підпрограм, що присвоєні цим змінної. Наприклад, якщо в програмі маються наступні описи:
var
Operation : function (х, у : real) :real;
function Add ( a,b:real ) : real;
begin
Add := a+b
end;
function Sub ( a,b:real ) : real;
begin
Sub := a-b
end;
то виконання послідовності операторів
if Condition then
Operation := Add
elseOperation := Sub;
Write(Operation(2.05,3+X))
приведе до присвоєння змінної Operation, у залежності від істинності умови, або функції Add, або функції Sub. Конструкція Operation (2.05, 3+X) викликає активізацію тієї функції, що була присвоєна змінній Operation. Таким чином, у викликах функцій і в операторах процедури, крім ідентифікаторів відповідних підпрограм, можуть стояти імена змінних процедурних типів. Виконання таких конструкцій буде полягати у виклику підпрограм, що були присвоєні цим змінним.
Введення процедурних типів вимагає розв’язку проблеми сумісності, зокрема, сумісності по присвоюванню. У даному випадку питання вирішується досить просто. Процедурні типи, щоб вони були сумісними по присвоюванню, повинні мати однакову кількість формальних параметрів, а параметри на відповідних позиціях повинні бути одного типу. Імена параметрів, як говорилося вище, ніякого значення не мають. Аналогічним чином повинні збігатися типи значень, що повертаються, у випадку функцій.
Використання процедурних типів не обмежується простими процедурними змінними. Як і будь-який інший тип, процедурні типи можуть брати участь у побудові складених типів, наприклад, комбінованих чи регулярних:
type
Proc = procedure ( Т:real );
NoticePtr = ^Notice;
Notice = record
Next : NoticePtr;
Time : real;
Action : Proc
end;
var
NewNotices : array[1..10] of Proc;
Notices : NoticePtr;
з урахуванням цих описів припустимі наступні оператори процедур:
MewNotices[7]((Х+17.5) *2) ;
Notices^.Action(0.25) ;
Механізм процедурних типів надає програмісту дуже широкі і зручні можливості при розробці програм. Однак для коректної роботи необхідно дотримуватись наступних правил, які стосуються процедур і функцій, що присвоюються змінним процедурних типів:
1. Підпрограма, що присвоюється процедурній змінний, повинна бути відтрансльована в режимі "далекого типу викликів". Не поглиблюючи в подробиці системного характеру, досить пояснити, що необхідного ефекту можна досягти, розташувавши перед чи підпрограмою групою підпрограм директиву компілятора $F зі знаком '+' (плюс), а наприкінці цієї групи - таку ж директиву зі знаком '-' (мінус), наприклад:
{$?+} function Add ( a,b:real ) : real;
begin
Add := a+b
end;
function Sub ( a,b:real ) : real;
begin
Sub := a-b
end;
{$F-}
Якщо необхідно поширити дію цієї директиви на всю програму, досить помістити одну директиву {$F+} на самому початку тексту.
Еквівалентом директиви компілятора {$F+} є , службове слово far, що, будучи записаним перед блоком підпрограми, задає для неї далекий тип виклику. (Інший можливий тип виклику - "ближній" - задається службовим словом near у тім же місці опису підпрограми). Таким чином, перша процедура з попереднього приклада може бути записана в наступному виді:
function Add ( a,b:real ) : real; far;
begin
Add := a+b
end;
2. Підпрограма, що присвоюється процедурної змінний, не повинна бути стандартною чи процедурою функцією. Це обмеження при необхідності легко обійти, обеживши виклик стандартної підпрограми "оболонкою", після чого її можна використовувати в присвоюванні, наприклад:
var
Func : function ( S:string ) : byte;
function MyLength ( S:string ) : byte; far;
begin .
MyLength := Length(S)
end;
Func := MyLength;
3. Важливим обмеженням є те, що обговорювані підпрограми НЕ МОЖУТЬ БУТИ вкладеними в інші підпрограми.
4. Нарешті, підпрограми, що присвоюються процедурним змінним, не можуть бути підпрограмами спеціального виду, що містять специфікацію interrupt і конструкцію inline.
Зазначені обмеження можуть стати трохи більш зрозумілими, якщо мати на увазі, що на фізичному рівні змінна процедурного типу містить повну адресу початку коду підпрограми в пам'яті (сегмент і зсув). Фактично, процедурна змінна дуже нагадує змінну посилкового типу, тільки замість покажчика на данні вона містить покажчик на код підпрограми. Процедурна змінна завжди займає в пам'яті 4 байти (два слова), причому в першому слові зберігається зсув, у другому - сегмент.
Оскільки процедурні типи можна використовувати в будь-якому контексті, то можна описувати процедури і функції, параметрами яких є процедурні змінні. Таким чином, можна організувати підпрограму, що буде виконувати деяку загальну дію для різних підпрограм-параметрів. Нижче приводиться приклад такої процедури.
program Tables;
type
Func = function (x,y :integer) : integer;
function Add ( a,b:integer ) : integer; far;
begin
Add := a+b
end;
function Mult ( a,b:integer ) : integer; far;
begin
Mult := a*b
end;
procedure MakeTable ( W,H:integer; Operation:Func );
var
i, j : integer;
begin
{ Формуємо заголовок }
Write (' ':6);
for i:=1 to W do Write(i:5);
WriteLn;
Write(' ':6) ;
for i:=1 to W do Write('-----');
WriteLn;
{ Проводимо обчислення }
for i:=1 to H do
begin
Write(i:5,'|');
for j:=1 to W do Write (Operation(j,i):5) ;
WriteLn
end;
WriteLn
end;
begin { MakeTable }
MakeTable(10,10,Add);
MakeTable(10,10,Mult)
end.
Тіло програми Tables містить два виклики процедури MakeTable, що будує таблиці, використовуючи для обчислень у першому випадку функцію Add (одержуючи таблицю додавання 10х10), а в другому випадку - функцію Mult (одержуючи таблицю множення 10х10).
Необхідно нагадати, що хоча змінні процедурних типів можна передавати як параметри підпрограмам, функції, що повертають значення процедурних типів, НЕ ДОПУСКАЮТЬСЯ.
У загальному випадку використання процедурної змінної в операторі чи виразі означає виклик присвоєної даної змінної процедури чи функції. Однак, тут мається одне виключення: якщо в лівій частині оператора присвоювання стоїть процедурна змінна, права його частина повинна представляти ідентифікатор іншої процедурної змінної чи ідентифікатор підпрограми. Розглянемо, наприклад, таку програму:
type
IntFunc = function : integer;
var
F : IntFunc;
N : integer;
function Readint : integer; far;
var
i : integer;
begin
Read(i);
Readint := i
end;
begin
F := Readint; { приссвоїти значення процедури )
N := Readint; { присвоїти результат функції }
end.
Перший оператор в основній програмі присвоює значення процедури Readint (її адресу) процедурної змінний F, другий оператор ВИКЛИКАЄ функцію Readint і присвоює отримане значення змінній N. Одержання значення процедури чи виклик функції розрізняються по типу присвоюванної змінної (F чи N).
На жаль, виникають ситуації, коли компілятор з контексту не може визначити бажану дію. Наприклад, у наступному операторі для компілятора неочевидно, повинний він порівняти значення процедури в F зі значенням процедури Readint, чи потрібно викликати процедури F і Readint, а потім порівняти їх значення:
if F = Readint then Writeln('Рівні');
У подібних випадках вважається, що таке входження ідентифікатора чи процедури функції означає виклик функції, тому результатом даного оператора буде виклик F і Readint і порівняння отриманих результатів. Щоб порівняти значення змінної F зі значенням (адресою) процедури Readint, потрібно використовувати наступну конструкцію:
if @F = @ReadInt then Writein('Рівні');
У випадку застосування до ідентифікатора процедури чи функції операції взяття адреси @ запобігає виклику компілятором процедури і у той же час перетворює аргумент у покажчик. Таким чином, @F перетворить F у нетипізований покажчик (pointer), що містить адресу, а @ReadInt повертає адресу Readint. Два значення-покажчики можна порівняти і визначити, чи посилається в даний момент F на Readint.
Зверніть увагу: щоб одержати адресу самої процедурної змінний, а не адресу підпрограми, що у ній збережена, потрібно двічі використовувати операцію узяття адреси: @@. Наприклад, @Р означає перетворення Р в нетипізований покажчик, а @@Р означає повернення фізичної адреси процедурної змінної Р.
Turbo Pascal цілком підтримує приведення типів змінних для процедурних типів. Наприклад, з урахуванням наступних описів:
type
Func = function ( X:integer) : integer
function MyFunc ( X:integer ) : integer;
begin
MyFunc := X*X
end;
var
F:func
p:pointer
n:integer
можна побудувати наступні присвоювання:
{ змінній F присвоюється функція MyFunc }
F := MyFunc;
( функція MyFunc викликається через змінну F }
N := F(N);
{ Р одержує покажчик на функцію MyFunc }
Р := @F;
{ функція MyFunc викликається через покажчик Р }
N := Func(P)(N);
{ присвоїти значення процедури в Р змінній F }
F := Func(P);
{ присвоїти значення процедури в F покажчику Р }
Func(P) := F;
{ присвоїти значення покажчика в Р змінної F }
@F := Р;
Зауважимо, зокрема, що оператор одержання адреси @, застосований до процедурної змінної, можна використовувати в лівій частині присвоювання. Зверніть також увагу на приведення типів у четвертому операторі при виклику функції через змінну-покажчик.