Рассмотрим примеры 1 и 2 предыдущего раздела, в которых применяются соответственно функции MiddleAr3 и Sign. В функции MiddleAr3 в качестве формального параметра используется массив Buf, которому при обращении ставится в соответствие фактический параметр X, Y или Z. В функции Sign формальным параметром является переменная x, которой при обращении соответствует фактический параметр d1 или d2.
На уровне Паскаль-программы можно считать, что при обращении к процедуре или функции в их теле происходит замена имени формального параметра на имя фактического параметра. Тогда, например, обращение Sx := MiddleAr3(X) эквивалентно выполнению операторов:
Количество фактических параметров должно быть равно количеству формальных параметров, типы фактических параметров должны соответствовать типам формальных параметров (если последние указаны).
Пусть в заголовке процедуры описан формальный параметр Z типа Ar, где Ar – имя типа массива. Предположим, что при обращении к процедуре формальному массиву Z ставится в соответствие фактический массив X или фактический массив Y того же типа Ar. Возникает вопрос – как передать информацию о фактическом массиве в процедуру?
При передаче массива или переменной другого типа можно использовать один из двух возможных способов: передача адреса переменной или передача значения этой переменной. В соответствии с этим параметры разделяются на два вида: параметры-переменные и параметры-значения.
В первом случае формальному параметру назначается в процедуре некоторый фиктивный адрес. При обращении к ней этот адрес заменяется на адрес фактического параметра. Следовательно, формальный и фактический параметры определяют в данном случае одно и то же поле памяти. В нашем примере имени Z при первом обращении присваивается адрес, который имеет массив X, при втором обращении – адрес массива Y.
Если в процедуре описан формальный параметр-значение, то для него отводится отдельное поле памяти, размер которого определяется типом формального параметра. Тогда при обращении к процедуре в это поле переписывается значение фактического параметра. Следовательно, если Z – формальный параметр-значение, то при обращении к процедуре имена Z и X, Z и Y определяют различные поля памяти.
Обмен информацией между вызывающей программой и процедурой в принципе может быть обеспечен тремя способами, которые можно условно обозначить in, out и inout. В первом случае информация передается только от вызывающей программы в процедуру, во втором случае – только из процедуры в вызывающую программу, в третьем – в обоих направлениях.
В Турбо Паскале для параметра-переменной реализовано два способа: in и inout. В первом случае перед именем формального параметра записывают слово Constи называют его параметром-константой. Во втором случае перед именем формального параметра ставят слово Varи называют его по-прежнему параметром-переменной.
Поскольку формальный параметр-переменная и соответствующий ему фактический параметр имеют один и тот же адрес поля памяти, то любые изменения формального параметра в теле процедуры приводят к таким же изменениям фактического параметра. В частности, это означает, что выходные результаты работы процедуры обязательно должны быть параметрами-переменными.
Различие между параметром-переменной и параметром-константой заключается лишь в том, что по отношению к параметру-константе запрещаются любые изменения ее значения в теле процедуры, т.е. компилятор при трансляции программы запрещает присваивание новых значений такому параметру. Обычно параметры-константы устанавливаются для формальных массивов; в этом случае в процедуру передается адрес массива (4 байта), но изменения фактического массива производиться не будут.
Для параметра-значения в Турбо Паскале реализован лишь один способ обмена информацией – вариант in. Перед именем формального параметра-значения в заголовке процедуры ничего не записывается.
При обращении к процедуре формальному параметру-переменной в качестве фактического параметра может соответствовать только переменная (простая или составная), параметру-значению – переменная, константа, выражение, а также имя конкретной процедуры или функции, которые считаются соответственно процедурной или функциональной константой.
Параметры-значения в общем случае требуют больше машинного времени и больше памяти, поэтому их следует применять главным образом тогда, когда в качестве фактического параметра может быть использована константа или выражение.
Рассмотрим несколько подробнее, почему формальному параметру-переменной не могут ставиться в соответствие константа или выражение.
Константа, как и любая переменная, имеет в программе определенный адрес. Однако передача этого адреса в процедуру означало бы, что в теле процедуры может быть изменена данная константа, что недопустимо. Выражение, записанное в списке фактических параметров, не может иметь какого-либо адреса. Перед обращением к процедуре вызывающая программа вычисляет значение такого выражения и передает это значение в процедуру.
Параметры-переменные могут быть нетипизированными, т.е. после имени формального параметра не указывается имя типа. В этом случае им соответствуют фактические параметры любого типа.
Пример 1.Этот пример иллюстрирует разницу между параметром-переменной и параметром-значением.
Program Parameters;
Varm,n : integer;
{ -------------------- }
ProcedureAdd(x:integer; Var y:integer);
Begin
x:=x+1; y:=y+1;
Writeln('x= ',x,' y=',y);
End { Add };
{ -------------------- }
Begin
m:=0; n:=0;
Add(m,n);
Writeln('m= ',m,' n= ',n);
End.
Будет напечатано:
1 1
0 1
При рассмотрении оператора Forбыло указано, что описание параметра цикла должно быть расположено в том же блоке, где находится сам оператор For. Рассмотрим ситуацию, которая может сложиться при нарушении этого требования.
Пример 2. Для заданного вещественного массива сформировать целочисленный массив , в котором значение определяет количество элементов в массиве , превышающих значение . Вычисление количества таких элементов оформить в виде функции.
Program MakeArray;
ConstNmax = 500;
Type Ar1 = array[1..Nmax] of real;
Ar2 = array[1..Nmax] ofword;
Vari,n : word;
X : Ar1;
Y : Ar2;
{ --------------------------- }
FunctionElem(k:word):word;
Varp : word; { Определение количества }
Begin{ элементов массива Х, }
p:=0; { превышающих значение }
For i:=1 ton do{ элемента x[k] }
Ifx[i]>x[k] then
Inc(p);
Elem:=p;
End{ Elem };
{ --------------------------- }
Begin
В в о д n, X
For i:=1 to n do
y[i]:=Elem(i);
П е ч а т ь Y
End.
Здесь и в основной программе, и в подпрограмме используется параметр цикла i, описанный как глобальная переменная. Обращение к функции Elem производится при каждом выполнении цикла For основной программы. После первого же обращения к функции Elem переменная i будет иметь значение n, что приведет к прекращению работы цикла For основной программы. В результате для массива Y будет вычислено лишь одно значение .
Ситуация, демонстрируемая в программе MakeArray, аналогична случаю, когда во внешнем и внутреннем циклах используется один и тот же параметр цикла. Пример:
For i:=1 ton do
Begin
p:=0; k:=i;
For i:=1 ton do
Ifx[i]>x[k] then
Inc(p);
y[i]:=p;
End;
Следовательно, параметр цикла Forнеобходимо описывать в той же процедуре или функции, где расположен этот оператор.