Параметры. Глобальные и локальные описания
При введении в программу подпрограммы возникает разделение данных и их типов на глобальные и локальные.
Глобальными по отношению к данному блоку являются объекты верхнего уровня, описанные до появления данного блока.
Локальные объекты – объекты, существующие внутри данного блока. Объявлены либо в списке параметров (только переменные), либо в разделах CONST, VAR, TYPE, внутри подпрограммы.
Для программистов, использующих обращение к блоку, блок представляется как «черный ящик», в котором реализуется тот или иной алгоритм. В примере, рассмотренном нами, из основной программы можно обратиться к процедурам А, В, С, но нельзя вызвать А1, А2, В1, В2. Образно говоря, блок как бы окружен полупрозрачными стенками, снаружи не видно, что делается внутри, но попав в блок можно увидеть, что делается вне его, но только «сверху», а не «снизу». Так, из блока В21 можно обратиться к В2 , В1, В, а, к основной программе.
При совпадении глобальных и локальных имен сильнее оказывается локальное имя, и именно оно используется внутри подпрограммы. Существует неписанное правило: если блок содержит цикл FOR, то параметры должны быть описаны как локальные переменные. Это предотвратит неразбериху при циклическом вызове процедур.
Процедуры
Процедура состоит из заголовка и тела.
Заголовок: PROCEDURE (ИМЯ) [(список формальных параметров)]
(имя – имя процедуры (правильный идентификатор)
(список формальных параметров) - список формальных параметров (может отсутствовать)
Затем идут те же разделы, что и в основной программе. Завершается «;».
Пример. Процедура ввода N целых чисел.
Пусть в основной программе определен TYPE и VAR
TYPE
arr:=array[1…10]of integer;
VAR
m:arr;
k:integer;
Процедура может иметь вид:
{заголовок}
PROCEDURE INPINT (var mas :arr; n: integer);
Var
i:integer; (локальный параметр процедуры)
Begin
Writeln (‘Введите’, n:3,‘целых чисел’);
For i:=1 to n do
read (mas[i])
End;
Синтаксис вызова процедур прост: выполнение активизируется указанием ее имени и списком var или значений, подставляемых на место формальных параметров (фактические параметры):
<имя процедуры> (параметр 1, параметр 2,…….);
K:=40;
Inpint (m,k); или inpint (m,40);
Вызывается процедура input для ввода 40 целых чисел в массив М.
Параметры, описываемые в заголовке подпрограммы, являются по сути локальными переменными. Но, кроме того, они обеспечивают обмен значениями между вызывающими и вызываемыми программами. Описываемые в заголовке параметры называются формальными, а те которые подставляются на их место при вызове – фактическими, т.к. замещают все вхождения в подпрограмму своих формальных «двойников». Между формальными и фактическими параметрами должно быть полное соответствие:
- одинаковое количество;
- один и тот же порядок следования;
- тип фактических параметров должен совпадать с типом формальных параметров.
Пример. Программа подсчитывает количество точек (.) в первой вводимой с терминала строке, а во второй строке – количество букв «А».
Program A;
Const
Point=‘.’;
Var
S:=integer;
Procedure P (simb:char; var kol:integer);
Var
C:char;
begin
Kol:=0;
Writeln (‘Введите строку’);
Repeat
Read (c);
If c=simb then inc(kol);
Until eoln
end;
Begin
P(point,s);
Writeln (‘Количество точек=’,s:3);
P(‘A’,s);
Writeln (‘Количество букв «А»=’,s:3);
end.
Процедура имеет два формальных параметра:
· simb - входной параметр (параметр-значение), определяет символ, который подсчитывается в строке;
· kol – выходной параметр (параметр-переменная), через который передается в программу количество подсчитанных символов.
Таким образом мы столкнулись с двумя видами параметров:
· параметр-переменная;
· параметр-значение.
Параметры-значения – локальные переменные подпрограммы, стартовые значения которых задаются при вызове подпрограммы из внешних блоков. Параметры значения могут изменять свои значения, как и другие переменные, но эти изменения никак не передаются в вызывающую программу. Для того, чтобы подпрограмма изменяла значение переданной переменной, нужно объявлять соответствующие параметры, как параметры-переменные, вставляя слово var перед описанием в заголовке.
Рассмотрим внутренний механизм передачи параметров в подпрограмму.
При вызове процедуры или функции каждой локальной переменной, описанной внутри подпрограммы, и каждому параметру значения отводится место для хранения данных в специальной области памяти, именуемой стеком. Эти места принадлежат переменным ровно столько времени, сколько выполняется подпрограмма. Ячейки для переменных-значений заполняются сразу содержимым.
По-другому организуются параметры-переменные. Вместо копий значения подпрограмма получает разрешение работать с тем местом, где постоянно (т.е. во время работы самого вызывающего блока ) хранится значение переменной, указанной в вызове. Все действия с параметром-переменной в подпрограмме на самом деле являются действиями над поставленной в вызове переменной. В этом заключается причина того, что на место параметров-значений можно подставлять непосредственно значения, а на местах параметров переменных могут быть только идентификаторы переменных.
Процедура Р может передавать результат своей работы через глобальную переменную S:
Procedure P(simb:char);
If c=simb then s:=s+1;
S:=0; P(point);
……………………………….
S:=0; P(‘A’);
Однако, в сложных программных комплексах не рекомендуется использование глобальных переменных.
Функции
Описание функции в основном аналогично описанию процедуры. Однако, имеются два отличия.
Первое - результатом работы функции является одно скалярное значение или одно значение ссылочного типа. Тип результата задается в заголовке функции.
Второе отличие заключается в том, что теле функции хотя бы раз имени функции должно быть присвоено значение.
Общий вид:
Function <имя> [(список фактических параметров)] :<тип>;
< тип> тип возвращаемого функцией результата.
Пример. Функция принимает любое число и возвращает его квадрат. Если значение квалрата больше 100, то оно считается равным 100. При этом устанавливается глобальный «флаг».
Var
glflag:boolen: {глобальный флаг}
Function getsqr (x:real):real;
Const
sqrmax=100; {локальная const}
begin
X:=x*x; {вычисление квадрата}
Glflag := (x>sqrmax); {результат сравнения в глобальный флаг]
If glflag then x:= sqrmax {ограничение x}
Getsqr :=x {возвращает значение}
end;
Begin {основной вызывающий блок}
Writeln (getsqr (6):7:2, ‘флаг:’, glflag)
End.
При объявлении формальных параметров в <списке формальных параметров>, тип любого параметра может быть только стандартным или ранее объявленным, потому, что нельзя объявить:
Procedure S(a:array[1…10] of real);
Проблема решается так:
Type
Atype = array [1..10] of real
Procedure S (a:atype)
В блок передается весь массив.
Процедура EXIT
GOTO нельзя использовать для досрочного выхода из подпрограммы. В Т-П для этой цели используется процедура EXIT.
Пример. Функция, определяющее первое отрицательное число в массиве.
Type
Arr_=array [1..100] of real;
Var
Ar:arr_
Function minus (var mas:arr_; N:integer):real;
Var
I:integer;
Begin
Minus :=0;
For i:=1 to n do
If mas[i]<0 then
Begin
Minus:=mas[i];
Exit
End;
End;
Begin
Writeln (‘Первое отрицательное число в массиве=’, minus(ar,50):7:2);
End.
Рекурсия
Использование рекурсии – преимущество Т-П. Под рекурсией понимается вызов функции (процедуры) из тела самой функции (процедуры)
Эта возможность связана с тем, что при каждом новом обращении параметры, которые она использует, заносятся в стек, причем параметры предыдущего обращения сохраняются.
В ряде случаев рекурсивное оформление более компактно и эффективно.
Пример. Вариант функции вычисляющей факториал числа N.
Function fact (n:integer):longint;
Begin
If n in[0..1] then fact:=1
else
fact :=n*fact (n-1)
End;
Если в функцию передается n>0, то происходит следующее:
· запоминаются известные значения членов выражения;
· для вычисления неизвестных вызываются те же функции, но с «предшествующими» аргументами.
Так происходит до тех пор, пока выражение не станет полностью определенным (ветвь then). После чего алгоритм начинает «раскручиваться» в другую сторону, изымая из памяти «отложенные значения. При этом на каждом очередном шаге все члены выражения будут известны через n обратных шагов, получили результат.
Рекурсия заставляет программы расходовать больше памяти, но сами программы более изящны.
Директивы подпрограмм
Директивы дают дополнительную информацию, в частности, о размещении подпрограмм.
Директива FORWARD
Если одна подпрограмма использует другую, а та в свою очередь первую, то возникает проблема размещения таких подпрограмм в программе. Текст программы транслируется последовательно сверху вниз. При этом const, type, var должны быть определены до того, как начнутся их упоминания в операторах. В противном случае компилятор объявит их неизвестными. Проблема решается следующим образом:
Procedure <имя процедуры> (параметры); forward;
Function <имя функции> (параметры) : тип значения; forward;
Procedure <имя процедуры> ; {только имя}
{Тело процедуры}
Function <имя функции>; {только имя}
{Тело функции}
Открытые массивы
В Т-П имеется возможность передавать в подпрограмму массивы, размер которых не фиксируется в объявлении подпрограммы.
Function sum1 (var a:array of byte):byte;
Это единственное исключение из правила, согласно которому типы формальных параметров в заголовке подпрограммы могут задаваться только посредством идентификаторов.
Подпрограмма, содержащая открытый массив в списке своих формальных параметров, может быть вызвана с массивом любого размера (типы фактических параметров должны совпадать).