Средства организации модульности в языках высокого уровня

С увеличением объема программы становится невозможным удерживать в памяти все детали. Естественным способом борьбы со сложностью любой задачи является ее разбиение на части. В языках высокого уровня задача может быть разделена на более простые и обозримые с помощью подпрограмм, после чего программу можно рассматривать в более укрупненном виде — на уровне их взаимодействия.

Разделение программы на подпрограммы позволяет также избежать избыточности кода, поскольку подпрограмму записывают один раз, а вызывать ее на выполнение можно многократно из разных точек программы.

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

При проектировании программы часто применяется технология нисходящего проектирования, основная идея которого состоит в разбиении задачи на подзадачи меньшей сложности, пригодные для рассмотрения по отдельности. Эта технология кратко описана в гл. 6.

Подпрограммы нужны, для того Подпрограммы

чтобы упростить структуру программы -

и облегчить ее отладку. В виде подпрограмм оформляются логические законченные части программы.

Подпрограмма — это фрагмент кода, к которому можно обратиться по имени. Она описывается один раз, а вызываться может столько раз, сколько необходимо. Одна и та же подпрограмма может обрабатывать различные данные, переданные ей в качестве аргументов.

В ПАСКАЛЕ два вида подпрограмм: процедуры и функции. Они имеют незначительные отличия в синтаксисе и правилах вызова. Процедуры и функции описываются в соответствующих разделах описания, до начала блока исполняемых операторов.

Само по себе описание не приводит к выполнению подпрограммы. Для того чтобы подпрограмма выполнилась, ее надо вызвать. Вызов записывается в том месте программы, где требуется получить результаты работы подпрограммы. Подпрограмма вызывается по имени, за которым следует список аргументов в круглых скобках. Если аргументов нет, скобки не нужны. Список аргументов при вызове как бы накладывается на список параметров, поэтому они должны попарно соответствовать друг другу.

Процедура вызывается с помощью отдельного оператора, а функция — в правой части оператора присваивания, например: inc (i); writeln (а, Ь, с); {вызовы процедур} у:= sin (х) + 1; {вызов функции}

Внутри подпрограмм можно описывать другие подпрограммы.

Они доступны только из той подпрограммы, в которой они описаны. Рассмотрим правила описания подпрограмм.

Процедуры Структура процедуры аналогична струк-

- туре основной программы:

procedure имя [(список параметров)]; {заголовок} разделы описаний begin

раздел операторов end;

Пример

Найти разность средних арифметических значений двух вещественных массивов из 10 элементов.

Как очевидно из условия, для двух массивов требуется найти одну и ту же величину — среднее арифметическое. Следовательно, логичным будет оформить его нахождение в виде подпрограммы, которая сможет работать с разными массивами.

program dif_average; const n = 10;

type mas = array [1.. n] of real; var a, b: mas; i: integer;

dif, av_a, av_b: real;

procedure average (x: mas; var av: real); {1}

var i: integer; begin av:= 0;

for i:= 1 to n do av:= av + x [i]; av:= av / n;

end; {2}

begin

for i:= 1 to n do read (a [i]); for i:= 1 to n do read (b [i]);

average (a, av_a); {3}

average (b, av_b); {4}

dif:= av_a — av_b;

