Листинг 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;{Описание переменной, в которой будет храниться структура, предназначенная для сохранения)