goto L1;
…
L1 : goto L2;
…
L2 :
End.
В примере программа, дойдя до goto L1, сразу заканчивается. Можно ставить метки перед словом end, хотя это не оператор (перед begin нельзя). Следует избегать переходов внутрь составных операторов, вложенных в тот, где выполняется оператор goto (особенно если это составные операторы цикла). Наиболее распространены случаи использования goto при выходе из вложенных операторов во внешние, в частности для преждевременного выхода из операторов цикла.
Область действия операторов безусловного перехода строго локализована. Метки, описанные в основном блоке, действуют только в нем, и не распространяются на процедуры и функции. Запрещены переходы по оператору goto между процедурами, а также между процедурами и основным блоком.
Есть два оператора, похожих на goto, предназначенные для выхода из программных блоков (процедур, функций, основного программного блока). Это бывает удобно, т.к. позволяет завершить программу или процедуру без предварительного перехода к меткам.
К таким операторам завершения относятся вызовы системных процедур Exit и Halt. Вызов Exit завершает работу своего программного блока. Т.е. если Exit выполняется в процедуре, то ее выполнение завершится и ход программы вернется к следующему за вызовом этой процедуры оператору. При этом процедура вернет значения, которые успели вычислиться к моменту выполнения Exit (если она должна их возвратить). Сама программа не прервется. Но если Exit выполняется в основном блоке, выход из нее будет эквивалентен нормальному ее завершению. В принципе, процедура Exit избыточна, т.к. эквивалентна оператору goto на метку, стоящую перед последним словом end. Но ее использование позволяет избежать лишней возни с метками и улучшает читаемость программы. Процедура Halt (более полно Halt(n)) действует менее разборчиво. Независимо от того, где она находится, ее выполнение завершает работу программы с кодом завершения n. Этот код впоследствии может быть проанализирован в среде MS-DOS. Имеет смысл при нескольких вызовах Halt в тексте программы назначить им разные коды завершения. Тогда можно будет при отладке или работе определить, чем вызвано прерывание программы.
ПРОЦЕДУРЫ И ФУНКЦИИ
В разделе будут рассмотрены вопросы, связанные с написанием и употреблением подпрограмм, представленных в виде процедур и ли функций.
Процедуры и функции представляют собой относительно самостоятельные фрагменты программы, оформленные особым способом и снабженные именем. Упоминание этого имени в тексте программы называется вызовом процедуры (функции).
Подпрограммы представляют собой инструмент, с помощью которого любая программа может быть разбита на ряд в определенной степени независимых друг от друга частей. Такое разбиение имеет смысл по двум причинам.
Во-первых, это средство экономии памяти и других ресурсов: каждая подпрограмма существует в программе в единственном экземпляре, в то время как обращаться к ней можно многократно из разных точек программы. При вызове подпрограммы активизируется последовательность образующих ее операторов, а с помощью передаваемых подпрограмме параметров нужным образом модифицируется реализуемый в ней алгоритм.
Во-вторых, при таком разбиении максимально реализуется принцип нисходящего проектирования. В этом случае алгоритм представляется в виде последовательности относительно крупных подпрограмм, реализующих более или менее самостоятельные смысловые части алгоритма. Подпрограммы в свою очередь разбиваются на менее крупные подпрограммы нижнего уровня и т.д. Все заканчивается, когда реализуемые подпрограммами алгоритмы не станут настолько простыми, чтобы их можно было запрограммировать.
Структура процедуры во многом повторяет структуру программы в целом:
Procedure <имя> (<список формальных параметров>);
Label <метки внутри тела процедуры>;
Const <локальные константы процедуры>;
Type <локальные типы>;
Var <локальные переменные>;
Procedure <вложенные процедуры и функции>;
…
Function
…
Begin
<операторы – тело процедуры>
End;
Отличие от структуры программы состоит в заголовке и завершающей «;» вместо «.» после оканчивающего End. Порядок следования разделов описаний подчиняется тем же правилам, по которым строится вся программа. В отличие от стандартного языка Pascal в ТР разделы Label, Type, Const, Var могут следовать друг за другом в любом порядке и встречаться в разделе описаний сколько угодно раз. Однако поскольку программа компилируется последовательно, начиная с начала, важно соблюдать правило: все, что в программе используется должно быть перед этим описано.
Отличие структуры функции от структуры процедуры состоит в указании типа возвращаемого значения функции:
Function <имя> (<список формальных параметров>) : <тип>;
Label
…
End;
Поскольку процедуры и функции должны обладать определенной независимостью в смысле использования переменных и т.д., при их введении в программу возникает разделение данных и их типов на глобальные и локальные. Глобальные константы, типы, переменные – это те, которые объявлены в программе вне процедур или функций. Локальные – это константы, типы, переменные, существующие только внутри процедур или функций, и объявленные либо в списке формальных параметров (только параметры-значения), либо в соответствующих разделах Const, Type, Var внутри данной процедуры или функции. Считается, что все имена, описанные внутри подпрограммы (локальные данные), локализуются в ней, т.е. они как бы "невидимы" снаружи подпрограммы. Подпрограммы, наряду со своими локальными данными, могут использовать и модифицировать и глобальные. Для этого лишь нужно, чтобы описание процедуры (функции) стояло в тексте программы ниже, чем описание соответствующих глобальных типов, констант и переменных.
Program Main;
Var
Xmain, Ymain : Integer; {глобальные переменные}
Procedure P1; {описание процедуры, у которой нет параметров}
Var
Res : Real; {локальные переменные}
begin
Res := 0.5;
Res := Res + Xmain*Ymain;
Xmain := Xmain + 1
end;
…
Var {описание других переменных, недоступных из Р1}
…
Begin
…
P1; {вызов процедуры}
…
End.
При совпадении имен локальной и глобальной переменных (типов, констант) сильнее оказывается локальное имя, и именно оно используется внутри подпрограммы.
Параметры
При описании подпрограммы после ее заголовка в скобках могут быть указаны параметры, которые, по сути, являются ее локальными данными. Параметры могут быть параметрами-значениями, параметрами-переменными или параметрами-константами. При описании необходимо указывать их тип:
Procedure <имя> ( <имя п.-з.> : <тип>;
Var <имя п.-п.> : <тип>;
Const <имя п.-к.> : <тип>);
Общее назначение - параметры обеспечивают обмен значениями между вызывающей частью программы и вызываемой подпрограммой. Описываемые в заголовке объявления подпрограммы параметры называются формальными:
Procedure P1 (A : Integer; Var B : Real)
…
а те, которые подставляются на их место при вызове - фактическими, ибо они при выполнении как бы замещают все вхождения в подпрограмму своих формальных "двойников". Вызов
Р1 (Afact,Bfact);
Переменная Аfact (типа Integer) действует в основной программе. Ее значение подставляется в подпрограмму при ее вызове на место формального параметра А.
Количество и тип формальных параметров должно строго соответствовать количеству и типам фактических параметров.
Параметры-значения - это локальные переменные подпрограммы, стартовые значения которых задаются при вызове подпрограммы из внешних блоков (в отличие от локальных переменных, описанных в разделе описаний подпрограммы, которые должны получать свои начальные значения в теле подпрограммы). Описанные в заголовке параметры-значения могут изменять свои значения наряду с прочими переменными, но эти изменения будут строго локальными и никак не передадутся в вызывающие операторы.
Для того чтобы подпрограмма изменила значение переданной ей переменной, нужно объявлять соответствующие параметры, как параметры-переменные, вставляя слово Var перед их описанием в заголовках.
Внутренний механизм передачи параметров подпрограмм следующий. При вызове процедуры или функции каждой локальной переменной, описанной внутри подпрограммы, и каждому параметру-значению отводится место для хранения данных в специальной области памяти, называемой стеком. Эти места принадлежат переменным ровно столько времени, сколько выполняется подпрограмма. При этом ячейки, соответствующие параметрам-значениям, сразу же заполняются конкретным содержимым, заданным при вызове подпрограммы. По-другому организуются параметры-переменные и параметры-константы. Вместо копии их значения (как было у параметра-значения) подпрограмма получает разрешение работать с тем местом, где постоянно (во время работы самого вызывающего блока) хранится значение переменной или константы, которая указана при вызове на месте параметра-переменной или параметра-константы. С рассмотренного следует, что на место параметра-значения можно подставлять непосредственно значение, а на местах параметра-переменной и параметра-константы могут быть только идентификаторы. Компилятор блокирует любые присваивания параметру-константе новых значений в теле подпрограммы. Использование параметров-констант бывает полезным для того, чтобы не дублировать данные большого объема, что получается, если они описаны, как параметры-значения.
Пример удвоения чисел, где одно задается как параметр-переменная, а второй - как параметр-значение.
Const
A : Integer = 5;
B : Integer = 7; {типизированные константы - переменные}
Procedure Plus (Var C : Integer; B : Integer);
begin {в процедуре одна локальная переменная В)