Файл: Учебное пособие для студентов Авторы А. Н. Вальвачев, К. А. Сурков, Д. А. Сурков, Ю. М. Четырько Содержание Содержание 1.doc

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 04.05.2024

Просмотров: 133

Скачиваний: 4

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
Заголовок процедуры состоит из зарезервированного слова procedure, имени процедуры и необязательного заключенного в круглые скобки списка формальных параметров. Имя процедуры — это идентификатор, уникальный в пределах программы. Формальные параметры — это данные, которые вы передаете в процедуру для обработки, и данные, которые процедура возвращает (подробно параметры описаны ниже). Если процедура не получает данных извне и ничего не возвращает, формальные параметры (в том числе круглые скобки) не записываются. Тело процедуры представляет собой локальный блок, по структуре аналогичный программе:

procedure <имя процедуры> ( <список формальных параметров> ) ;

const ...;

type ...;

var ...;

begin

<операторы>

end;

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

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

<имя процедуры> ( <список фактических параметров> );

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

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

Приведем пример небольшой программы, использующей процедуру Power для вычисления числа X в степени Y. Результат вычисления процедура Power заносит в глобальную переменную Z.

program Console;
{$APPTYPE CONSOLE}
uses

SysUtils;
var

Z: Double;
procedure Power(X, Y: Double); // X и Y - формальные параметры

begin

Z := Exp(Y * Ln(X));

end;
begin

Power(2, 3); // 2 и 3 - фактические параметры

Writeln('2 в степени 3 = ', Z);

Writeln('Press Enter to exit...');

Readln;

end.

2.8.4. Функции программиста

Функции программиста применяются в тех случаях, когда надо создать подпрограмму, участвующую в выражении как операнд. Как и процедура, функция состоит из заголовка и блока.
Заголовок функции состоит из зарезервированного слова function, имени функции, необязательного заключенного в круглые скобки списка формальных параметров и типа возвращаемого функцией значения. Функции возвращают значения любых типов данных кроме Text и file of (см. файлы). Тело функции представляет собой локальный блок, по структуре аналогичный программе.

function <имя функции> ( <список формальных параметров> ): <тип результата>;

const ...;

type ...;

var ...;

begin

<операторы>

end;

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

В качестве примера заменим явно неуклюжую процедуру Power (см. выше) на функцию с таким же именем:

program Console;
{$APPTYPE CONSOLE}
uses

SysUtils;
function Power(X, Y: Double): Double; // X и Y - формальные параметры

begin

Result := Exp(Y * Ln(X));

end;
begin

Writeln('2 в степени 3 = ', Power(2, 3)); // 2 и 3 - фактические параметры

Writeln('Press Enter to exit...');

Readln;

end.

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

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

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

Входные параметры объявляются с помощью ключевого слова const; их значения не могут быть изменены внутри подпрограммы:

function Min(const A, B: Integer): Integer;

begin

if A < B then Result := A

else Result := B;

end;

Для объявления выходных параметров служит ключевое слово out:

procedure GetScreenResolution(out Width, Height: Integer);

begin

Width := GetScreenWidth;

Height := GetScreenHeight;

end;

Установка значений выходных параметров внутри подпрограммы приводит к установке значений переменных, переданных в качестве аргументов:

var

W, H: Integer;

begin

GetScreenResolution(W, H);

...

end;

После вызова процедуры GetScreenResolution переменные W и H будут содержать значения, которые были присвоены формальным параметрам Width и Height соответственно.

Если параметр является одновременно и входным, и выходным, то он описывается с ключевым словом
var:

procedure Exchange(var A, B: Integer);

var

C: Integer;

begin

C := A;

A := B;

B := C;

end;

Изменение значений var-параметров внутри подпрограммы приводит к изменению значений переменных, переданных в качестве аргументов:

var

X, Y: Integer;

begin

X := 5;

Y := 10;

...

Exchange(X, Y);

// Теперь X = 10, Y = 5

...

end;

При вызове подпрограмм на место out- и var-параметров можно подставлять только переменные, но не константы и не выражения.

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

function NumberOfSetBits(A: Cardinal): Byte;

begin

Result := 0;

while A <> 0 do

begin

Result := Result + (A mod 2);

A := A div 2;

end;

end;

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

