8.1. ЛОКАЛИЗАЦИЯ ИМЕН

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

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

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

Рис. 8.1. Пример структуры программы

Procedure A; Procedure Al;

begin

end {A1};

Procedure A2;

begin

end {A2};

begin {A}

end {A};

Procedure B;

Procedure Bl;

begin

end {Bl};

, Procedure B2;

Procedure B21;

И Т. Д.

Подпрограмма любого уровня имеет обычно множество имен констант, переменных, типов и вложенных в нее подпрограмм низшего уровня. Считается, что все имена, описанные внутри подпрограммы, локализуются в ней, т. е. они как бы “невидимы” снаружи подпрограммы. Таким образом, со стороны операторов; использующих обращение к подпрограмме, она трактуется как “черный ящик”, в котором реализуется тот или иной алгоритм. Все детали этой реализации скрыты от глаз пользователя подпрограммы и потому недоступны ему. Например, в рассмотренном выше примере из основной программы можно обратиться к процедурам а и в, но нельзя вызвать ни одну из вложенных в них процедур a1,

А2, В1

И Т. Д.

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

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

Пусть имеем такое описание:

var V1 : ... ;

Procedure A;

var V2 : ...;

end {A};

Procedure В;

var V3 : . . . ;

Procedure B1;

var V4 : . . . ;

Procedure В 11;

var V5;

Из процедуры B11 доступны все пять переменных v1,...,v5, из процедуры в1 доступны переменные v1,...,v4, из центральной программы-только v1.

При взаимодействии подпрограмм одного уровня иерархии вступает в силу основное правило Object Pascal: любая подпрограмма перед ее использованием должна быть описана. Поэтому из подпрограммы в можно вызвать подпрограмму а, но из а вызвать в невозможно (точнее, такая возможность появляется только с использованием опережающего описания, см. п. 8.5.) Продолжая образное сравнение, подпрограмму можно уподобить ящику с непрозрачными стенками и дном и полупрозрачной крышей: из подпрограммы можно смотреть только “вверх” и нельзя “вниз”, т. е. подпрограмме доступны только те объекты верхнего уровня, которые описаны до описания данной подпрограммы. Эти объекты называются глобальными по отношению к подпрограмме.

В Object Pascal допускается произвольная последовательность описания констант, переменных, типов, меток и подпрограмм. Например, раздел var описания переменных может появляться в пределах раздела описаний одной и той же подпрограммы много раз и перемежаться с объявлениями других объектов и подпрограмм. Для Object Pascal совершенно безразличен порядок следования и количество разделов var, type, const и label, но при определении области действия этих описаний следует помнить, что имена, описанные ниже по тексту программы, недоступны из ранее описанных подпрограмм, например:

var V1 : ; . . ;

Procedure S;

var V2 : . . . ;

end {S};

var V3 : . . . ;

Из процедуры s можно обратиться к переменным v1 и v2, но нельзя использовать V3, так как описание этой переменной следует в программе за описанием процедуры s.

Локализованные в подпрограмме имена могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя “закрывает” глобальное и делает его недоступным, например:

var

i : Integer;

Procedure P;

var

i : Integer;

begin

IbOutput.Caption := IntToStr(i);

end {P};

begin

i := 1;

P

end;

Что выведет эта программа на экран? Все что угодно: значение внутренней переменной i при входе в процедуру p не определено, хотя одноименная глобальная переменная имеет значение 1. Локальная переменная “закроет” глобальную, и на экран будет выведено произвольное значение, содержащееся в неинициированной внутренней переменной.

Если убрать описание

var

i : integer;

из процедуры p, то на экран будет выведено значение глобальной переменной i,t. е. 1.

Таким образом, одноименные глобальные и локальные переменные - это разные переменные. Любое обращение к таким переменным в теле подпрограммы трактуется как обращение к локальным переменным, т. е. глобальные переменные в этом случае попросту недоступны.