Листинг 4.3. Передача параметров по значению

Program ChangingParamsl ; Var

I: Integer; Procedure

{Описание целочисленной переменной} Proc (A: Integer);

{Описание процедуры Proc с одним параметром, передаваемым по значению}

Begin

А := 154,

end; Begin I := 200; Proc (I) ;

Proc (80);

End.

{Изменение значения параметра, переданного в процедуру. Никаких изменений данных вызывающего фрагмента не происходит}

{Начало раздела описания логики программы}

{Изменение значение переменной 1}

{Вызов процедуры Proc, в качестве параметра

передается значениепеременной I, после

окончания работы процедуры значение

переменной сохраняется}

{Вызов процедуры Proc, в качестве параметра

передается целочисленная константа 80, что

допустимо при передаче параметров по значению}

Передача параметров по ссылке. Параметры-переменные

Второй механизм передачи параметров — по ссылке — подразумевает возможность изменения подпрограммой данных вызывающего фрагмен­та программы. Для этого в качестве параметра вызывающий фрагмент должен использовать переменную, адрес которой будет передан в под­программу через стек (см. рис. 4.2). Далее подпрограмма обеспечивает доступ к переданному адресу по имени параметра. Соответственно, из­менения, производимые с параметром в подпрограмме, влияют на ту переменную, которая указана в качестве параметра.

Рис. 4.2. Механизм передачи параметров по ссылке

Для указания компилятору на необходимость передачи параметра по ссылке перед описанием соответствующего параметра указывается спе­циальный модификатор параметра — ключевое слово Var(от англ. Vatriable — переменный):

Procedure(..., Var<Параметр>: <Тип параметра>, ...);

Так как при использовании параметров-переменных в подпрограмму должен быть передан некоторый адрес, то в качестве параметра, передаваемого по ссылке, должны использоваться только переменные. Изме­ним предыдущий пример таким образом, чтобы параметр передавался в процедуру по ссылке (см. листинг 4.4).

Листинг 4.4. Передача параметров по ссылке

Program ChangingParams2;

Var

I: Integer;{Описание целочисленной переменной}

Procedure Proc (Var A: Integer);

{Описание процедуры Proc с одним параметром, передаваемым по ссылке}

Begin

 

А := 154; {Изменение значения параметра, переданного в про­цедуру. Одновременно с этим изменяется значение переменной, переданной в качестве параметра}

End;

Begin{Начало раздела описания логики программы}

I := 200;{Изменение значение переменной 1}

Proc(I); {Вызов процедуры Proc, в качестве параметра передается адреспеременной I, после окончания работы процедуры, значение переменной изменится и станет равным 154}

 

End.

Параметры, передаваемые по ссылке, называют параметрами-переменны­ми (от англ. Variable parameter — параметр-переменная).

4.2.3. Частные случаи передачи параметров подпрограммам

Параметры по умолчанию

Особенностью Delphi относительно Pascal является возможность исполь­зования параметров по умолчанию. Такие параметры находятся в конце списка параметров и их описания имеют следующий вид:

<Параметр>: <Тип параметра> = <Значение умолчанию>

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

Листинг 4.5. Использование параметров по умолчанию

ProgramUsingFunctions;

Var

s String;{Описание строковой переменной S}

Function GetFullName SurName: String;

FirstName: String = 'имя не введено';

SecondName: String= 'отчество не введено' : String;{Описание функции GetFullame с одним обязательным параметром и двумя параметрами, заданными по умолчанию}

 

Begin

Result:= SurName + ' ' +

FirstName + ' ' + SecondName;

{В качестве результата функция возвращает сумму параметров. Если при вызове не задан последний параметр, то вместо него подставляется строка 'отчество не введено', если не задан еще и предпоследний параметр, то вместо него подставляется 'имя не введено'}

End

Begin {Начало раздела описания логики программы}

s= GetFullName('Иванов');

