Мегаобучалка Главная | О нас | Обратная связь  


УПРАВЛЯЮЩИЕ СТРУКТУРЫ ЯЗЫКА TURBO PASCAL




Поможем в ✍️ написании учебной работы
Поможем с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой

 

В этом разделе будут рассмотрены управляющие операторы ТР (условный оператор, операторы циклов, выбора и др.), а также вопросы построения процедур и функций, и принципы построения модулей.

 

 

ПРОСТОЙ И СОСТАВНОЙ ОПЕРАТОР

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

Два последовательных оператора должны разделяться точкой с запятой. Этот символ имеет смысл конца оператора, в частности, он разделяет операторы при записи в строку:

a := 11;  b:=a*a;  Write(a,b);

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



begin

a := 11;

b := a*a;

Write(a,b)

end;

Составной оператор может содержать любое допустимое число простых операторов. Кроме того, он допускает вложенность, т.е. может содержать внутри себя другие составные операторы (в этом случае нужно лишь, чтобы внутренний составной оператор открывался позже, чем внешний, а закрывался - раньше).

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

 

УСЛОВНЫЙ ОПЕРАТОР

Условный оператор имеет структуру

if <условие> then <оператор1> else <оператор2>

где if, then, else - зарезервированные слова (если...то...иначе);

<условие> - логическое значение True или False, представленное константой, переменной или логическим выражением.

Если условие представлено значением True, то выполняется <оператор1>, следующий за словом then. Но если условие не выполняется, т.е. представлено значением False, то будет выполняться <оператор2> за словом else. Например,

if K > 5 then

begin

X := X + 5; Y := 1

end

else

Y := -1;

Здесь после then следует составной оператор, перед словом else точки с запятой нет, а затем простой оператор (между ключевыми словами точки с запятой не требуется). Точка с запятой после else обязательна, т.к. она отделяет весь условный оператор от последующего.

Часть else <оператор 2> может быть опущена. При этом если условие дает False оператор пропускается и программа переходит к следующему.

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

if Условие1 then

if Условие2 then

ОператорA

else

ОператорБ;

Можно предположить, что ОператорБ будет выполнен при невыполнении Условия1. На самом деле, имея в виду введенное правило, ОператорБ будет выполнен при выполнении Условия1 и невыполнения Условия2. Точка с запятой после ОператорА не помогает, т.к. закрывает весь оператор, а else… становится как бы отдельным оператором. Выход - использование составного оператора:

if Условие1 then

begin

if Условие2 then

ОператорA

end

else

ОператорБ;

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

 

ОПЕРАТОР МНОЖЕСТВЕННОГО ВЫБОРА

Оператор выбора позволяет выбрать одно из нескольких возможных продолжений программы. Если варианта всего два, то можно использовать оператор if, если больше, то оптимален оператор case, структура которого имеет вид:

case <ключ выбора> of <список выбора> else <операторы> end

где <ключ выбора> - переменная или выражение любого порядкового типа кроме LongInt,

<список выбора> - одна или несколько констукций вида:

<константа выбора> : <оператор>; где константа выбора - константа того же типа, что и ключ выбора.

Если ключ выбора - выражение, то оно вычисляется при исполнении оператора case. В зависимсти от значения ключа выбора выбирается тот оператор, которому предшествует константа выбора, равная вычисленному значению. Оператор может быть простым или составным. Найденный оператор вычисляется, после чего оператор выбора завершает свою работу. Если в списке выбора не будет найдена константа, соответствующая вычисленному значению ключа выбора управление передается операторам (их может быть несколько), стоящим за словом else. Имеется принциальная возможность опускать часть else <операторы>. В этом случае при отсутствии в списке выбора нужной константы ничего не произойдет, и оператор выбора просто завершит свою работу. По-прежнему, перед end и else точка с запятой не обязательна (если поставить - работать будет). Константы выбора могут перечисляться через запятую, а также записываться в виде диапазона. Значения констант должны быть уникальными в каждом наборе, т.е. они могут появиться только в одном варианте. Пересечение набора значений в разных вариантах является ошибкой. Пример:

case I of                  {I : Word}

1 : X := X +1;

2,3 : X := X +2;

4..9 : begin

Write(X);

X := X + 3     {м.б. ";"}

end                 {м.б. «;»}

else

X := X*X;

Writeln(X)              {м.б. «;»}

end;

Эффективность оператора case будет максимальным, если размещать наиболее вероятные значений первыми в порядке следования.

 

ОПЕРАТОР ЦИКЛА "ПОКА" (С ПРЕДУСЛОВИЕМ)

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

while <условие> do <оператор>;

Оператор (простой или составной), стоящий после служебного слова do и называемый телом цикла будет выполняться циклически до тех пор, пока выполняется логическое условие, т.е. пока значение условия равно True. Само условие цикла может быть логической константой, переменной или выражением с логическим результатом.

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

