Согласно более общему определению процедура может иметь параметры, метки перехода внутри себя и свои, локальные, переменные (рис. 6.5). Обязательными элементами процедур и функций тоже является заголовок и тело, т.е. тот же составной оператор.
Синтаксис вызова процедуры прост. Ее выполнение активизируется указанием ее имени и списком переменных или значений, подставляемых на место параметров:
ИмяПроцедуры(Параметр1, Параметр2,);
- 107 -
PROCEDURE ИмяПроцедуры (ПарамЗнач1 : ТипЗнач1;
ПарамЗнач2 : ТипЗнач2;
VAR ПарамПерем1 : ТипПерем1;
VAR ПарамПерем2 : ТипПерем2; ... );
LABEL
Перечисление меток внутри тела процедуры
CONST
Описание локальных констант процедуры
TYPE
Описание локальных типов
VAR
Описание локальных переменных
Описание вложенных процедур и (или) функций
BEGIN
Тело процедуры
END;
Рис. 6.5
Общая структура функций совпадает с процедурами, за исключением заголовка. Он записывается как
FUNCTION ИмяФункции( Список параметров ) :
ИмяСкалярногоТипаЗначенияФункций;
Что и как может возвращать функция при ее вызове, мы рассмотрим чуть позже.
Нетрудно заметить, что структура подпрограмм копирует структуру программы в целом (не считая заголовка и завершающей точки с запятой вместо точки после END). Порядок следования разделов описаний подчиняется тем же правилам, по которым строится вся программа.
6.9.1. Параметры. Глобальные и локальные описания
Поскольку процедуры и функции должны обладать определенной независимостью в смысле использования переменных (а также типов и констант), при их введении в программу возникает разделение данных и их типов на глобальные и локальные. Глобальные константы, типы, переменные — это те, которые объявлены в программе вне процедур или функций. Наоборот, локальные — это константы, типы и переменные, существующие только внутри процедур или функций, и объявленные либо в списке параметров (только переменные), либо в разделах CONST, TYPE, VAR внутри процедуры или функции.
- 108 -
Процедуры и функции могут, наряду со своими локальными данными, использовать и модифицировать и глобальные. Для этого нужно лишь, чтобы описание процедуры (функции) стояло в тексте программы ниже, чем описания соответствующих глобальных типов, констант и переменных (рис. 6.6).
PROGRAM Main;
| VAR
| Xmain, Ymain : LongInt; {глобальные переменные}
| Res : Real;
| PROCEDURE Proc1( a,b : Word; VAR Result : Real );
| VAR
| Res : Real; { локальная Res, закрывающая глобальную }
| BEGIN
| Res := a*a + b*b; { локальные действия }
| Result:= Xmain+Ymain*Res; {работают глобальные значения}
| Xmain := Xmain+1; { модифицируется глобальное значение}
| END;
TYPE
CONST Другие глобальные объявления, уже
VAR недоступные из процедуры Proc1;
BEGIN
Основной блок, в котором может вызываться Proc1
END.
Рис. 6.6
При совпадении имен локальной и глобальной переменных (типов, констант) сильнее оказывается локальное имя, и именно оно используется внутри подпрограммы. Так, существует неписанное правило: если подпрограмма содержит в себе циклы FOR, то параметры циклов должны быть описаны как локальные переменные. Это предупредит неразбериху при циклическом вызове процедур.
Мы уже отмечали, что параметры, описываемые в заголовке процедур и функций, по сути, являются локальными переменными. Но кроме того, они обеспечивают обмен значениями между вызывающими и вызываемыми частями программы (т.е. теми же процедурами или функциями). Описываемые в заголовке объявления подпрограммы параметры называются формальными, а те, которые подставляются на их место при вызове, — фактическими, ибо они при выполнении как бы замещают все вхождения в подпрограмму своих формальных «двойников».
- 109 -
Параметры подпрограмм разделяются на параметры-значения и параметры-переменные. Параметры-значения — это локальные переменные подпрограммы, стартовые значения которых задаются при вызове подпрограммы из внешних блоков (а те локальные переменные, которые описаны в разделе VAR между заголовком и телом подпрограммы, должны получать свои значения присваиванием внутри тела подпрограммы). Параметры-значения, описанные в заголовке, могут изменять свои значения наряду с прочими переменными, но эти изменения будут строго локальными и никак не передадутся в вызывающие операторы. Для того чтобы подпрограмма изменила значение переданной ей переменной, нужно объявлять соответствующие параметры как параметры-переменные, вставляя слово VAR перед их описанием в заголовках. Рассмотрим внутренний механизм передачи параметров подпрограмм. При вызове процедуры или функции каждой локальной переменной, описанной внутри процедуры, и каждому параметру-значению отводится место для хранения данных в специальной области памяти, называемой стеком. Эти места принадлежат переменным ровно столько времени, сколько выполняется подпрограмма. Причем ячейки, соответствующие параметрам-значениям, сразу заполняются конкретным содержимым, заданным в вызове подпрограммы. По-другому организуются параметры-переменные. Вместо копии значения подпрограмма получает разрешение работать с тем местом, где постоянно (т.е. все время работы самого вызывающего программного блока) хранится значение переменной, указанной в вызове на месте параметра-переменной. Все действия с параметром-переменной в подпрограмме на самом деле являются действиями над подставленной в вызов переменной.
В этом, кстати, заключается причина того, что на место параметров-значений можно подставлять непосредственно значения, а на местах параметров-переменных может быть лишь идентификатор переменной.
Рассмотрим пример процедуры (рис. 6.7), принимающей любое число и возвращающей его квадрат. Если значение квадрата числа превышает значение 100, то оно считается равным 100. При этом должен устанавливаться глобальный «флаг».
На рис. 6.7 отмечены оба способа обмена данными с процедурой: непосредственной модификацией глобальных переменных и передачей переменной через VAR-параметр. Обратите внимание на использование локальной переменной X. Подобные приемы иногда позволяют не вводить лишних локальных переменных.
- 110 -
| VAR
| GlobalFlag : Boolean; {глобальный флаг}
| PROCEDURE GetSQR( X : Real; VAR Sq : Real );
| { процедура не имеет локальных переменных, кроме X }
| CONST
| SQRMAX =100; { локальная простая константа }
| BEGIN { начало тела процедуры }
| { В X запишется квадрат его последнего значения: }
| X := X * X;
| { Результат сравнения запишется в глобальный флаг: }
| GlobalFlag := ( X > SQRMAX );
| if GlobalFlag then
| X:=SQRMAX; { ограничение X }
| Sq := X { возвращение значения }
| END; { конец тела процедуры }
| VAR
| SqGlobal : Real;