ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



 

Часть 10

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

     Описание процедуры позволяет связать идентификатор с процедурным блоком. Процедуру можно затем активизировать с помощью оператора процедуры. 
             ЪДДДДДДДДДДДї  ХНё  ЪДДДДДДДДДДДДДДДДї  ХНё
 описание ДДі заголовок ГДі;ГДі тело процедуры ГДі;ГДДДДДДД
 процедуры   і процедуры і  ФНѕ  АДДДДДДДДДДДДДДДДЩ  ФНѕ
             АДДДДДДДДДДДЩ

             ХНННННННННННё  ЪДДДДДДДДДДДДДї
 заголовок -і procedure ГДіидентификаторГДДВДДДДДДДДДДДДДДДДД
 процедуры   ФНННННННННННѕ  АДДДДДДДДДДДДДЩ  і            
                                     ЪДДДДДДДЩ            і
                                     і ЪДДДДДДДДДДДДДДДДї і
                                     і і     список     і і
                                     АДґ   формальных   іДЩ
                                       і   параметров   і
                                       АДДДДДДДДДДДДДДДДЩ
                       ЪДДДДДДДДДДДДДДДДДДДДї
 тип параметра ДДДДДДДі идентификатор типа ГДДДДВДДДД
                      АДДДДДДДДДДДДДДДДДДДДЩ    і
                 і           ХННННННННё          і
                 АДДДДДДДДДДДґ строка іДДДДДДДДДЩ
                             ФННННННННѕ

                                             ЪДДДДДДї
   тело     ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДВДі блок іДДДДДД
 процедуры  і                            і  АДДДДДДЩ  
            і   ХННННННННННННё    ХНННё і і            і
            ГДДі interrupt  ГДДДі ; ГДЩ і            і
            і   ФННННННННННННѕ   ФНННѕ   і            і
            і   ХННННННННННННё і          і            і
            ГДДі    near    ГДґ          і            і
            і   ФННННННННННННѕ і          і            і
            і   ХННННННННННННё і          і            і
            ГДДі    far     ГДґ          і            і
            і   ФННННННННННННѕ і          і            і
            і   ХННННННННННННё і          і            і
            ГДДі  export    ГДЩ          і            і
            і   ФННННННННННННѕ            і            і
            і            ЪДДДДДДДДДДДДДДДДЩ            і
            і            і  ХНННННННННё                і
            і            ГДі forward ГДДДДДДДДДДДДДДДі
            і            і  ФНННННННННѕ                і
            і            і  ХННННННННННё               і
            і            ГДі external ГДДДДДДДДДДДДДДі
            і            і  ФННННННННННѕ               і
            і            і   ЪДДДДДДДДДДДДДДї          і
            і            АДДі   блок asm   ГДДДДДДДДДі
            і                АДДДДДДДДДДДДДДЩ          і
            і     ХННННННННННННННННННННННННННё         і
            АДДДДі     директива inline     ГДДДДДДДДДЩ
                  ФННННННННННННННННННННННННННѕ

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

     procedure NumString(N: integer; var S: string);
     var
       V: integer;
     begin
       V := Abs(N);
       S := '';
       repeat
         S := Chr(N mod 10 + Ord('0')) + S;
         N := N div 10;
       until N = 0;
       if N < 0 then S := '-' + S;
     end;

                       Описания near и far

     Турбо Паскаль поддерживает две модели вызова процедур - ближнюю (near) и дальнюю (far). С точки зрения объема программы и скорости выполнения ближняя модель вызова более эффективна, но с ней связаны ограничения: процедуры типа near могут вызываться 
только в том модуле, где они описаны. Процедуры же с дальним типом вызова можно вызывать из любого модуля, но они несколько менее эффективны. 
     Примечание: О вызовах ближнего и дальнего типа рассказывается в Главе 18 "Вопросы управления". 
     На основе описания процедуры Турбо Паскаль будет автоматически выбирать правильную модель вызова. Для процедур, описанных в интерфейсной части модуля (interface), используется дальняя модель вызова - их можно вызывать из других модулей. Процедуры, о
