



                          ГЛАВА 8

                    Процедуры и функции

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

                     Описания процедур

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

             -------------   -   ------------------   -
 Описание -->! заголовок !->(;)->! тело процедуры !->(;)-->
процедуры    ! процедуры !   -   ------------------   -
             -------------

              -----------   ---------------
 Заголовок ->( procedure )->!идентификатор!----------------->
процедуры     -----------   ---------------  !            ^
                                     ---------            !
                                     ! ------------------ !
                                     ! !     список     ! !
                                     --!   формальных   !--
                                       !   параметров   !
                                       ------------------

     В заголовке процедуры указывается имя  процедуры и опи-
сывается список формальных параметров (если  он  присутству-

                                - 2 -


ет):

                       ----------------------
 Тип параметра ------->! идентификатор типа !--------->
                 ^     ----------------------    !
                 !            --------           !
                 ------------( строка )<----------
                              --------

                                           --------
   Тело    ------------------------------->! блок !---->
 процедуры  !    ------------    ---  ^ !  --------  ^
            !-->( interrupt  )->( ; )-! !            !
            !    ------------    ---    !            !
            !            ----------------            !
            !            !   ---------               !
            !            !->( forward )------------->!
            !            !   ---------               !
            !            !   ----------              !
            !            !->( external )------------>!
            !                ----------              !
            !      --------------------------        !
            ----->(     директива inline     )--------
                   --------------------------

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

  procedure NumString(N: integer; var S: string);

                                - 3 -


  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;

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

  procedure MyInt(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP):
                    word);
  interrupt;

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

               Опережающее описание (forward)

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


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

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

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

                 Внешние описания (external)

     Описания external позволяют связывать отдельно  скомпи-
лированные  процедуры  и функции, написанные на языке ассем-

                                - 5 -


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

  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}

     Внешними процедурами следует пользоваться, когда вы хо-
тите объединить  большое количество объектных модулей.  Если
ваши программы имеют небольшой объем, лучше вместо этого ис-
пользовать внутренние процедуры.

                    Описания inline

     Директивы inline позволяют записывать  вместо блока опе-
раторов инструкции в машинном коде. При вызове обычной проце-
дуры  компилятор  создает  код, в котором параметры  процеду-
ры помещаются в стек, а затем для вызова процедуры  генериру-
ется инструкция  САLL.  Когда  вы вызываете процедуру inline,
компилятор  генерирует  код  с помощью директивы inline, а не
с помощью инструкции САLL.  Таким  образом,  процедура inline
"расширяется"  при  каждом обращении к ней, аналогично макро-
инструкции на языке ассемблера. Приведем два небольших приме-
ра процедур типа inline:

                                - 6 -


 procedure DisableInterrupts: inline($FA);  { CLI }
 procedure EnableInterrupts;  inline($FB);  { STI }

     Процедуры типа  inline описаны  подробно  в  Главе 15
("Внутренняя организация Турбо-Паскаля").

                      Описания функций

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

            -------------   -    --------------    -
 Описание-->! заголовок !->(;)-->!тело функции!-->(;)---->
  функции   !  функции  !   -    --------------    -
            -------------

     В заголовке функции определяется идентификатор функции,
формальные параметры (если они  имеются)  и  тип  результата
функции.
             ----------   -----------------
Заголовок ->( function )->! идентификатор !----------------
 функции     ----------   ----------------- !           ^ !
                                       ------           ! !
                                       ! -------------- ! !
                                       ! !   список   ! ! !
                                       ->! формальных !-- !
                                         ! параметров !   !
                                         --------------   !
              ---------------------------------------------
              !   ---      ----------------------
              -->( : )---->!   тип результата   !---------->
                  ---      ----------------------

                           ----------------------
Тип результата ----------->! идентификатор типа !---------->
                     !     ----------------------      !
                     !           --------              !
                     ---------->( строка )--------------
                                 --------

                                - 7 -



                        --------
Тело функции ---------->! блок !-------------------------->
                 !      --------               ^
                 !      ---------              !
                 !---->( forward )------------>!
                 !      ---------              !
                 !      ----------             !
                 !---->( external )----------->!
                 !      ----------             !
                 !     --------------------    !
                 ----->! директива inline !-----
                       --------------------

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

  function Max(a: Vector; n: integer): extended;
  var
    x: extended;
    i: integer;

                                - 8 -


  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;

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

                         Параметры

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

                   ---    -----------------------    ---
 Список фор- ---->( ( )-->! описание параметров !-->( ) )-->
 мальных пара-     ---  ^ ----------------------- !  ---
   метров               !          ---            !
                        ----------( ; )<-----------
                                   ---

                                - 9 -



                          --------------------------
  Описание -------------->! список идентификаторов !-------->
 параметров   !         ^ -------------------------- !    ^
              !  -----  !      -----------------------    !
              ->( var )--      !  ---   ----------------- !
                 -----         ->( : )->! тип параметра !--
                                  ---   -----------------

                          ----------------------
Тип параметра ------------! идентификатор типа !--------->
                  !       ----------------------     ^
                  !             --------             !
                  !----------->( string )------------!
                  !             --------             !
                  !              ------              !
                  ------------->( file )--------------
                                 ------

     Существует три типа параметров: значение, переменная  и