Разные способы передачи параметров (const, out, var и без них) можно совмещать в одной подпрограмме. В следующем законченном примере процедура Average принимает четыре параметра. Первые два (X и Y) являются входными и служат для передачи исходных данных. Вторые два параметра являются выходными и служат для приема в вызывающей программе результатов вычисления среднего арифметического (M) и среднего геометрического (P) от значений X и Y:

program Console;
{$APPTYPE CONSOLE}
uses

SysUtils;
procedure Average(const X, Y: Double; out M, P: Double);

begin

M := (X + Y) / 2;

P := Sqrt(X * Y);

end;
var

M, P: Double;
begin

Average(10, 20, M, P);

Writeln('Среднее арифметическое = ', M);

Writeln('Среднее геометрическое = ', P);

Writeln('Press Enter to exit...');

Readln;

end.

Существует разновидность параметров без типа. Они называются нетипизированными и предназначены для передачи и для приема данных любого типа. Нетипизированные параметры описываются с помощью ключевых слов const и var, при этом тип данных опускается:

procedure JustProc(const X; var Y; out Z);

Внутри подпрограммы тип таких параметров не известен, поэтому программист должен сам позаботиться о правильной интерпретации переданных данных. Заметим, что при вызове подпрограмм на место нетипизированных параметров (в том числе и на место нетипизированных const-параметров) можно подставлять только переменные.


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

Ключевое слово

Назначение

Способ передачи

<отсутствует>

Входной

Передается копия значения

const

Входной

Передается копия значения либо ссылка на значение в зависимости от типа данных

out

Выходной

Передается ссылка на значение

var

Входной и выходной

Передается ссылка на значение

Таблица 2.10. Способы передачи параметров

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

2.8.6. Опущенные параметры процедур и функций

В языке Delphi существует возможность задать параметрам процедур и функций стандартные значения. Они указываются через знак равенства после типа параметра. Например, опишем процедуру, которая заполняет некоторую область памяти заданным значением:

procedure Initialize(var X; MemSize: Integer; InitValue: Byte = 0);

Для параметра InitValue задано стандартное значение, поэтому его можно опустить при вызове процедуры Initialize:

Initialize(MyVar, 10); // Эквивалентно Initialize(MyVar, 10, 0);

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

procedure Initialize(var X; InitValue: Byte = 0; MemSize: Integer); // Ошибка!

2.8.7. Перегрузка процедур и функций


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

procedure IncrementInteger(var Value: Integer);

procedure IncrementReal(var Value: Real);

В языке Delphi существует возможность дать двум и более процедурам (функциям) одинаковые идентификаторы при условии, что все такие процедуры (функции) отличаются списком параметров. Такая возможность называется перегрузкой. Для указания того, что процедура (функция) перегружена, служит стандартная директива overload. С ее помощью вышеприведенный пример можно переписать следующим образом:

procedure Increment(var Value: Integer); overload; // процедура 1

procedure Increment(var Value: Real); overload; // процедура 2

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

var

X: Integer;

Y: Real;

begin

X:=1;

Y:=2.0;

Increment(X); // Вызывается процедура 1

Increment(Y); // Вызывается процедура 2

end.

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

procedure Print(X: Shortint); overload; // процедура 1

procedure Print(X: Longint); overload; // процедура 2

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

Print(5); // Вызывается процедура 1

Print(150); // Вызывается процедура 2

Print(-500); // Вызывается процедура 2

Print(-1); // Вызывается процедура 1

Очевидно, что одно и то же число может интерпретироваться и как Longint, и как Shortint (например, числа 5 и –1). Логика компилятора в таких случаях такова: если значение фактического параметра попадает в диапазон значений нескольких типов, по которым происходит перегрузка, то компилятор выбирает процеудуру (функцию), у которой тип параметра имеет меньший диапазон значений. Например, вызов Print(5) будет означать вызов того варианта процедуры, который имеет тип параметра Shortint. А вот вызов Print(150) будет означать вызов того варианта процедуры, который имеет тип параметра Longint, т.к. число 150 не вмещается в диапазон значений типа данных Shortint.

Поскольку в нынешней версии среды Delphi обощенный тип данных Integer совпадает с фундаментальным типом данных Longint, следующий вариант перегрузки является ошибочным:

procedure Print(X: Integer); overload;