Дмитрий Поляков - Программирование в среде Турбо Паскаль стр 25.

Шрифт
Фон

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

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

- 112 -

6.9.2. Опережающее описание процедур и функций

Текст программы транслируется в выполнимый код последовательно сверху вниз. При этом переменные, типы, константы и подпрограммы должны описываться до того, как начнутся их упоминания в операторах или выражениях. В противном случае компилятор объявит имя неизвестным, и придется перемещать описания подпрограмм вверх по тексту программы. В случаях с процедурами и функциями этого можно избежать, используя опережающее описание директивой FORWARD:

PROCEDURE ИмяПроцедуры(параметры); FORWARD;

FUNCTION ИмяФункции( параметры ) : ТипЗначения; FORWARD;

......

PROCEDURE ИмяПроцедуры; { список параметров уже не нужен }

Тело процедуры

FUNCTION ИмяФункции; { достаточно указать только имя }

Тело функции

......

Эта директива объявляет заголовок подпрограммы, откладывая описание содержимого "на потом". Местоположение этого описания уже не играет роли, и в нем можно не указывать параметры, а ограничиться лишь именем подпрограммы. Основное описание не может иметь никаких директив (FORWARD, EXTERNAL и др.).

Директива FORWARD существует в языке в основном для развязки закольцованных вызовов. Так, ситуацию на рис. 6.9 можно разрешить только с ее помощью:

PROCEDURE a( у : TypeXY ); FORWARD;

PROCEDURE b( x : TypeXY );

BEGIN

...

a(p); {процедура b вызывает a}

END;

PROCEDURE a;

BEGIN

...

b( q ); {но сама a вызывает b }

END;

Рис. 6.9

- 113 -

6.9.3. Объявление внешних процедур

Турбо Паскаль - язык не слишком коммуникабельный по отношению к прочим языкам. Он не поддерживает генерацию объектных файлов в формате OBJ и вследствие этого не может поставлять написанные на нем процедуры и функции для связи с другими языками. Единственное, что можно - это использовать при компиляции и компоновке программ на Турбо Паскале внешние подпрограммы в виде OBJ-файлов, созданных другими компиляторами. OBJ-файлы должны при этом удовлетворять определенным требованиям к используемой модели памяти и способу передачи значений. Гарантируется совместимость кодов, полученных компилятором Turbo Assembler. He должно быть проблем и с кодами от ассемблера MASM или ему подобных. Возможен импорт объектных кодов, полученных в Turbo C и других языках, но на практике он труднореализуем.

Команды подстыковки объектных файлов в программу на Турбо Паскале задаются директивами компилятора {$L ИмяФайла.OBJ}, установленными в подходящих местах программы. А те процедуры и функции, которые реализованы в этих файлах, должны быть объявлены своими заголовками и специальным словом EXTERNAL, например:

{$L memlib.obj} { включение объектного кода }

procedure MemProc1; external;

PROCEDURE MemProc2( X,Y : Byte ); EXTERNAL;

FUNCTION MemFunc1( X :Byte; VAR Y :Byte ): Word; EXTERNAL;

Подключенные таким образом внешние функции или процедуры в дальнейшем ничем не отличаются от написанных в тексте. Обычно директиву включения OBJ-файла и объявления внешних подпрограмм удобно размещать рядом. Порядок следования директивы $L и описаний заголовков может быть произвольным.

6.9.4. Процедуры и функции как параметры

Отличительной особенностью Турбо Паскаля является разрешение передавать в процедуры и функции имена других подпрограмм, оформляя их как параметры. И точно так же, как передавалось значение, может передаваться некая функция его обработки. Особенно важным это становится при программной реализации алгоритмов вычислительной математики (хотя можно назвать и ряд других областей). Например, становится возможным написать процедуру интегрирования любой функции вида f(t) по следующей

- 114 -

схеме (рис. 6.10). Неочевидным здесь может показаться только введение функционального типа и то, как он определяется.

PROCEDURE Integrate LowerLimit, UpperLimit : Real;

VAR

Result : Real;

Funct : Функциональный тип);

VAR Описание локальных переменных процедуры

t : Real;

BEGIN

Численное интегрирование по t от LowerLimit до

Upper limit функции Funct, причем для получения

значения функции при заданном аргументе t достаточно

сделать вызов Funct(t).

Результат интегрирования должен быть возвращен через

параметр-переменную Result.

END;

Рис. 6.10

Функциональный или процедурный тип (в зависимости от того что описывается) - отнюдь не тип возвращаемого значения, а тип заголовка подпрограммы в целом. Так, на рис. 6.10 параметр Func есть одноместная функция вида f(t), возвращающая вещественное значение. Класс таких функций может быть описан типом

| TYPE

RealFunctionType = function ( t : Real ) : Real;

В этом описании имя подпрограммы не ставится - оно здесь не играет роли. Но обязательно перечисляются типы параметров и, если тип описывает функцию, тип результата. Идентификаторы параметров могут быть выбраны произвольно. Основная смысловая нагрузка падает на их типы и порядок следования. Тип, к которому могла бы принадлежать процедура Integral (см. рис. 6.10), должен был бы выглядеть примерно так:

| TYPE

ProcType = procedure ( А, В : Real; VAR X : Real;

f : RealFunctionType );

а тип процедуры без параметров:

NoParamProcType = procedure;

После объявления процедурного (функционального) типа его можно использовать в описаниях параметров подпрограмм. И, ко-

- 115 -

нечно, необходимо написать те реальные процедуры и функции, которые будут передаваться как параметры. Требование к ним одно: они должны компилироваться в режиме {$F+}. Поскольку по умолчанию принят режим {$F-}, такие процедуры обрамляются парой соответствующих директив. На рис. 6.11 дан пример функции, принадлежащей введенному выше типу RealFunctionType.

| { $F+} { включение режима $F+ }

| FUNCTION SinExp ( tt : Real ) : Real;

| BEGIN

| SinExp := Sin(tt)*Exp(tt)

| END;

| {$F-} { восстановление режима по умолчанию }

Рис. 6.11

Такая функция может быть подставлена в вызов подпрограммы на рис. 6.10:

Integral( 0, 1, Res1, SinExp )

и мы получим в переменной Res1 значение интеграла в пределах [0,1]. Не всякую функцию (процедуру) можно подставить в вызов. Нельзя подставлять: во-первых, процедуры с директивами inline и interrupt (из-за особенностей их машинного представления); во-вторых, вложенные процедуры или функции; в-третьих, стандартные процедуры и функции, входящие в системные библиотеки Турбо Паскаля. Нельзя, например, взять интеграл функции синуса:

Integral(0, 1, Res2, Sin)

хотя встроенная функция Sin внешне подходит по типу параметра. Последнее ограничение легко обходится переопределением функции (рис. 6.12).

| { $F+}

| FUNCTION Sin2( X : Real ) : Real;

| BEGIN

| Sin2 := Sin( X )

| END;

| {$F-}

Рис. 6.12

- 116 -

Теперь вызов процедуры интегрирования переписывается как

Integral( 0, 1, Res2, Sin2 )

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

Ваша оценка очень важна

0
Шрифт
Фон

Помогите Вашим друзьям узнать о библиотеке