Следует помнить о двух моментах:

- чтобы цикл имел шанс когда-либо завершиться, содержимое его тела должно влиять на условие цикла;

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

Пример вычисления 10!

Var

F,N : Integer;

Begin

F := 1; N := 1;

while N <= 10 do

begin

F := F*N;

Inc(N)                     {N := N + 1}

end;

Writeln(F)

End.

Здесь важно присваивание N := 1 перед началом цикла, без него значение N может быть любым, и условие может быть некорректным.

 

ОПЕРАТОР ЦИКЛА "ДО" (С ПОСТУСЛОВИЕМ)

Рассмотренный ранее оператор "Пока" решает выполнять свое тело или нет, еще до первой иттерации. Если это не соответствует логике алгоритма, то можно использовать оператор цикла "До" с постусловием, который решает делать или нет следующую иттерацию лишь после завершения предыдущей. В этом операторе тело цикла будет выполнено по крайней мере один раз. Т.о. эти операторы цикла различаются только первым шагом, далее они веду себя идентично. Формат:

repeat <операторы> until <условие>

Операторы между словами repeat и until образуют тело цикла. В теле можт быть несколько операторов, при этом слова begin и end не требуются, их функции выполнют слова repeat и until (перед until можно не ставить ";"). Cравнение с циклом "Пока", где один оператор в теле.

Условие - логическая переменная, константа или выражение с логическим результатом. В отличии от цикла "Пока", если условие имеет значение True, то цикл завершается. Т.е.

Цикл "Пока" - "пока условие истинно, выполнять операторы тела";

Цикл "До" - "выполнять тело цикла, пока не станет истинным условие, т.е. пока оно ложно";

Известна простая конструкция "вечного цикла", в котором тело - пустой оператор

repeat until False;

 

ОПЕРАТОР ЦИКЛА С ПАРАМЕТРОМ (ЦИКЛ ПО СЧЕТЧИКУ)

Рассмотренные операторы цикла "Пока" и "До" (они классические) не слишком удобны для организации "строгих" циклов, которые должны быть проделаны данное число раз. Цикл с параметром вводится именно для таких случаев. Синтаксис следующий

for <параметр цикла> := <мл.зн.> to <ст.зн.> do <оператор>

Параметр цикла - переменная порядкового типа, к этому же типу должны относиться младшее и старшее значении. На первом шаге параметру цикла присваивается младшее значение, после чего идет его наращивание с постоянным шагом, равным интервалу между двумя ближайшими значениями в типе, к которому относится параметр цикла (для целочисленных типов - это 1, для символьного - от одного символа к другому при увеличении кода на 1, и т.д.). Например,

Var

I : Integer; C : Char ; B : Boolean;

E : (E1,E2,E3,E4);

Begin

for I := -10 to 10 do Write(I);

for C := 'a' to 'z' do Write(C);

for B := False to True do Write(B);

for E := E1 to E4 do       {здесь оператор - составной}

begin

I := ORD(E);

Write(I)

end

End.

Изменение параметра может быть не только возрастающим, как в рассмотренных примерах, но и убывающим. В этом случае несколько изменяется синтаксис:

for <параметр цикла> := <ст.зн.> downto <мл.зн.> do <оператор>

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

for I := 4 downto 5 do …

Запрещается изменять параметр цикла и его младшее и старшее значения (в том случае, если они заданы переменными или выражениями с ними) изнутри тела цикла. Параметр цикла не может участвовать в построении диапазонов этого цикла (for I := I-5 to I+5 do …).

В тех. описании к ТР отмечается, что после выполнения цикла for значение его параметра не определено. На самом деле скорее всего оно будет иметь конечное значение. Тем не менее корректнее его будет переприсвоить. Исключение выход из цикла с помощью goto. В этом случае значение параметра будет таким, каким оно было к моменту исполнения goto.

Циклы с параметром - быстрые и генерируют компактный код. Но они обладают существенным недостатком - параметр должен принадлежать порядковому типу, а шаг не может быть изменен. Для построения циклов с вещественным параметром и меняющимся шагом приходится обращаться к циклам с условием (цикл "До" и цикл "Пока").

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

 

ОПЕРАТОР БЕЗУСЛОВНОГО ПЕРЕХОДА

Оператор goto, который имеется во многих алгоритмических языках, в свое время был подвергнут гонениям, как противоречащий принципам структурного программирования. Ибо было доказано, что можно вообще программировать без него. Однако хотя язык Pascal является классическим языком структурного программирования, в нем тем не менее существует этот оператор. Т.е. выбор между чистотой идеи (структурное программирование) и элементарным удобством (использование goto в отдельных случаях) был сделан в пользу последнего. Полный синтаксис оператора безусловного перехода:

goto <метка>;

где метка - описанный в блоке Label идентификатор (может начинаться с цифры). Метка может стоять в программе в любом месте между операторами и отделяется от второго оператора двоеточием ":". Каждая метка может появляться только один раз.

Label

L1, L2;

Begin

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                       {в процедуре одна локальная переменная В)

C := C + C;

B := B + B;

Writeln(C,B)

end;

Begin

Writeln(A,B);

Plus(A,B);

Writeln(A,B)

End.

Вначале на экран будут выведены числа "5 7". В вызове процедуры Plus фактическими параметрами являются параметр-переменная А и параметр-значение В. Переменная А во время действия процедуры меняет свое значение (в течении процедуры она выступает с именем С), новое удвоенное значение записывается на место старого. Переменная В копирует свое значение в локальную переменную с тем же именем. После выполнения процедуры на экране появляются "10 14". Последний оператор выводит на экран числа "10 7".

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

Существенное различие в объявлении переменных в списке формальных параметров от объявления их в разделе описания переменных состоит в том, что типом любого параметра в списке формальных параметров может быть только стандартный или ранее объявленный тип (не годится Procedure P (A : array [1..10] of Real)). Поэтому для того, чтобы передать массив (или строку) нужно сначала описать его тип. Это накладывает некоторые ограничения в том случае, когда заранее не известна длина массива (строки). Описывать его по максимуму приводит к затратам памяти. Вообще, невозможность использования статических массивов с "плавающими" границами является существенным недостатком ТР, и объектом его критики. В версии 7.0 язык поддерживает т.н. открытые массивы, с помощью которых легко решается проблема передачи подпрограмме одномерных массивов переменной длины. Открытый массив - формальный параметр описывается как:

Procedure P (OpenArray: array of Real). В этом случае можно подставлять в процедуру фактические одномерные массивы любой длины. Верхняя граница возвращается функцией High.

Особенности функций

Идентификатор функции возвращает после вызова простое (порядковое или вещественное) или строковое значение заданного типа (возвращаемое значение не может быть файлом, массивом, записью, множеством, объектом). Для присвоения функции значения ее имя должно появиться хотя бы однажды в левой части оператора присваивания в теле самой функции.

Функция, как и процедура, может обмениваться значениями с программой и изменять глобальные переменные непосредственно или через параметры-переменные. Обычно, когда функция, кроме выдачи своего значения, меняет какие-либо глобальные значения или производит другие действия, не связанные с вычислениями своего значения, говорят, что она имеет побочный эффект. Функция вычисления Х в степени Y по формуле (ХY=exp(Y × ln(X)):

Var

X,Y : Real;

Function Power (Arg, P : Real) : Real;

begin

if Arg <> 0 then  {если аргумент ¹ 0}

Power := exp(P * ln(abs(Arg)))

else

if P = 0 then {если аргумент = 0 и степень = 0}

Power := 1

else               {если аргумент = 0 и степень ¹ 0}

Power := 0

end;

Begin

Readln(X,Y);

Writeln(Power(X,Y))

End.

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

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

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

Procedure <имя> (<параметры>); forward;

...

Procedure <имя>    {список параметров уже не нужен}

<тело процедуры>;

Эта директива объявляет заголовок подпрограммы, откладывая описание содержимого «на потом». Местоположение этого описания не играет роли, и в нем можно не указывать параметры, а ограничиться лишь именем. Директива Forward существует в языке, в основном, для развязки закольцованных вызовов (одна подпрограмма вызывает вторую, которая в свою очередь вызывает первую).

Кроме стандартной директивы Forward  можно использовать и другие стандартные директивы, которые должны указываться за заголовком подпрограммы. Эти директивы уточняют действия компилятора и распространяются на всю подпрограмму и только на нее.

Assembler - отменяет стандартную последовательность машинных инструкций, вырабатываемых при входе в процедуру и выходе из нее. Тело подпрограммы должно в этом случае реализовываться с помощью команд встроенного ассемблера (ассемблера, встроенного компилятор ТР);

External - с помощью этой директивы объявляется внешняя подпрограмма. При этом появляется возможность вызова процедур и функций, написанных и откомпилированных с помощью внешнего ассемблера (например, TurboAssembler Borland)). Возможен импорт объектных кодов, полученных с помощью Turbo C и других языков, но на практике он труднореализуем;

Far, Near - компилятор должен создавать код программы, рассчитанный на дальнюю либо ближнюю модель памяти;

Inline - указывает на то, что тело подпрограммы реализуется с помощью встроенных машинных команд;

Interrupt - используется при создании процедур обработки прерываний.

Процедурные типы. Параметры-функции и параметры-процедуры