писанные в секции реализации модуля (implementation), имеют ближний тип вызова. Вызываться они могут только из программ данного модуля. 
     Для некоторых специальных целей может потребоваться использовать модель с дальним типом вызова. Например, в оверлейных задачах обычно требуется, чтобы все процедуры и функции имели дальний тип вызова. Аналогично, если процедура или функция присваива
ется процедурной переменной, то она также должна использовать дальний тип вызова. Чтобы переопределить автоматический выбор модели вызова компилятором, можно использовать директиву компилятора {$F+}. Процедуры и функции, компилируемые в состоянии {$F+}, 
всегда будут иметь дальний тип вызова (far), а в состоянии {$F-} компилятор автоматически выбирает корректную модель. По умолчанию используется директива {$F-}. 
     Чтобы задать конкретную модель вызова, в описании процедуры перед ее блоком можно указать директиву near или far. При наличии такой директивы она переопределяет директиву $F компилятора и автоматический выбор модели вызова. 
                      Описания export

     Директива export делает процедуру или функцию "экспортируемой", принудительно используя дальний тип вызова и подготавливая подпрограмму для экспорта путем генерации специального кода входа и выхода. 
     В Windows требуется, чтобы процедуры и функции были экспортируемыми в следующих случаях:
     - процедуры и функции, которые экспортируются DLL (динамически компонуемой библиотекой);
     - процедуры и функции вызова.

     В Главе 10 "Динамически компонуемые библиотеки" описывается, как можно экспортировать процедуры и функции в DLL. Заметим, что даже если процедура или функция компилируется с директивой export, фактический экспорт процедуры или функции не происходит 
до тех пор, пока подпрограмма не указывается в операторе exports библиотеки. 
     Процедуры и функции вызова - это те процедуры и функции в вашей прикладной программе, которые вызываются не вашей прикладной программой, а Windows. Процедуры и функции вызова компилируются с директивой export, но их не нужно указывать в операторе ex
