Смекни!
smekni.com

Курс лекций (стр. 23 из 24)

C := C + C;

B := B + B;

Writeln(C,B)

end;

Begin

Writeln(A,B);

Plus(A,B);

Writeln(A,B)

End.

Вначале на экран будут выведены числа "5 7". В вызове процедуры Plus фактическими параметрами являются параметр-переменная А и параметр-значение В. Переменная А во время действия процедуры меняет свое значение (в течении процедуры она выступает с именем С), новое удвоенное значение записывается на место старого. Переменная В копирует свое значение в локальную переменную с тем же именем. После выполнения процедуры на экране появляются "10 14". Последний оператор выводит на экран числа "10 7".

Итак, параметры-переменные используются как средство передачи результатов своей работы вызывающему блоку. Разумеется, способом передачи может являться использование глобальных переменных. Однако злоупотребление глобальными связями делает программу, как правило, запутанной, трудной в понимании и сложной в отладке. В соответствии с требованиями хорошего стиля рекомендуется там, где это, возможно, использовать передачу результатов через фактические параметры-переменные. С другой стороны, описание всех формальных параметров, как параметров-переменных нежелательно по двум причинам. Во-первых, это исключает возможность вызова подпрограммы с фактическими параметрами в виде выражений, что делает программу менее компактной. Во-вторых, и главных, в подпрограмме возможно случайное использование формального параметра, что создает опасность непреднамеренно испортить фактическую переменную. Чем меньше параметров объявлено параметрами-переменными и чем меньше в подпрограмме используется глобальных переменных, тем меньше опасность получения побочных эффектов, тем проще подпрограмма в понимании и отладке.

Существенное различие в объявлении переменных в списке формальных параметров от объявления их в разделе описания переменных состоит в том, что типом любого параметра в списке формальных параметров может быть только стандартный или ранее объявленный тип (не годится Procedure P (A : array [1..10] of Real)). Поэтому для того, чтобы передать массив (или строку) нужно сначала описать его тип. Это накладывает некоторые ограничения в том случае, когда заранее не известна длина массива (строки). Описывать его по максимуму приводит к затратам памяти. Вообще, невозможность использования статических массивов с "плавающими" границами является существенным недостатком ТР, и объектом его критики. В версии 7.0 язык поддерживает т.н. открытые массивы, с помощью которых легко решается проблема передачи подпрограмме одномерных массивов переменной длины. Открытый массив - формальный параметр описывается как:

Procedure P (OpenArray: array of Real). В этом случае можно подставлять в процедуру фактические одномерные массивы любой длины. Верхняя граница возвращается функцией High.

Особенности функций

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

Функция, как и процедура, может обмениваться значениями с программой и изменять глобальные переменные непосредственно или через параметры-переменные. Обычно, когда функция, кроме выдачи своего значения, меняет какие-либо глобальные значения или производит другие действия, не связанные с вычислениями своего значения, говорят, что она имеет побочный эффект. Функция вычисления Х в степени Y по формуле (ХY=exp(Y × ln(X)):

Var

X,Y : Real;

Function Power (Arg, P : Real) : Real;

begin

if Arg <> 0 then {если аргумент ¹ 0}

Power := exp(P * ln(abs(Arg)))

else

if P = 0 then {если аргумент = 0 и степень = 0}

Power := 1

else {если аргумент = 0 и степень ¹ 0}

Power := 0

end;

Begin

Readln(X,Y);

Writeln(Power(X,Y))

End.

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

Опережающее описание

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

Procedure <имя> (<параметры>); forward;

...

Procedure <имя> {список параметров уже не нужен}

<тело процедуры>;

Эта директива объявляет заголовок подпрограммы, откладывая описание содержимого «на потом». Местоположение этого описания не играет роли, и в нем можно не указывать параметры, а ограничиться лишь именем. Директива Forward существует в языке, в основном, для развязки закольцованных вызовов (одна подпрограмма вызывает вторую, которая в свою очередь вызывает первую).

Кроме стандартной директивы Forward можно использовать и другие стандартные директивы, которые должны указываться за заголовком подпрограммы. Эти директивы уточняют действия компилятора и распространяются на всю подпрограмму и только на нее.

Assembler - отменяет стандартную последовательность машинных инструкций, вырабатываемых при входе в процедуру и выходе из нее. Тело подпрограммы должно в этом случае реализовываться с помощью команд встроенного ассемблера (ассемблера, встроенного компилятор ТР);

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

Far, Near - компилятор должен создавать код программы, рассчитанный на дальнюю либо ближнюю модель памяти;

Inline - указывает на то, что тело подпрограммы реализуется с помощью встроенных машинных команд;

Interrupt - используется при создании процедур обработки прерываний.

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

Начиная с версии 5.5 в ТР был введен новый тип - процедурный тип. Этим был сделан важный шаг от языка Turbo Pascal как средства для обучения программированию к языку Turbo Pascal как средству профессионального программирования. Процедурный тип, являясь расширением стандартного Pascal и позволяет интерпретировать процедуры и функции как объекты, которые можно использовать при передачи параметров (понятие "данные" сюда меньше подходит).

Описание процедурного типа выполняется в соответствии с уже принятым синтаксисом (процедурный тип - общее понятие, объединяющее и функциональный тип):

Type

ProcType = Procedure (S : String; Var B : Byte);

FuncType = Function (X,Y : Integer) : Real;

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

Type

FuncType = Function (X : Real) : Real; {процедурный тип}

Procedure Integral (LowerLimit, UpperLimit : Real; {вычисл. интеграла функции

Var Result : Real; Funct от точки LowerLimit до UpperLimit с

Funct : FuncType); результатом в параметре-переменной Result}

begin

end;

Function SinExp (Arg : Real) : Real; {некоторая функция}

begin

SinExp := Sin(Arg) + Exp(Arg)

end

Begin

Integral (0, 1, Res, SinExp); {процедура вычисления интеграла с

… записью в фактическую переменную Res}

End.

Описав другую функцию, можно определить ее интеграл, подставив на место соответствующего параметра.

Не разрешается объявлять функции, возвращающие значение процедурного типа. Требование к процедурам и функциям, которые передаются как параметры: они должны компилироваться в режиме {$F+}, что обеспечивает использование дальней модели обращения Far (в примере эта директива должна стоять перед описанием функции SinExp). Не всякую функцию (процедуру) можно подставить в вызов. Нельзя подставлять: процедуры с директивами Inline и Interrupt, вложенные процедуры и функции, стандартные процедуры и функции (не годится Integral (0, 1, Res, Sin)).

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

Type

RecType = record

X,Y : Integer;

P : ProcType;

end;

Var

Rec1,Rec2 : RecType;

Естественно, в процессе работы программы требуется присвоить некоторое значение полю Rec1.P. Этим значением должна быть специально описанная процедура. Т.е. в такое записи можно хранить не только данные, но и процедуру их обработки. Причем в любой момент можно сменить ту процедуру, которая понимается под именем Rec1.P.

Процедурные переменные по формату совместимы с переменными типа Pointer и после приведения типов могут обмениваться с ними значениями.

Используя директиву Absolute можно обмениваться данными между подпрограммами путем совмещения областей памяти их локальных переменных с адресом глобальной переменной.

Обыкновенные локальные переменные в подпрограммах всегда "забывают" свои значения и при повторном вызове стартовые значения локальных переменных совершенно случайны. Для сохранности информации между вызовами можно использовать статические локальные переменные, которые вводятся как локальные типизированные константы. Const A : Word = 240; Значение типизированной константы может меняться в теле подпрограммы. После чего оно запоминается до повторного вызова (восстановление стартового значения не происходит).