writeln ('Разность значений dif:6:2) end.

Описание процедуры average расположено в строках с {1} по {2}.

В строках {3} и {4}, эта процедура вызывается сначала для обработки массива а, затем — массива Ь. Эти массивы передаются в качестве аргументов. Результат вычисления среднего арифметического возвращается в главную программу через второй параметр процедуры.

Функции

Описание функции отличается от описания процедуры незначительно:

function имя [(список параметров)]: тип; {заголовок} разделы описаний begin

раздел операторов имя:= выражение; end;

Функция вычисляет одно значение, которое передается через ее имя. Следовательно, в заголовке должен быть описан тип этого значения, а в теле функции — оператор, присваивающий вычисленное значение ее имени.

Пример

Найти разность средних арифметических значений двух вещественных массивов из 10 элементов, program dif_averagel; const n = 3;

type mas = array [1.. n] of real; var

a, b : mas;

i : integer; dif : real;

function average (x: mas): real; {1}

var i: integer; {2}

av : real;

begin

av:= 0;

for i:= 1 to n do av:= av + x [i];

average:= av / n; {3}

end;

begin

for i:= 1 to n do read (a [i]); for i:= 1 to n do read (b [i]);

dif:= average (a) — average (b); {4}

writeln ('Разность значений dif:6:2) end.

Оператор, помеченный комментарием {1}, представляет собой заголовок функции. Тип функции определен как вещественный, потому что такой тип имеет среднее арифметическое элементов вещественного массива. Оператор {3} присваивает вычисленное значение имени функции. В операторе {4} функция вызывается дважды: сначала для одного массива, затем для другого.

Глобальные и локальные В ШМ PC-совместимых компьютерах переменные память условно разделена на так назы-

ваемые сегменты. Адрес каждого байта составляется из номера сегмента и смещения относительно его начала. Компилятор ПАСКАЛЯ формирует сегмент кода, в котором хранится программа в виде машинных команд, сегмент данных, в котором выделяется память под глобальные переменные программы, и сегмент стека, предназначенный для размещения локальных переменных во время выполнения программы (рис. 4.6).

Глобальными называются переменные, описанные в главной программе. Переменные, которые не были инициализированы явным образом, перед началом выполнения программы обнуляются. Время жизни глобальных переменных — с начала программы и до ее завершения. Глобальные переменные доступны в любом

Структура исполняемой программы в оперативной памяти месте программы или подпрограммы, кроме тех подпрограмм, в которых описаны локальные переменные с такими же именами

Рис. 4.6. Структура исполняемой программы в оперативной памяти месте программы или подпрограммы, кроме тех подпрограмм, в которых описаны локальные переменные с такими же именами.

Внутри подпрограмм описываются локальные переменные. Они располагаются в сегменте стека, причем распределение памяти происходит в момент вызова подпрограммы, а ее освобождение — по завершении подпрограммы. Значения локальных переменных между двумя вызовами одной и той же подпрограммы не сохраняются, и эти переменные предварительно не обнуляются. Локальные переменные могут использоваться только в подпрограмме, в которой они описаны, и всех вложенных в нее.

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

ВНИМАНИЕ

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

Список параметров, т.е. величин, Виды параметров

передаваемых в подпрограмму и обратно, подпрограмм

содержится в ее заголовке. Для каждого

параметра обычно задается его имя, тип и способ передачи. Либо тип, либо способ передачи могут не указываться.

Важно запомнить, что в заголовке подпрограммы нельзя вводить описание нового типа, там должны использоваться либо имена стандартных типов, либо имена типов, описанных программистом ранее в разделе type.

Основными видами параметров ПАСКАЛЯ являются параметры-значения, параметры-переменные и параметры-константы.

Параметры-значения. Параметр-значение описывается в заголовке подпрограммы следующим образом: имя: тип;

Например, передача в процедуру Р величины целого типа записывается так:

procedure Р (х: integer);

Имя параметра может быть произвольным. Параметр х можно представить себе как локальную переменную, которая получает свое значение из главной программы при вызове подпрограммы. В подпрограмму передается копия значения аргумента.

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

При вызове подпрограммы на месте параметра, передаваемого по значению, может находиться выражение. Тип выражения должен быть совместим по присваиванию с типом параметра.

Например, если в вызывающей программе описаны переменные

var х: integer; с: byte; у: longint;

то следующие вызовы подпрограммы Р, заголовок которой описан ранее, будут синтаксически правильными:

Недостатками передачи по значению являются затраты времени на копирование параметра, затраты памяти в стеке и опасность его переполнения, когда речь идет о параметрах, занимающих много места, например массивах большого размера. Поэтому более правильно использовать для передачи в подпрограмму исходных данных параметры-константы.

 
Посмотреть оригинал
< Пред   СОДЕРЖАНИЕ   ОРИГИНАЛ     След >