ports. Приведем некоторые общие примеры процедур и функций вызова:
     - процедуры Windows;
     - диалоговые процедуры;
     - перечислимые процедуры вызова;
     - процедуры описания памяти;
     - специальные процедуры Windows (фильтры).

                       Описания interrupt

     В описании процедуры перед блоком операторов может указыватся директива прерывания (interrupt). Процедура в этом случае рассматривается, как процедура прерывания. 
     Примечание: Полное описание процедур прерывания приводится в Главе 18 "Вопросы управления". 
     В данный момент отметим, что процедуры обработки прерывания нельзя вызывать с помощью операторов процедуры, и что в каждой из них задается список параметров, который обязательно должен иметь следующий вид: 
     procedure MyInt(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP :
                     word);
     interrupt;

     Вместо блока операторов в описании процедуры или функции можно записать опережающее описание (описание forward), внешнее описание (описание external) или описание inline. 
                        Описание forward

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

     procedure Walter(m,n : integer); forward;
     procedure Clara(x,y : real);
     begin
      .
      .
      .
     end;
     procedure Walter;
     begin
      .
      .
      Clara(8.3,2.4);
      .
      .
     end;

     Определяющее описание процедуры может быть внешним описанием (external). Однако, оно не может быть описанием inline или другим опережающим описанием. Определяющее описание также не может содержать директиву interrupt. 
     Опережающие описания не допускаются в интерфейсной части модуля. 
                        Описания external

     Описания external позволяют компоновать отдельно скомпилированные процедуры и функции, написанные на языке Ассемблера. С помощью команды "$L имя_файла" для внешней программы должны быть установлены связи с программой или модулем на языке Паскаль. 
     Примечание: Более детальное описания компоновки с программой на языке Ассемблера содержится в Главе 23. 
     Приведем следующие примеры описаний внешних процедур:

     procedure MoveWord(var source,dest; count: longint);
       external;

     procedure MoveLong(var source,dest; count: longint);
       external;

     procedure FillWord(var dest,data: integer; count: longint);
       external;

     procedure FillLong(var dest,data: integer; count: longint);
       external;

     {$L BLOCK.OBJ}

     Директиву external можно также использовать для импорта процедур и функций из DLL (динамически компонуемой библиотеки). Например, слкедующее описание external импортирует функцию с именем GlobalAlloc из библиотеки DLL с именем 'KERNEL' (ядро Windows
). 
     function GlobalAlloc(Flags: Word; Bytes: LongInt:
     THandle; far; external 'KERNEL' index 15;

     Директива external занимает место описания и операторной части импортируемой процедуры или функции. Импортируемые процедуры и функции должны использовать дальний тип вызова, задаваемый директивой far или директивой компилятора {$F}. 
     Кроме этого требования импортируемые процедуры и функции аналогичны обычным процедурам и функциям. 
     Примечание: Более подробную информацию об импортировании процедур и функций из библиотек DLL можно найти в Главе 10 "Динамически компонуемые библиотеки". 
                       Описания assembler

     Описания assembler позволяют вам написать всю процедуру или функцию на Ассемблере. 
     Примечание: Более подпробно о процедурах и функциях на Ассемблере рассказывается в Главе 23 "Встроенный Ассемблер". 
            ХНННННННННё  ХНё  ЪДДДДДДДДДДДДДДДї   ЪДДДДДДДДДДДДї
 блок asm ДДґassemblerГДі;ГДіраздел описанийГДДіоператор asmГД
            ФНННННННННѕ  ФНѕ  АДДДДДДДДДДДДДДДЩ   АДДДДДДДДДДДДЩ

                         Описания inline

     Директивы inline позволяют записывать вместо блока операторов инструкции в машинном коде. При вызове обычной процедуры компилятор создает код, в котором параметры процедуры помещаются в стек, а затем для вызова процедуры генерируется инструкция CALL
. Когда вы вызываете процедуру inline, компилятор генерирует код с помощью директивы inline, а не с помощью инструкции CALL. Таким образом, процедура inline "расширяется" при каждом обращении к ней, аналогично макроинструкции на языке Ассемблера. Приведе
м два небольших примера процедур типа inline: 
     procedure DisableInterrupts: inline($FA);  { CLI }
     procedure EnableInterrupts;  inline($FB);  { STI }

     Примечание: Процедуры типа inline описаны подробно в Главе 22 "Встроенный Ассемблер". 
                        Описания функций

     Описание функции определяет часть программы, в которой вычисляются и возвращается значение. 
            ЪДДДДДДДДДДДї  ХНё   ЪДДДДДДДДДДДДї   ХНё
 описаниеДДі заголовок ГДі;ГДДітело функцииіДДі;ГДДДДДДДДДДД
  функции   і  функции  і  ФНѕ   АДДДДДДДДДДДДЩ   ФНѕ
            АДДДДДДДДДДДЩ
             ХННННННННННё  ЪДДДДДДДДДДДДДДДї
 заголовок -і function ГДі идентификатор ГДВДДДДДДДДДДДДДї
  функции    ФННННННННННѕ  АДДДДДДДДДДДДДДДЩ і            і
                                        ЪДДДДЩ           і і
                                        і ЪДДДДДДДДДДДДї і і
                                        і і   список   і і і
                                        Аі формальных ГДЩ і
                                          і параметров і   і
                                          АДДДДДДДДДДДДЩ   і
               ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ
               і  ХНННё     ЪДДДДДДДДДДДДДДДДДДДДї
               АДі : ГДДДДі   тип результата   ГДДДДДДДДДДДДДД
                  ФНННѕ     АДДДДДДДДДДДДДДДДДДДДЩ

                            ЪДДДДДДДДДДДДДДДДДДДДї
 тип результата ДДДДДДВДДДДі идентификатор типа ГДДДДДДВДДДДДДД
                      і     АДДДДДДДДДДДДДДДДДДДДЩ      і
                      і          ХННННННННё             і
                      АДДДДДДДДДі строка ГДДДДДДДДДДДДДЩ
                                 ФННННННННѕ

                                                     ЪДДДДДДї
 тело функции ДДДДВДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДВДі блок ГДДДД
                  і                              і  АДДДДДДЩ  
                  і   ХННННННё            ХНё   і і            і
                  ГДДі near ГДДДДДДДДДДДі;ГДДДЩ і            і
                  і   ФННННННѕ           ФНѕ     і            і
                  і   ХННННННё      і             і            і
                  ГДДі far  ГДДДДДДґ             і            і
                  і   ФННННННѕ      і             і            і
                  і   ХННННННё      і             і            і
                  ГДДіexportГДДДДДДЩ             і            і
                  і   ФННННННѕ                    і            і
                  і                               і            і
                  і   ЪДДДДДДДДДДДДДДДДДДДДДДДДДДДЩ            і
                  і   і                                        і
                  і   і     ХНННННННННё             ЪДДДДДДДДДДЩ
                  і   ГДДДДі forward ГДДДДДДДДДДДДі
                  і   і     ФНННННННННѕ             і
                  і   і     ХННННННННННё            і
                  і   ГДДДДі external ГДДДДДДДДДДДі
                  і   і     ФННННННННННѕ            і
                  і   і      ЪДДДДДДДДДДї           і
                  і   АДДДДДі блок asm ГДДДДДДДДДДі
                  і          АДДДДДДДДДДЩ           і
                  і     ЪДДДДДДДДДДДДДДДДДДї        і
                  АДДДДі директива inline ГДДДДДДДДЩ
                        АДДДДДДДДДДДДДДДДДДЩ

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

     function Max(a: Vector; n: integer): extended;
     var
      x: extended;
      i: integer;
     begin
       x := a(1);
       for i := 2 to n do if x < a[i] then x := a[i];
       Max := x;
     end;

     function Power(x: extended; y: integer): extended;
     var
       z: extended;
       i: integer;
     begin
       z := 1.0; i := y;
       while i > 0 do
     begin
       if Odd(i) then z := z*x;
       x := Sqr(x);
     end;
     Power := z;
     end;

     Аналогично процедурам функции могут описываться, как с ближним типом вызова (near), с дальним типом вызова (far), опережающие (forward), внешние (external), ассемблерные (assembler) или встраиваемые (inline). Однако функции прерываний (interrupt) не
 допускаются. 
                        Описания методов

     Объявление метода внутри объектного типа соответствует опережающему объявлению (forward) этого метода. Таким образом, метод должен быть реализован где-нибудь после объявления объектного типа и внутри той же самой области действия метода путем опреде
ления объявления. 
     Для процедурных и функциональных методов определение описания имеет форму обычного объявления процедуры или функции, за тем исключением, что в этом случае идентификатор процедуры или функции рассматривается как идентификатор метода. 
     Для методов типа конструкторов и деструкторов определение описаний принимает форму описания процедурного метода, за тем исключением, что зарезервированное слово procedure заменяется на зарезервированное слово constructor или destructor. Определение 
описания метода может повторять (но не обязательно) список формальных параметров заголовка метода в объектном типе. В этом случае заголовок метода должен в точности повторять заголовок в объектном типе в порядке, типах и именах параметров и в типе возвра
щаемого функцией результата, если метод является функцией. 
     В определяющем описании метода всегда присутствует неявный параметр с идентификатором Self, соответствующий формальному параметру-переменной, обладающему объектным типом. Внутри блока метода Self представляет экземпляр, компонент метода которого был
 указан для активизации метода. Таким образом, любые изменения значений полей Self отражаются на экземпляре. 
     Область действия идентификатора компонента объектного типа распространяется на блоки процедур, функций, конструкторов и деструктора, которые реализуют методы данного объектного типа. Эффект получается тот же, как если бы в начало блока метода был вс
тавлен оператор with в следующей форме: 
     with Self do
     begin
      ...
     end;

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

     procedure Rect.Intersect(var R: Rect);
     begin
     if A.X < R.A.X then A.X := R.A.X;
     if A.X < R.A.Y then A.Y := R.A.Y;
     if B.X > R.B.X then B.X := R.B.X;
     if B.Y < R.B.Y then B.Y := R.B.Y;
     if (A.X >= B.X) or (A.Y >= B.Y) then Init (0, 0, 0, 0);
     end;

     procedure Field.Display;
     begin
       GotoXY(X, Y);
       Write(Name^, ' ', GetStr);
     end;

     function NumField.PutStr(S: string): boolean;
     var
        E: integer;
     begin
       Val(S, Value, E);
       PutStr := (E = 0) and (Value >= Min) and (Value <= Max);
     end;

                   Конструкторы и деструкторы

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

     constructor Field.Copy(var F: Field);
     begin
       Self := F;
     end;

     constructor Field.Init(FX, FY, FLen: integer; FName: string);
     begin
       X := FX;
       Y := FY;
       GetMem(Name, Length (FName) + 1);
       Name^ := FName;
     end;

     constructor StrField.Init(FX, FY, FLen: integer; FNAme:
                               string);
     begin
       Field.Init(FX, FY, FLen, FName);
       GetMem(Value, Len);
       Value^ := '';
     end;

     Главным действием конструктора порожденного (дочернего) типа, такого как указанный выше StrField.Init, почти всегда является вызов соответствующего конструктора его непосредственного родителя для инициализации наследуемых полей объекта. После выполн
ения этой процедуры, конструктор инициализирует поля объекта, которые принадлежат только порожденному типу. 
      Деструкторы (сборщики мусора) являются противоположностями конструкторов и используются для очистки объектов после их использования. Обычно очистка состоит из удаления всех полей-указателей в объекте. 
     Примечание: Деструктор может быть виртуальным и часто является таковым. Деструктор редко имеет параметры. 
     Приведем несколько примеров деструкторов:

      destructor Field.Done;
      begin
        FreeMem(Name, Length (Name^) + 1);
      end;

      destructor StrField.Done;
      begin
        FreeMem(Value, Len);
        Field.Done;
      end;

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

     В описании процедуры или функции задается список формальных параметров. Каждый параметр, описанный в списке формальных параметров, является локальным по отношению к описываемой процедуре или функции и в модуле, связанным с данной процедурой или функ
цией на него можно ссылаться по его идентификатору. 
                  ХНННё   ЪДДДДДДДДДДДДДДДДДДДДДї   ХНННё
     список  ДДДДі ( ГДДі описание параметров іДВі ) ГДДДДДД
   формальных     ФНННѕ  АДДДДДДДДДДДДДДДДДДДДДЩ і ФНННѕ
   параметров           і         ХНННё           і
                        АДДДДДДДДДґ ; іДДДДДДДДДДЩ
                                  ФНННѕ

                          ЪДДДДДДДДДДДДДДДДДДДДДДДДї
  описание ДДДВДДДДДДДДДДі список идентификаторов ГДВДДДДДДДДД
 параметров   і          АДДДДДДДДДДДДДДДДДДДДДДДДЩ і    
              і ХНННННё і      ЪДДДДДДДДДДДДДДДДДДДДДЩ    і
              Аі var ГДЩ      і ХНННё  ЪДДДДДДДДДДДДДДДї і
                ФНННННѕ        Аі : ГДі тип параметра ГДЩ
                                 ФНННѕ  АДДДДДДДДДДДДДДДЩ

                           ЪДДДДДДДДДДДДДДДДДДДДї
 тип параметра ДДДДВДДДДДДДґ идентификатор типа ГДДДДДДДДДДДДДД
                   і       АДДДДДДДДДДДДДДДДДДДДЩ     
                   і            ХННННННННё            і
                   ГДДДДДДДДДДДі string ГДДДДДДДДДДДДґ
                   і            ФННННННННѕ            і
                   і            ХННННННННё            і
                   АДДДДДДДДДДДі  file  ГДДДДДДДДДДДДЩ
                                ФННННННННѕ

     Существует три типа параметров: значение, переменная и нетипизованная переменная. Они характеризуются следующим: 
     1. Группа параметров, перед которыми отсутствует ключевое слово var и за которыми следует тип, является списком параметров-значений.
     2. Группа параметров, перед которыми следует ключевое слово var и за которыми следует тип, является списком параметров-переменных.
     3. Группа параметров, перед которыми стоит ключевое слово var и за которыми не следует тип, является списком нетипизованных параметров-переменных. 
                       Параметры-значения

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

     Параметр-переменная используется, когда значение должно передаваться из процедуры или функции вызывающей программе. Соответствующий фактический параметр в операторе вызова процедуры или функции должен быть ссылкой на переменную. При активизации проц
едуры или функции формальный параметр-переменная замещается фактической переменной, любые изменения в значении формального параметра-переменной отражаются на фактическом параметре. 
     Внутри процедуры или функции любая ссылка на формальный параметр-переменную приводит к доступу к самому фактическому параметру. Тип фактического параметра должен совпадать с типом формального параметра-переменной (вы можете обойти это ограничение с 
помощью нетипизованного параметра-переменной). Если формальный параметр имеет строковый тип, ему присваивается атрибут длины, равный 255, и фактический параметр должен также иметь строковый тип и атрибут длины, равный 255. 
     Файловый тип может передаваться только, как параметр-переменная. 
     При ссылке на фактический параметр-переменную, связанную с индексированием массива или получением указателя на объект, эти действия выполняются перед активизацией процедуры или функции. 
                             Объекты

     Правила совместимости объектных типов по присваиванию также применимы к параметрам-переменным объектного типа: для формального параметра типа T1 фактический параметр должен быть типа T2, если тип T2 является наследником типа T1. Например, методу Fie
ld.Copy может передаваться экземпляр типа Field, NumField, ZipField или любого другого типа, являющегося производным по отношению к типу Field. 
               Нетипизованные параметры-переменные

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

     function Equal(var source,dest; size: word): boolean;
     type
       Bytes = array[0..MaxInt] of byte;
     var
       N: integer;
     begin
       N := 0;
      while (N Bytes(source)[N]
                      do Inc(N);
       Equal := N = size;
     end;

     Эта функция может использоваться для сравнения любых двух переменных любого размера. Например, с помощью описаний: 
    type
      Vector = array[1..10] of integer;
      Point = record
                x,y: integer;
              end;
     var
       Vec1, Vec2: Vector;
       N: integer;
       P: Point;
 и вызовов функций: 
     Equal(Vec1,Vec2,SizeOf(Vector))
     Equal(Vec1,Vec2,SizeOf(integer)*N)
     Equal(Vec[1],Vec1[6],SizeOf(integer)*5)
     Equal(Vec1[1],P,4)
 сравнивается Vес1 с Vес2, сравниваются первые N элементов Vес1 с первыми N элементами Vес2, сравниваются первые 5 элементов Vес1 с последними пятью элементами Vес2 и сравниваются Vес1[1] с Р.х и Vес2[2] с P.Y. 
                        Процедурные типы

     Являясь расширением стандартного Паскаля, Турбо Паскаль позволяет интерпретировать процедуры и функции, как объекты, которые можно присваивать переменным и передавать в качестве параметров. Таким образом, допускается использование процедурных типов.
 
    Примечание: Процедурные типы определяются также в Главе 3 ("Типы"). 
                     Процедурные переменные

     После определения процедурного типа появляется возможность описывать переменные этого типа. Такие переменные нызывают процедурными переменными. Например, с учетом описаний типа из предыдущего примера, можно объявить следующие переменные: 
     var
       P: SwapProc;
       F: MathFunc;

     Как и целая переменная, которой можно присвоить значение целого типа, процедурной переменной можно присвоить значение процедурного типа. Таким значением может быть, конечно, другая процедурная переменная, но оно может также представлять собой иденти
фикатор процедуры или функции. В таком контексте описания процедуры или функции можно рассматривать, как описание особого рода константы, значением которой является процедура или функция. Например, пусть мы имеем следующие описания процедуры и функции: 
     procedure Swap(var A,B: integer);
     var
       Temp: integer;
     begin
       Temp := A;
       A := B;
       B := Temp;
     end.

     function Tan(Angle: real): real;
     begin
       Tan := Sin(Angle) / Cos(Angle);
     end.

     Описанным ранее переменным P и F теперь можно присвоить значения: 
     P := Swap;
     F := Tan;

     После такого присваивания обращение P(i,j) эквивалентно Swap (i,j) и F(X) эквивалентно Tan(X). 
     Как и при любом другом присваивании переменная слева и значение в правой части должны быть совместимы по присваиванию. Процедурные типы, чтобы они были совместимы по присваиванию, должны иметь одно и то же число параметров, а параметры на соотвеству
ющих позициях должны быть одинакового типа. Как упоминалось ранее, имена параметров в описании процедурного типа никакого действия не вызывают. 
     Кроме того, для обеспечения совместимости по присваиванию процедура и функция, если ее нужно присвоить процедурной переменной, должна удовлетворять следующим требованиям: 
     - Это не должна быть стандатная процедура или функция.
     - Такая процедура или функция не может быть вложенной.
     - Такая процедура не должна быть процедурой типа inline.
     - Она не должна быть процедурой прерывания (interrupt).

     Стандартными процедурами и функциями считаются процедуры и функции, описанные в модуле System, такие, как Writeln, Readln, Chr, Ord. Чтобы получить возможность использовать стандартную процедуру или функцию с процедурной переменной, вы должны написа
ть для нее специальную "оболочку". Например, пусть мы имеем процедурный тип: 
     type
        IntProc = procedure(N: integer);

     Следующая процедура для записи целого числа будет совместимой по присваиванию: 
     procedure WriteInt(Number: Integer); far;
     begin
        Write(Number);
     end.

     Вложенные процедуры и функции с процедурными переменными использовать нельзя. Процедура или функция считается вложенной, когда она описывается внутри другой процедуры или функции. В следующем примере процедура Inner вложена в процедуру Outer и поэто
му ее нельзя присваивать процедурной переменной: 
     program Nested;
     procedure Outer;
     procedure Inner;
      begin
        Writeln('Процедура Inner является вложенной');
      end;
      begin
        Inner;
      end;
      begin
        Outer;
      end.

     Использование процедурных типов не ограничивается просто процедурными переменными. Как и любой другой тип, процедурный тип может участвовать в описании структурного типа, что видно из следующих описаний: 
     type
       GotoProc   = procedure(X,Y: integer);
       ProcList   = array[1..10] of GotoProc;
       WindowPtr  = ^WindowRec;
       Window     = record
                      Next: WindowPtr;
                      Header: string[31];
                      Top,Left,Bottom,Right: integer;
                      SetCursor: GotoProc;
                    end;
     var
       P: ProcList;
       W: WindowPtr;

     С учетом этих описаний допустимы следующие вызовы процедур: 
     P[3](1,1);
     W.SetCursor(10,10);

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

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

     type
       Func = function(X,Y: integer): integer;

     function Add(X,Y: integer): integer; far;
     begin
        Add := X + Y;
      end;

     function Multiply(X,Y: integer): integer; far;
     begin
        Multiply := X*Y;
     end;

     function Funny(X,Y: integer): integer; far;
     begin
         Funny := (X+Y) * (X-Y);
     end;

     procedure PrintTable(W,H: integer; Operation: Func);
     var
        X,Y : integer;
     begin
        for Y := 1 to H do
        begin
          for X := 1 to W do Write(Operation(X,Y):5);
          Writeln;
        end;
        Writeln;
     end;

     begin
       PrintTable(10,10,Add);
       PrintTable(10,10,Multiply);
       PrintTable(10,10,Funny);
     end.

     При работе программа Table выводит три таблицы. Вторая из них выглядит следующим образом: 
            1   2   3   4   5   6   7   8   9   10
            2   4   6   8  10  12  14  16  18   20
            3   6   9  12  15  18  21  24  27   30
            4   8  12  16  20  24  28  32  36   40
            5  10  15  20  25  30  35  40  45   50
            6  12  18  24  30  36  42  48  54   60
            7  14  21  28  35  42  49  56  63   70
            8  16  24  32  40  48  56  64  72   80
            9  18  27  36  45  54  63  72  81   90
           10  20  30  40  50  60  70  80  90  100

     Параметры процедурного типа особенно полезны в том случае, когда над множеством процедур или функций нужно выполнить какие-то общие действия. В данном случае процедуры PrintTable представляет собой общее действие, выполняемое над функциями Add, Mult
iply и Funny. 
     Если процедура или функция должны передаваться в качестве параметра, они должны удовлетворять тем же правилам совместимости типа, что и при присваивании. То есть, такие процедуры или функции должны компилироваться с директивой far, они не могут быть
 встроенными функциями, не могут быть вложенными и не могут описываться с атрибутами inline или interrupt.


Яндекс цитирования