Подпрограммы: процедуры и функции | Язык Паскаль

Процедуры и функции - создание подпрограмм в Pascal

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

Зачем нужны подпрограммы? Их использование удобно, когда в программе несколько раз решается одна и та же подзадача, но для разных наборов данных. Кроме того, использование подпрограмм естественно для человека, которому проще воспринимать логически связанные объекты, чем множество разнородных данных.

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

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

procedure имя (параметры);

function имя (параметры): тип результата;

Между функциями и процедурами есть существенное отличие. Значение, полученное в результате выполнения кода функции, жестко соотносится с ее именем путем присвоения этому имени конкретного значения. Тип, который может принять вычисляемое значение, указывается в заголовке функции (тип результата).

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

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

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

Примеры определения процедуры и ее вызововы из основной программы:

procedure box (s: char; w, h: integer);
    var i,j:integer;
    begin
        for i := 1 to h do begin
            for j := 1 to w do
                write(s);
            writeln
        end;
    end;
 
begin

    box ('+', 10, 5);

    box ('$', 20, 3);
 
end.

Результат выполнения программы:

++++++++++
++++++++++
++++++++++
++++++++++
++++++++++
$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$

Примеры определения функции и ее вызовов из основной программы:

var num: longint;
 
function digits (n: longint): byte;
    var i: byte;
    begin
        i := 0;
        while n > 0 do begin
            n := n div 10;
            i := i + 1
        end;
 
        digits := i
    end;
 
begin
    write('Введите положительное число: ');
    readln(num);
 
    num := digits(num);

    writeln ('Количество разрядов = ', num); 
end.

Результат выполнения программы:

Введите положительное число: 346114
Количество разрядов = 6

Формальные и фактические параметры

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

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

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

Соответствие между фактическими и формальными параметрами должно быть следующим:
а) число фактических параметров должно быть равно числу формальных параметров;
б) соответствующие фактические и формальные параметры должны совпадать по порядку следования и по типу.

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

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

Параметры-значения

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

Если фактический параметр есть имя переменной, например, r, то значение этой переменной пересылается в соответствующий формальный параметр, например, a. На этом всякая связь между a и r обрывается.

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

var
    i: integer;
 
procedure p(i: integer);
    begin
        i := i * 2
    end;
 
begin
    i := 2;
    p(i);
    writeln(' i = ', i);
end.

В программе происходит засылка числа 2 в ячейку, отведенную для переменной i, затем идет обращение к процедуре p с фактическим параметром i = 2. При этом значение 2 пересылается в другую ячейку, отведенную для формального параметра i. В этой ячейке после выполнения оператора i := i * 2 появляется число 4. Но после возврата из процедуры на оператор writeln программа "знает" только одну переменную i, которая по-прежнему содержит число 2. Поэтому программа выведет i = 2.

Если формальный параметр есть параметр-значение, то соответствующим фактическим параметром должно быть выражение того же типа, что и формальный параметр.

Параметры-переменные

Если перед именем формального параметра стоит ключевое слово var, то такой параметр есть параметр-переменная. Примерами таких параметров служат x1 и x2 в заголовке:

procedure sq(a, b, c: real; var x1, x2: real);

Фактический параметр, соответствующий параметру-переменной, может быть только переменной (не константой и не выражением).

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

Через параметр-переменную можно передать результат выполнения процедуры:

var
    a, b: integer;
 
procedure h(x: integer; var y: integer);
    begin
        x := x + 1;
        y := y + 1;
        writeln(x, y)
    end;
 
begin
    a := 0;
    b := 0;
    h(a, b);
    writeln(a, b);
end.

Результаты, выдаваемые процедурой h – это 11; основная ветка программы выводит 01.

Разберем пример. Фактический параметр a соответствует формальному параметру-значению x, а фактический параметр b – формальному параметру-переменной y. Под параметры a и x отведены две разные ячейки памяти, под b и y – одна и та же ячейка.

При вызове h(a, b) из ячейки a пересылается значение 0 в ячейку x, а в ячейку y засылается адрес ячейки b, содержащей 0, т.к. в процедуре h параметр x – это параметр-значение, а y – параметр-переменная.

При выполнении оператора x := x + 1 в ячейку x прибавляется 1 и в ячейке x окажется 1, а в ячейке a по-прежнему 0.

Выполнение оператора y := y + 1 имеет следующий смысл: "взять число из ячейки, адрес которой находится в y (т.е. из ячейки b), прибавить 1 и заслать в ту же ячейку (т.е. в b).

Поэтому в результате выполнения оператора y := y + 1 значение ячейки b станет 1. Оператор печати из процедуры выдаст содержимое ячейки x и ячейки y, т.е. 1 и 1. Оператор печати в программе напечатает содержимое a, которое осталось равным 0, и содержимое ячейки b, которое теперь равно 1.

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

Если в процедуре p есть обращение к процедуре q, описанной ниже, то перед описанием p процедура q декларируется как forward: после заголовка процедуры q ставится двоеточие, а затем ключевое слово forward. В этом случае параметры процедуры описываются только в операторе с forward. В заголовке самой процедуры параметры опускаются.

procedure q(x: t1): forward;
 
procedure p(x: y);
    begin
        q(a)
    end;
 
procedure q;
    begin
        p(b)
    end;