Отчет о проделанной работе должен содержать
- название и цель работы;
- номер и условие своего варианта;
- тексты программ;
- полученные результаты;
- анализ результатов;
- ответы на контрольные вопросы.
Теоретические положения
Общие сведения о подпрограммах
Подпрограммы представляют собой инструмент, с помощью которого любая программа может быть разбита на ряд в известной степени независимых друг от друга частей. Такое разбиение необходимо по двум причинам.
Во-первых, это средство экономии памяти: каждая подпрограмма существует в программе в единственном экземпляре, в то время как обращаться к ней можно многократно из разных точек программы. При вызове подпрограммы активизируется последовательность образующих ее операторов, а с помощью передаваемых подпрограмме параметров нужным образом модифицируется реализуемый в ней алгоритм.
Вторая причина заключается в применении методики нисходящего проектирования программ. В этом случае алгоритм представляется в виде последовательности относительно крупных подпрограмм, реализующих более или менее самостоятельные смысловые части алгоритма. Подпрограммы в свою очередь могут разбиваться на менее крупные подпрограммы нижнего уровня и
т.д. (рис. 1). Последовательное структурирование программы продолжается до тех пор, пока реализуемые подпрограммами алгоритмы не станут настолько простыми, чтобы их можно было легко запрограммировать.
Рисунок 1 – Пример структуры программы
Вот какую иерархию описаний получим, например, для программы, структура которой изображена на рис.1 (для простоты считается, что все подпрограммы представляют собой процедуры без параметров):
Program ...;
Procedure А;
Procedure A1;
.......
begin
.......
end {A1};
Procedure A2;
.......
begin
end {A2};
begin {A}
.......
{A};
Procedure В;
Procedure B1;
.......
begin {B};
end
Procedure B2 ;
Procedure B21;
.......
и т.д.
Имена, локализованные в подпрограмме, могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя «закрывает» глобальное и делает его недоступным, например:
var
i : Integer;
Procedure P;
var i : Integer;
begin
writeln(i)
end {P};
begin
i := 1;
P
end.
Что напечатает эта программа? Все, что угодно: значение внутренней переменной I при входе в процедуру Р не определено, хотя одноименная глобальная переменная имеет значение 1. Локальная переменная «закроет» глобальную и на экран будет выведено произвольное значение, содержащееся в неинициированной внутренней переменной. Если убрать описание
var i : Integer;
из процедуры Р, то на экран будет выведено значение глобальной переменной I, т.е. 1.
Таким образом, одноименные глобальные и локальные переменные - это разные переменные. Любое обращение к таким переменным в теле подпрограммы трактуется как обращение к локальным переменным, т.е. глобальные переменные в этом случае попросту недоступны.
Описание подпрограммы состоит из заголовка и тела подпрограммы.
Заголовок
Заголовок процедуры имеет вид:
PROCEDURE <имя> [ (<сп. ф. п . >) ] ;
Заголовок функции:
FUNCTION <имя> [ (<сп.ф.п.>)] : <тип>;
Здесь <имя> - имя подпрограммы (правильный идентификатор);
<сп.ф.п.> - список формальных параметров;
<тип> - тип возвращаемого функцией результата.
Сразу за заголовком подпрограммы может следовать одна из стандартных директив ASSEMBLER, EXTERNAL, FAR, FORWARD, INLINE, INTERRUPT, NEAR. Эти директивы уточняют действия компилятора и распространяются на всю подпрограмму и только на нее, т.е. если за подпрограммой следует другая подпрограмма, стандартная директива, указанная за заголовком первой, не распространяется на вторую.
ASSEMBLER - эта директива отменяет стандартную последовательность машинных инструкций, вырабатываемых при входе в процедуру и перед выходом из нее. Тело подпрограммы в этом случае должно реализоваться с помощью команд встроенного ассемблера.
EXTERNAL - с помощью этой директивы объявляется внешняя подпрограмма.
FAR - компилятор должен создавать код подпрограммы, рассчитанный на дальнюю модель вызова.
NEAR заставит компилятор создать код, рассчитанный на ближнюю модель памяти. По умолчанию все подпрограммы, объявленные в интерфейсной части модулей, генерируются с расчетом на дальнюю модель вызова, а все остальные подпрограммы - на ближнюю модель.
В соответствии с архитектурой микропроцессора ПК, в программах могут использоваться две модели памяти: ближняя и дальняя. Модель памяти определяет возможность вызова процедуры из различных частей программы: если используется ближняя модель, вызов возможен только в пределах 64 Кбайт (в пределах одного сегмента кода, который выделяется основной программе и каждому используемому в ней модулю); при дальней модели вызов возможен из любого сегмента. Ближняя модель экономит один байт и несколько микросекунд на каждом вызове подпрограммы, поэтому стандартный режим компиляции предполагает эту модель памяти. Однако при передаче процедурных параметров, а также в оверлейных модулях соответствующие подпрограммы должны компилироваться с расчетом на универсальную - дальнюю - модель памяти, одинаково пригодную при любом расположении процедуры и вызывающей ее программы в памяти.
Явное объявление модели памяти стандартными директивами имеет более высокий приоритет по сравнению с опциями настройки среды Турбо Паскаля.
FORWARD - используется при опережающем описании (см. п.8.6) для сообщения компилятору, что описание подпрограммы следует где-то дальше по тексту программы (но в пределах текущего программного модуля).
INLINE - указывает на то, что тело подпрограммы реализуется с помощью встроенных машинных инструкций (см. п.11.2).
INTERRUPT - используется при создании процедур обработки прерываний (см. п.11.4).
Параметры
Список формальных параметров необязателен и может отсутствовать. Если же он есть, то в нем должны быть перечислены имена формальных параметров и их типы, например:
Procedure SB(a: Real; b: Integer; c: Char);
Как видно из примера, параметры в списке отделяются друг от друга точками с запятой. Несколько следующих подряд однотипных параметров можно объединять в подсписки, например, вместо
Function F(a: Real; b: Real): Real;
можно написать проще:
Function F(a,b: Real): Real;
Операторы тела подпрограммы рассматривают список формальных параметров как своеобразное расширение раздела описаний: все переменные из этого списка могут использоваться в любых выражениях внутри подпрограммы. Таким способом осуществляется настройка алгоритма подпрограммы на конкретную задачу.
Рассмотрим следующий пример. В языке Паскаль нет операции возведения в степень, однако с помощью встроенных функций LN(X) и ЕХР(Х) нетрудно реализовать новую функцию с именем, например, POWER, осуществляющую возведение любого вещественного числа в любую вещественную степень. В программе (пример 5.1) вводится пара чисел X и Y и выводится на экран дисплея результат возведения X сначала в степень +Y, а затем - в степень -Y. Для выхода из программы нужно ввести Ctrl-Z и Enter.
Пример 5.1
Program stepeny;
var х,у:Real;
Function Power (a, b : Real):Real; { параметры-значения }
begin {Power}
if a > 0 then Power := exp(b * Ln (a))
else
if a < 0 then Power := exp(b*Ln(abs(a)))
else
if b = 0 then Power := 1
else Power := 0
end {Power} ;
{-------------------}
begin {main}
readln(x,y) ;
writeln (Power (x,y) :12:2, Power (x, -y) : 15 : 4)
end.
Для вызова функции POWER мы просто указали ее в качестве параметра при обращении к встроенной процедуре WRITELN. Параметры X и Y в момент обращения к функции - это фактические параметры. Они подставляются вместо формальных параметров А и В в заголовке функции и затем над ними осуществляются нужные действия. Полученный результат присваивается идентификатору функции - именно он и будет возвращен как значение функции при выходе из нее. В программе функция POWER вызывается дважды - сначала с параметрами Х и Y, а затем Х и -Y, поэтому будут получены два разных результата.
Механизм замены формальных параметров на фактические позволяет нужным образом настроить алгоритм, реализованный в подпрограмме. Паскаль следит за тем, чтобы количество и тип формальных параметров строго соответствовали количеству и типам фактических параметров в момент обращения к подпрограмме. Смысл используемых фактических параметров зависит от того, в каком порядке они перечислены при вызове подпрограммы. В примере 5.1 первый по порядку фактический параметр будет возводиться в степень, задаваемую вторым параметром, а не наоборот. Пользователь должен сам следить за правильным порядком перечисления фактических параметров при обращении к подпрограмме.