Начиная с версии 5.5 в ТР был введен новый тип - процедурный тип. Этим был сделан важный шаг от языка Turbo Pascal как средства для обучения программированию к языку Turbo Pascal как средству профессионального программирования. Процедурный тип, являясь расширением стандартного Pascal и позволяет интерпретировать процедуры и функции как объекты, которые можно использовать при передачи параметров (понятие "данные" сюда меньше подходит).

Описание процедурного типа выполняется в соответствии с уже принятым синтаксисом (процедурный тип - общее понятие, объединяющее и функциональный тип):

Type

ProcType = Procedure (S : String; Var B : Byte);

FuncType = Function (X,Y : Integer) : Real;

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

Type

FuncType = Function (X : Real) : Real;    {процедурный тип}

Procedure Integral (LowerLimit, UpperLimit : Real; {вычисл. интеграла функции

Var Result : Real;  Funct от точки LowerLimit до UpperLimit с

Funct : FuncType); результатом в параметре-переменной Result}

begin

end;

 

Function SinExp (Arg : Real) : Real; {некоторая функция}

begin

SinExp := Sin(Arg) + Exp(Arg)

end

Begin

Integral (0, 1, Res, SinExp); {процедура вычисления интеграла с

…                            записью в фактическую переменную Res}

End.

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

Не разрешается объявлять функции, возвращающие значение процедурного типа. Требование к процедурам и функциям, которые передаются как параметры: они должны компилироваться в режиме {$F+}, что обеспечивает использование дальней модели обращения Far (в примере эта директива должна стоять перед описанием функции SinExp). Не всякую функцию (процедуру) можно подставить в вызов. Нельзя подставлять: процедуры с директивами Inline и Interrupt, вложенные процедуры и функции, стандартные процедуры и функции (не годится Integral (0, 1, Res, Sin)).

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

Type

RecType = record

X,Y : Integer;

P : ProcType;

end;

Var

Rec1,Rec2 : RecType;

Естественно, в процессе работы программы требуется присвоить некоторое значение полю Rec1.P. Этим значением должна быть специально описанная процедура. Т.е. в такое записи можно хранить не только данные, но и процедуру их обработки. Причем в любой момент можно сменить ту процедуру, которая понимается под именем Rec1.P.

Процедурные переменные по формату совместимы с переменными типа Pointer и после приведения типов могут обмениваться с ними значениями.

Используя директиву Absolute можно обмениваться данными между подпрограммами путем совмещения областей памяти их локальных переменных с адресом глобальной переменной.

Обыкновенные локальные переменные в подпрограммах всегда "забывают" свои значения и при повторном вызове стартовые значения локальных переменных совершенно случайны. Для сохранности информации между вызовами можно использовать статические локальные переменные, которые вводятся как локальные типизированные константы. Const A : Word = 240; Значение типизированной константы может меняться в теле подпрограммы. После чего оно запоминается до повторного вызова (восстановление стартового значения не происходит).

Процедуры и функции могут содержать в своих заголовках параметры-переменные (т.е. те, которые передаются как адрес, а не как значение) без указания их типа. Procedure P (Var V1,V2); Через такое объявление можно передавать подпрограммам строки, массивы, записи и т.д. Но при этом процедура (или функция) должна явно задавать тип, к которому внутри нее приравниваются бестиповые переменные.

Традиционное преимущество языка ТР - это использование рекурсии, под которой понимается вызов функции (процедуры) из тела этой же функции (процедуры). Например, вычисление факториала:

Function Fact (n : Word) : LongInt;

begin

if n = 0 then

Fact := 1

else Fact := n * Fact(n-1);

end;

Внесение рекурсивности придает программе изящество, но всегда оно же "заставляет" программы расходовать больше памяти. Дело в том, что каждый "отложенный" вызов подпрограммы это свой набор значений всех локальных переменных этой функции, размещенной в стеке. В ТР размер стека не превышает 64 К.

 

МОДУЛИ

Стандартный Pascal не предусматривает механизмов раздельной компиляции частей программы с последующей их сборкой перед выполнением. Более того, принцип обязательного описания объекта перед его использованием делает практически невозможным разработку разнообразных библиотек прикладных программ. Точнее, такие библиотеки в рамках стандартного Pascal могут существовать только в виде исходных текстов и программист должен сам включать в программу большие тексты различных поддерживающих процедур (например, процедуры численного интегрирования, математической статистики и т.д.)

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

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

Модуль имеет следующую структуру:

Unit <имя>;

Interface

…         {интерфейсная часть}

Implementation

…         {выполнение - исполняемая часть}

Begin

…         {инициирующая часть}

End.

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




Читайте также:



©2015-2020 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (145)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.096 сек.)
Поможем в написании
> Курсовые, контрольные, дипломные и другие работы со скидкой до 25%
3 569 лучших специалисов, готовы оказать помощь 24/7