нетипизованная переменная. Они характеризуются следующим:

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

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

     Формальный  параметр-значение  обрабатывается,  как ло-
кальная по отношению к процедуре или функции  переменная, за

                                - 10 -


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

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

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



            Нетипизованные параметры-переменные

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

  function Equal(var source,dest; size: word): boolean;
  type
    Bytes = array[0..MaxInt] of byte;
  var
    N: integer;
  begin
    N := 0;
    while (N<size) and (Bytes(dest)[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;

                                - 12 -



и вызовов функций:

  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.

                      Процедурные типы

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

                 Описания процедурного типа

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

                              -----------------------
                        ----->! заголовок процедуры !---
                        !     -----------------------  !
 процедурный тип -------!                              !--->
                        !     -----------------------  !
                        ----->!   заголовок функции !---
                              -----------------------

     По существу синтаксис записи описания процедурного типа
совпадает с записью заголовка процедуры или фукнции, за иск-
лючением того, что идентификатор, следующий за ключевым сло-
вом  procedure  или function, опускается. Приведем некоторые

                                - 13 -


примеры описаний процедурного типа:

  type
    Proc               = procedure;
    SwapProc           = procedure(var X,Y: integer);
    StrProc            = procedure(S: string);
    MathFunc           = function(X: real) : real;
    DeviceFunc         = function(var F: text) : integer;
    MaxFunc            = function(A,B: real; F: MathFunc);

     Имена параметров с описании  процедурного  типа  играют
чисто иллюстративную роль и на значение этого описания ника-
кого влияния не оказывают.

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

                   Процедурные переменные

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

  var
    P: SwapProc;
    F: MathFunc;

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


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

  {$F+}

  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.

  {$F-}

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

     P := Swap;
     F := Tan;

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



     - Она должна компилироваться в состоянии {$F+}.
     - Это не должна быть стандатная процедура или функция.
     - Такая процедура или функция не может быть вложенной.
     -  Такая  процедура  не  должна  быть  процедурой  типа
inline.
     - Она не должна быть процедурой прерывания (interrupt).

     Простейший  способ  удовлетворения  первого  требования
состоит  в  размещении  директивы компилятора {$F+} в начале
исходного  текста  (или  в  установке  меню  "Опции/Компиля-
тор/Дальний  тип  вызова" (O/C/Force Far Calls) в интегриро-
ванной интерактивной среде.
     Стандартными процедурами и функциями считаются процеду-
ры и функции, описанные в модуле System, такие, как Writeln,
Readln, Chr, Ord. Чтобы  получить  возможность  использовать
стандартную  процедуру или функцию с процедурной переменной,
вы должны написать для нее специальную "оболочку". Например,
пусть мы имеем процедурный тип:

  type
    IntProc = procedure(N: integer);

     Следующая процедура для записи целого числа будет  сов-
местимой по присваиванию:

  begin
    Write(Number);
  end.

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

  program Nested;

                                - 16 -


  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);

     Когда  процедурной  переменной  присваивается  значение

                                - 17 -


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

                Параметры процедурного типа

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

  program Tables;

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

  {$F+}

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

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

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



   {$F-}

   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

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


PrintTable  представляет  собой  общее действие, выполняемое
над функциями Add, Multiply и Funny.
     Если процедура или функция должны  передаваться  в  ка-
честве  параметра,  они должны удовлетворять тем же правилам
совместимости типа, что и при присваивании. То  есть,  такие
процедуры  или  функции  должны компилироваться с директивой
компилятора {$F+}, они не могут быть встроенными  функциями,
не могут быть вложенными и не могут описываться с атрибутами
inline или interrupt.

               Процедурные типы в выражениях

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

  {$F+}

  type
    IntFunc = function: integer;

  var
    F: IntFunc;
    N: integer;

  function ReadInt: integer;
  var
    I: integer;
  begin
    Read(I);
    ReadInt := I;
  end;

  begin

                                - 20 -


    F := ReadInt;       { присвоить значение процедуры }
    N := ReadInt;       { присвоить результат функции  }
  end.

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

     if F = ReadInt then Writeln('Равны');

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

 if @F = @ReadInt then Writeln('Равны');

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

           Примечание: Чтобы получить адрес процедурной  пе-
      ременной,  а  не  адрес, который в ней сохранен, нужно
      использовать двойной  оператор  получения  адреса  @@.

                                - 21 -


      Например,  @P означает преобразование Р в нетипизован-
      ную переменную-указатель, а @@P  означает  возвращение
      физического адреса переменной Р.

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

  type
    func = function(X: integer): integer;
  var
    F: Func;
    P: Pointer;
    N: integer;

можно построить следующие присваивания:

  F := Func(P);  { присвоить значения процедуры в Р F }
  Func(P) := F;  { присвоить значение процедуры в F P }
  @F := P;       { присвоить значение указателя в P F }
  P := @F;       { присвоить значение указателя в F P }
  N := F(N);     { вызвать функцию через F }
  N := Func(P)(N);     { вызвать функцию через P }

     Заметим, в частности, что оператор получения адреса  @,
примененный  к  процедурной переменной, можно использовать в
левой части присваивания. Обратите также внимание на  приве-
дение типов на последней строке при вызове фукнции через пе-
ременную-указатель.


