Любая подпрограмма перед использованием должна быть описана. Из В можно вызвать А, но не наоборот (использовать опережающее описание). Подпрограмме доступны только те объекты верхнего уровня, которые описаны до объявления переменных данной программы. Эти объекты являются глобальными по отношению к программе.
В Турбо Паскале допускается произвольная последовательность описания констант, переменных, типов, меток и подпрограмм. Раздел описания переменных varможет появляться много раз. Безразличен порядок следования и количество разделов var, const, type, label.
НО При определении области действия этих описаний следует помнить, что имена, описанные ниже по тексту программ, недоступны из ранее описанных подпрограмм.
Пример
var v1: ...;
procedure S;
var v2: ...;
…………..
end {S};
var v3: ...;
…………..
Из процедуры S можно обратиться к переменным v1 и v2, но нельзя использовать v3, т.к. v3 следует в программе за описанием процедуры S.
Имена, локализованные в подпрограмме, могут совпадать с ранее объявленными глобальными переменными. Считается, что локальное имя «закрывает» глобальное, делая его недоступным.
Пример
var
i: integer;
procedure P;
var
i: integer;
begin
writeln(i);
end {P};
begin
i:=1;
P;
end.
Программа печатает всё, что угодно, так как i – не определено. Если описание var i: integer убрать, то будет напечатано значение глобальной переменной 1.
Области действия идентификаторов
program ShowScope;
область действия x, y, z, Proc1, Proc2
| |
var x, y, z: real;
procedure Proc1;
(m, n, me: real);
область действия
m, n, me, x, you
| | var x, you: real;
begin {Proc1}
…………
end; {Proc1}
procedure Proc2
(var s, p, x: real);
область действия
s, p, x, Blank, hi, bye
| | const Blank = ' ';
var hi, bye: string;
begin {Proc2}
…………
end; {Proc2}
begin {ShowScope}
………………
end. {ShowScope}
|
Допустимые вызовы процедуры
Программа или процедура,
откуда производится вызов
| Процедуры, которые
могут быть вызваны
|
ShowScope
Proc1
Proc 2
| Proc1, Proc2
Proc1
Proc 1, Proc 2
|
Вложенные процедуры
program Nested;
область действия
m, n, outer
| | var m, n: real;
procedure outer
область действия
x, y, p, q inner
| | (var x; y: real);
var p, q: real;
procedure inner;
var p, t: real;
begin {inner}
……………
end; {inner}
begin {outer}
……………
end; {outer}
begin {Nested}
……………
end. {Nested}
Допустимые обращения к идентификаторам
| |
Тело программы
или процедуры
| Идентификаторы,
к которым возможны обращения
|
Nested
| m, n (глобальные переменные)
оuter (глобальная процедура)
|
Outer
| m, n (глобальные переменные)
x, y (параметры)
p,q (локальные переменные)
оuter (глобальная процедура)
inner (локальная процедура)
|
Inner
| m, n (глобальные переменные)
x, y (параметры outer )
Q (переменная, объявленная в outer)
p,t (локальные переменные)
оuter (глобальная процедура)
inner (процедура, объявленная в outer)
|
| | |
Совместимость и преобразование типов данных
Turbo Pascal – типизированный язык. Концепция типов предполагает, что применяемые в языке операции определены только над операндами совместимых типов.
Совместимость типов учитывается при согласовании формальных и фактических параметров подпрограмм и выполнении операций присваивания.
Несовместимость типов определяется на этапе компиляции. Если типы операндов выражения или формальных и фактических параметров подпрограмм неодинаковы, но совместимы, то производится преобразование типов для приведения их к одному и тому же допустимому типу.
Принадлежат ли переменные одному типу данных, зависит от того, как в языке трактуется эквивалентность. Она может быть структурная и именная.
При первой анализируется структура переменных: число их компонентов и их типы.
Пример
type
t1=array [1..10] of integer;
t2=array [1..10] of integer;
var
v1,v2: t1;
v3: t2;
При структурной эквивалентности Т1 и Т2 – один тип.
В языке Pascal применяется именная эквивалентность. Типы переменных совпадают тогда и только тогда, когда при их описании используется один и тот же идентификатор типа. Т1 и Т2 – 2 разных типа!
Типы Т1 и Т2 идентичны (одинаковы), если
- Т1 и Т2 – один и тот же тип;
- определены один через другой, т.е.
type Т1=Т2;
type Т2=Т1;
При вычислении выражений и при выполнении операции присваивания операнды должны быть совместимы.
Во время вычисления выражений два типа операндов совместимы, если:
1) оба они одного типа;
2) один – вещественный, другой – целый;
3) один является диапазоном типа второго операнда;
4) оба – диапазоны одного и того же базового типа;
5) оба – множества, составленные из элементов одного и того же базового типа данных;
6) оба – строки;
7) один тип – строка, другой – символ.
Пусть Т1 – тип переменной левой части присваивания, Т2 – тип результата выражения его правой части.
Присваивание возможно, если:
1) Т1 и Т2 – один тип;
2) Т1 и Т2 – совместимые порядковые типы и значение Т2 лежит в диапазоне возможных значений Т1;
3) Т1 и Т2 – вещественные типы и значение Т2 лежит в диапазоне возможных значений Т1;
4) Т1 – вещественный тип, Т2 – целый;
5) Т1 – строка, Т2 – строка или символ;
6) Т1 и Т2 – совместимые множества и все элементы Т2 принадлежат множеству возможных значений Т1;
Преобразование типов может быть явным и неявным.
При явном преобразовании типов используют вызовы специальных функций, аргументы которых принадлежат одному типу, результат – другому.
Пример
Ord, Odd, Trunc, Round, Chr.
Данные одного типа могут автоматически (неявно) преобразовываться в данные другого типа перед выполнением операций в выражениях и через выполнение операций присваивания. Неявное преобразование типов возможно в трех случаях:
а) в выражениях из вещественных и целочисленных данных целые автоматически преобразуются в вещественные, результат будет вещественного типа;
б) при выполнении операции присваивания переменной вещественного типа значения целого типа значение целого типа преобразуется в значение вещественного типа, после чего оно присваивается переменной левой части оператора присваивания;
в) одна и та же область памяти попеременно содержит данные то одного, то другого типа (совмещение в памяти данных разных типов). Например, при использовании записей с вариантными полями.
Явное преобразование может быть с помощью стандартных функций и с помощью операции приведения типа.
Форма операции
<имя типа>( <имя переменной> / <выражение>);
Операция приведения может находиться, как в левой части оператора присваивания, так и в правой.
Если она находится в левой части оператора присваивания, то в скобках может быть только имя переменной. Если в правой, то и выражение.
Пример Явное преобразование символов и чисел.
uses crt;
var
si:0..255;
a:byte;
c:char;
begin
clcscr;
char(si):=’a’; {Приведение типа в левой части: из чего char
преобразуется для si!!}
writeln(char(si)+’s’); {as}
writeln(si); {97}
si:=ord(‘C’); {Приведение типа в правой части: во что char
преобразуется !!}
writeln(si); {67}
si:=byte (‘E’); {Приведение типа в правой части: во что char
преобразуется !!}
writeln(si); {69}
a:=byte(‘E’);
writeln(a); {69}
char(a):=’B’;
writeln(char(a)); {B}
writeln(a); {66}
byte(c):=si; {Приведение типа в левой части: из чего si
преобразуется для char!!}
writeln(byte(c)+200); {269}
writeln(c); {E}
byte(c):=a;
writeln(c); {B}
end.
Пример Явное преобразование целых и перечислимых типов.
type
Days=(mon, tues, wen, thurs, fri, sat, sun);
var
aDay:Days;
num:byte;
begin
clrscr;
Days(Num):=sun; {Приведение типа в левой части: из чего Days
преобразуется для byte !!}
writeln(ord(Days(num)),ord(sun),num); {666}
aDay:=Days(num); {Приведение типа в правой части: во что byte
преобразуется !!}
writeln(ord(Days(num)),ord(aDay),num); {666}
num:=byte(fri);
writeln(ord(fri), num); {44}
num:=ord(fri);
writeln(Ord(fri), num); {44}
aday:=Days(num+1);
writeln(ord(Days(num+1)), num+1); {55}
num:=byte(pred(fri));
writeln(num); {3}
end.
Бестиповые параметры
Можно использовать параметры-переменные и параметры-константы без указания типов.
Фактическим параметром может быть переменная любого типа; ответственность за правильность использования параметра возлагается на программиста.
Пример
function Equal(var Param1; Param2; Len:word);
Для формальных параметров Param 1, Param 2 в качестве фактических параметров могут быть использованы любые переменные простого типа, массивы, записи и др.
Параметр без типа внутри подпрограммы перед его использованием надо привести (преобразовать) к конкретному определенному типу.
Пример Программа для вычисления максимального значения с использованием функции с бестиповым формальным параметром.
type
tarr = array[1..100] of integer;
var
massiv: tarr;
maxim, i, k: integer;
function max(var mas; n: byte): integer;
type
tarray=array[1..maxint] of integer;
var ma:integer;
i:byte;
begin
ma:=tarray(mas)[1];
for i:=2 to n do
if ma<tarray(mas)[i] then
ma:=tarray(mas)[i];
end;
begin
writeln(‘Введите число элементов массива ’);
read(k);
writeln(‘Введите элементы массива’);
for i:=1 to k do
read(massiv[i]);
maxim:=max(massiv,k);
writeln(maxim);
end.
Параметры-массивы и строки открытого типа
7.0-версия использует открытые параметры-переменные подпрограммы: массивы и строки. Открытые массивы - это параметры-массивы подпрограммы, размер которых не указывается в списке формальных параметров заголовка подпрограммы.
Пример Объявление открытого одномерного массива
function Sum(var a: array of byte): byte;
var в объявлении параметра открытого типа обязательно.
Фактическим параметром открытого массива может быть массив любого размера. Тип элементов фактического массива должен совпадать с типом массива-формального параметра.
В качестве открытых массивов могут использоваться только одномерные массивы. Внутри подпрограмм границы переданного массива могут определяться с помощью функций Low и High.
function Low(x:<тип>):word;
Возвращает минимальное значение величины;
x – параметр или идентификатор порядкового типа, типа-массива, типа-строки или открытого массива.
function High (x:<тип>):word;
Возвращает максимальное значение величины;
x – параметр или идентификатор порядкового типа, типа-массива, типа-строки или открытого массива.
Обращение к Low всегда будет давать 0, а результат функции High – верхнее возможное значение индекса при обращении к массиву относительно нуля, независимо от значений границ массива, передаваемого в подпрограмму.
Напомним, как передавались в подпрограмму массивы.
type
arr=array[1...10] of byte;
…..
procedure P(var a: arr);
begin
…..
end;
Определён в программе тип arr и затем в заголовке подпрограммы объявлена переменная А указанного типа. После такого объявления процедуры Р в качестве в качестве фактического параметра при ее вызове мог фигурировать только массив, описанный при помощи того же самого( из-за именной эквивалентности типа) типа arr, т.е.
var
A1, A2: Arr;
…..
P(A1);
…..
P(A2)
Таким образом, процедура Р может работать только с массивами из десяти элементов типа byte, «пронумерованных» от 1 до 10. Это сильно ограничивает универсальность процедуры.
Параметры-массивы открытого типа существенно расширяют возможности, т.к. в массиве – формальном параметре подпрограммы возможно не задавать его границы.
Пример Программа подсчета суммы элементов разных массивов с использованием в процедуре формального параметра-открытого массива.
Возможные пути решения задачи подсчета суммы элементов разных массивов:
1) написание отдельных суммирующих функций;
2) использование нетипизированных (бестиповых) параметров;
3) использование открытых массивов.
program OtkrPar;
type
ta1=array[1..5] of byte;
ta2=array[0..2] of byte;
ta3=array[-2..2] of byte;
const
a1:ta1=( 1, 2, 3, 4, 5);
a2:ta2=( 7, 8, 9);
a3:ta3=( 1, 2, 3, 4, 5);
procedure p(var a: array of byte);
var
i,s:word;
begin
s:=0;
for i:=low(a) to high(a) do begin
s:=s+a[i];
write(‘ ‘, i ,’ ‘,a[i]); end;
writeln(‘s= ‘,s);
end;
begin
p(a1); p(a2); p(a3);
end.
Результат выполнения программы program OtkrPar.
Вывод на экран:
0 1 1 2 2 3 3 4 4 5 s=15
0 7 2 8 3 9 s=24
0 1 1 2 2 3 3 4 4 5 s=15
Программа иллюстрирует интерпретацию граничных значений и индексов открытых массивов.
Остановимся на механизме открытых строк. Во многом они аналогичны открытым массивам и позволяют передавать в подпрограммы строки произвольной длины.
Директива {$V+/-}-проверка строковых параметров.
Если использовать ключ{$V+}, то при обращении к подпрограммам и передаче ей параметров типа string фактические и формальные параметры должны быть идентичными.
Если использовать ключ{$V-}, то формальные и фактические параметры могут быть строками разного типа, в том числе разной длины. Ключ являются локальным.
По умолчанию используется ключ {$V+}. Эквивалентное меню Options/Compiler/Strict var-strings.
В 7.0 версии есть директива {$P+/-} –использование в качестве параметров массивов открытого типа. Она ориентирована специально на поддержку открытых строк. По умолчанию используется ключ{$P-}. Эквивалентное меню Options/Compiler/Open parameters.
Если в программе установили {$P-}, то var-параметры подпрограммы, имеющие тип string, считаются обычными параметрами, передаваемыми по ссылке; для них действительны обычные правила эквивалентности типов.
Если в программе задана директива {$P+}, то var-параметры подпрограмм, имеющие тип string, считаются открытыми параметрами. Это означает, что независимо от установки директивы {$V+/-} в качестве фактического параметра подпрограмме можно передать переменную любого строкового типа.
При этом внутри подпрограммы размер (максимальная длина) строки – формального параметра всегда будет совпадать с размером фактического параметра.
Пример
{$P+}
program TestOpen;
uses crt;
var
s1:string;
s2:string[10];
procedure p(var s: string);
begin
writeln(‘s= ‘,sizeof(s),’ ‘,’l=’, length(s))
end;
begin
s1:=’abs’; p(s1);
s2:=’xxxxxxxxxx’; p(s2);
end.
function size of(x):word;
Возвращает размер аргумента в байтах;
х-переменная любого типаили любой тип.
function length (s:string):integer;
Возвращает текущий размер строки;
S-строка, у которой определяется размер.
Результат выполнения программы program TestOpen.
Вывод на экран:
S=256 L=3
S=11 L=10
Внутри процедуры Р размер открытой строки всегда будет соответствовать реально переданной в ней фактической строки.
Установим {$V-}.
Тогда
S=256 L=3
S=256 L=10
В этом случае внутри процедуры Р строка-параметр всегда трактуется по «максимуму», независимо от того, какой фактический параметр реально ей передан.
По существу, директива {$V-} просто снимает контроль за соответствием типов и за корректностью присваивания строковым var-параметрам, что может привести к тяжелым ошибкам во время исполнения.
Механизм открытых строк, предоставляет большую гибкость при передаче параметров, сохраняет полный контроль за их использованием.
Дополнение Версия 7.0 содержит специальный стандартный строковый тип с именем openstring, который указывать вместо любого строкового типа при спецификации параметров подпрограмм.
Использование типа openstring включает механизм открытых строк для данного параметра независимо от текущей установки директивы {$P+/-}.
Пример
procedure p(var s: openstring);
begin
writeln(‘s=’, sizeof(s),’L=’,length(s));
end;
Процедурные типы
Turbo Pascal позволяет вводить переменные специального вида, значениями которых могут служить подпрограммы. Иными словами, Turbo Pascal позволяет интерпретировать процедуры и функции как значения, которые можно присваивать переменным и использовать в качестве параметров. Речь идёт о подпрограммах как о целостных объектах, а не о значениях, возникающих в результате их выполнения.
Пример
var
P: procedure;
Возможными значениями переменной P могут являться любые процедуры без параметров.
В общем случае процедурный тип задаётся конструкцией, очень похожей на заголовок процедуры или функции.
Пример
type
Func=function(x,y: integer): integer;
var
F1, F2: Func;
Переменным F1 и F2 могут быть присвоены в качестве значений функции от двух целых параметров, возвращающие целый результат. Если в этой программе есть описание функции
function Add(a,b: integer):integer;
begin
Add:=a+b
end;
то допустимо присвоение вида F1:=Add;
Переменной F1 в качестве значения присваивается функция как таковая. Выполнения этой функции не происходит.
После такого присваивания имеется возможность вызывать функцию Add как по значению, так и с помощью указания переменной F1, то есть
writeln(Add(1, 2)); writeln(F1(1, 2)); Оба оператора напечатают число 3.
Определение процедурного типа аналогично заголовку подпрограммы с той лишь разницей, что имя подпрограммы не задаётся.
Пример
type
Proc= procedure;
BinOperation= function(x,y: real): real;
UnOperation= function(x: real): real;
Reading= procedure(var f: text; var Elem: char);
Имена формальных параметров, указываемых в процедурных типах, играют чисто иллюстративную роль, на смысл определений никакого влияния не оказывают. Необходимыми являются только идентификаторы типов параметров и результатов. Последнее указывается для функций.
Turbo Pascal позволяет определять переменные, значениями которых могут быть процедуры и функции. Для таких переменных допускаются операторы присваивания, в правых частях которых находятся идентификаторы других процедурных переменных или идентификаторов подпрограммы. Переменная процедурного типа в различные моменты времени может иметь в качестве значения различные подпрограммы.
Такие переменные могут быть использованы для вызова подпрограмм, которые присвоены этим переменным.
Пример
Пусть в программе есть
var
operation: function(x,y: 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
else operation:=Sub;
writeln(operation(2.05,3+x));
В зависимости от истинности условия condition переменной operation будут присвоены либо функция Add, либо Sub. Активизируется та функция, которая была присвоена переменной operation.
Таким образом, в вызовах функции и в операторах процедуры, кроме идентификаторов соответствующих подпрограмм могут быть имена переменных процедурных типов. Выполнение таких конструкций заключается в вызове подпрограмм, которые были присвоены этим переменным.
Проблема совместимости
По присваиванию - частный случай. Процедурные типы должны иметь одинаковое количество формальных параметров. Параметры на соответствующих позициях должны быть одного типа. Имена параметров никакого значения не имеют.
Аналогичным образом должны совпадать типы возвращаемых значений в случае функций.
Механизм процедурных типов предоставляет программисту весьма широкие и удобные возможности при разработке программ. Корректная работа требует соблюдения следующих правил.
- Подпрограмма, присваиваемая процедурной переменной, должна быть оттранслирована в режиме «дальнего типа вызовов». Для достижения эффекта необходимо перед подпрограммой или группой подпрограмм расположить директиву компилятора $F со знаком +, а в конце группы со знаком -.
Пример
{$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;
- Подпрограмма, присваиваемая процедурной переменной, не должна быть стандартной процедурой или функцией. Это ограничение при необходимости можно легко обойти, заключив вызов стандартной подпрограммы в «оболочку», после чего её можно использовать в качестве присваивания.
Пример