{В переменной S будет значение 'Иванов

имя не введено отчество не введено'}

s: = GetFullName('Иванов' , 'Иван') ;

{В переменной S будет значение 'Иванов

Иван отчество не введено'}

s:= GetFullName('Иванов' , 'Иван', 'Иванович');

{В переменной S будет значение 'Иванов

Иван Иванович' }

End.

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

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

Для начала продемонстрируем невозможность изменения самой ссылки:

Procedure ChangeParam (P: ^Double); Begin

New(P); End;

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

Теперь продемонстрируем возможность изменения информации, на ко­торую ссылается переданный параметр:

Procedure ChangeParam (P: ^Double) ;

Begin

Р^:= 150; {Изменение информации в памяти, на которую указывает параметр, влияет на данные вызывающего фрагмента}

New(P); {Выделение памяти под параметр Р не приводит к изменению адреса в памяти, который является значением переменной, переданной в качестве параметра. Память выделяется под локальную копию параметра}

Р^ := 320; {Изменение информации в памяти, на которую указывает параметр, не влияет на данные вызывающего фрагмента, так как значение параметра уже изменено}

Dispose (P);{Освобождение памяти, выделенной под локальную копию параметра}

End;

Изменим процедуру, сделав параметр передаваемым по ссьлке (листинг 4.6).

Листинг 4.6. Передача по значению параметров ссылочных типов данных

Procedure ChangeParam (Var P:^Double); Begin

Р:= 150;{Изменение информации в памяти, на которую

указывает параметр, влияет на данные

вызывающего фрагмента}

New(P) ; {Выделение памяти под параметр Р приводит

к потере ссылки на область памяти,

находящейся в параметре при его передаче.

Память выделяется под переменную, заданную

в качестве параметра}

P^ := 320; {Изменение информации в памяти, на которую

указывает параметр, влияет на данные

вызывающего фрагмента}

Dispose(P); {Освобождение памяти, выделенной под

переменную, заданную в качестве параметра}

End;

Аналогична ситуация с параметрами — динамическими массивами, зна­чения элементов которых также может изменить подпрограмма, так как ей передается не копия массива, а ссылка на него", даже если параметр не помечен в описании подпрограммы модификатором Var.

Параметры-константы

Для параметров, передаваемых по значению, предусмотрен модификатор Const,запрещающий изменять значение даже локальной копии данного параметра и передавать его как параметр-переменную в другие подпрог­раммы. Модификатор предназначен для указания компилятору Delphi на неизменяемый характер параметра и позволяет ему создавать более оп­тимальный программный код при использовании строковых параметров и параметров сложных типов данных.

Procedure (..., Const <Параметр>: <Тип параметра>, ...);

Таким образом, указание в процедуре Ргос из предыдущего примера модификатора Const вместо модификатора Var приведет к ошибке ком­пиляции с сообщением "Left side cannot be assigned to" - "Ле­вая часть выражения не может быть изменена".

Параметры, описанные с модификатором Const, называются параметра­ми-константами.Пример использования таких параметров приведен в листинге 4.7.

Листинг 4.7. Использование параметров-констант

Procedure OpenFile(Const: FileName: AnsiString);

{Заголовок процедуры OpenFile, строковый параметр FileName передается как константа]

Var

sLocal: AnsiString;

{Описание локальной переменной}

Begin

FileName := 4heFile.txt';

{Операция недопустима, так как параметр FileName помечен модификатором Const}

sLocal := FileName;{Операция возможна}

End;

Параметры для заполнения

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

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

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

Для описания параметров для заполнения используется модификатор Out:

Procedure(..., Out<Параметр>: <Тип параметра>, ...);

Пример использования параметров для заполнения приведен в лис­тинге 4.8.

Параметры без указания типа

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

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

Procedure SaveToFile(Buffer: Pointer; Size: Integer);

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

Туре

TRec = Record S: String; I: Integer; end;{Описание типа данных, используемого

для хранения информации)

Var

Rec: Tree;{Описание переменной, в которой будет храниться структура, предназначенная для сохранения)