ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
Часть 2 ГЛАВА 4 Переменные Описания переменных Описание переменной представляет собой список идентифи- каторов, которые обозначают новые переменные и их типы. ----------- - ----- - описание--->!список !-->(:)-->!тип!------------------->(;)--> переменной !идентифи-! - ----- ! ^ - !каторов ! ! ------------- ! ----------- -->!абсолютное !-- !предложение! ------------- Тип, задаваемый для переменных, может быть идентифика- тором типа, который был ранее описан в разделе описания ти- пов того же самого блока, или блока, в который входит данный блок, или модуля, или же этот тип может быть новым определе- нием типа. При указании идентификатора в списке идентификаторов описания переменной этот идентификатор имеет силу идентифи- катора переменной в том блоке, где это описание было указа- но. К этой переменной можно обращаться из любого места этого блока, если ее идентификатор не переопределен в блоке, вхо- дящем в первый. Переопределение означает, что для новой пе- ременной используется тот же самый идентификатор, но это ис- пользование не оказывает влияния на значение первоначальной переменной. Приведем пример раздела описания переменной: var X,Y,Z: real; I,J,K: integer; Digit: 0..9; C: Color; Done,Error: boolean; Operator: (plus, minus, times); Hue1,Hue2: set of Color; Today: Date; Results: MeasureList; P1,P2: Person; Matrix: aaray[1..10,1..10] of real; Переменные, описанные вне процедуры и функции, называ- ются глобальными переменными и располагаются в сегменте дан- ных. Переменные, описанные в самой процедуре или функции, называются локальными переменными и располагаются в сегменте стека. Сегмент данных Максимальный размер сегмента данных равен 65520 байт. При компоновке программы (что автоматически осуществляется в конце компиляции программы) глобальные переменные всех моду- лей, используемых программой, а также собственные глобальные переменные программы, размещаются в сегменте данных. Если для глобальных переменных требуется более 65520 байт, то следует распределить большие структуры в виде дина- мических переменных. Дальнейшее описание этой темы можно найти в разделе "Указатели и динамические переменные" насто- ящей главы. Сегмент стека Размер сегмента стека устанавливается с помощью дирек- тивы компилятора $M и лежит в пределах от 1024 до 65520 байт. По умолчанию размер стека равен 16384 байт. При каждой активизации (вызове) процедуры или функции в стек помещается множество локальных переменных. При заверше- нии работы память, занимаемая локальными переменными, осво- бождается. В любой момент выполнения программы общий размер локальных переменных в активных процедурах и функциях не должен превышать размера сегмента стека. Директива компилятора $S используется для проведения проверок переполнения стека в программе. В состоянии {$S+}, принятом по умолчанию, генерируется код, осуществляющий про- верку переполнения стека в начале каждой процедуры или функ- ции. В состоянии {$S-} такие проверки не проводятся. Пере- полнение стека может вызвать аварийное завершение работы системы, поэтому не следует отменять проверки стека, если нет абсолютной уверенности в том, что переполнения не прои- зойдет. Абсолютные переменные Переменные можно описать так, что они будут распола- гаться по определенному адресу в памяти, и в этом случае они называются абсолютными переменными. Описание таких перемен- ных должно содержать после типа абсолютное предложение: -------- ----------- - ----------- абсолютное-->(absolute)-->!целое без!-->(:)--!целое без!----> предложение -------- ! !знака ! - !знака ! ^ ! ----------- ----------- ! ! -------------------------- ! ---->!идентификатор переменной!---- -------------------------- Отметим, что список идентификаторов в описании перемен- ной при указании предложения absolute может содержать только один идентификатор. Первая часть предложения absolute содержит сегмент и смещение, то есть адрес, по которому переменная должна быть размещена. CrtMode : byte absolute $0040:$0049; monochrome Buffer : array[1..25,1..80] of record character, attribute : char end absolute $B000:$0000; Первая константа обозначает базу сегмента, а вторая оп- ределяет смещение внутри этого сегмента. Обе константы не должны выходить за пределы диапазона от $0000 до $FFFF (от 0 до 65535). Вторая часть предложения absolute используется для опи- сания переменной, которая помещается "поверх" другой пере- менной, то есть по тому же самому адресу, что и другая пере- менная. var Str: string[32]; StrLen: byte absolute Str; Это описание указывает, что переменная StrLen должна размещаться с того же адреса, что и переменная Str, а пос- кольку первый байт строковой переменной содержит динамичес- кую длину строки, то StrLen будет содержать длину Str. Ссылки на переменные Ссылка на переменную может обозначать следующее: - переменную; - компонент в переменной структурного или строкового типа; - динамическую переменную, на которую указывает пере- менная типa указатель. Синтаксис ссылки на переменную имеет вид: --------------- ссылка на------->!идентификатор!-----------------------------> переменную ! !переменной ! ^ ^ ! ! --------------- ! ! -------------- ! ! ----------------- ! --!квалификатор!<-- !-->!приведенный тип!-----! -------------- ! !переменной ! ! ! ----------------- ! ! --------------- - ! --->!вызов функции!-->(^)-- --------------- - Отметим, что синтаксис ссылки на переменную допускает использовать вызов функции в качестве указателя-функции. По- лученный в результате указатель затем разыменовывается с тем, чтобы обозначать динамическую переменную. Квалификаторы Обращение к функции представляет собой идентификатор переменной с несколькими квалификаторами или без них, кото- рые изменяют значение обращения к функции. -------- квалификатор----->!индекс!---------------------> ! -------- ^ ! ----------------- ! !-->!десигнатор поля!---! ! ----------------- ! ! - ! ----------->(^)---------- - Идентификатор массива без квалификатора является ссыл- кой на весь массив, например: Results Идентификатор массива с указанным индексом обозначает конкретный элемент массива, в данном случае структурную пе- ременную: Results[Current+1] В случае, если элементом является запись, за индексом можно указать обозначение поля. В этом случае ссылка на пе- ременную означает конкретное поле конкретного элемента мас- сива: Results[Current+1].data Обозначение поля в указателе-поле может сопровождаться символом указателя (^) с тем, чтобы указать различие между указателем-полем и динамической переменной, на которую он указывает. Results[Current+1].data^ Если переменная, на которую указывается, является мас- сивом, то можно добавить индексы для обозначения компонентов этого массива. Results[Current+1].data^[J] Массивы, строки и индексы Конкретный элемент массива обозначается с помощью ссыл- ки на переменную массива, за которой указывается индекс, оп- ределяющий данный элемент. Конкретный символ в строковой переменной обозначается с помощью ссылки на строковую переменную, за которой указыва- ется индекс, определяющий позицию символа. - ----------- - индекс--->([)----->!выражение!------->(])----> - ^ ----------- ! - ! - ! ------(,)<-------- - Индексные выражения обозначают компоненты в соответст- вующей размерности массива. Число выражений не должно превы- шать числа индексных типов в описании массива. Более того, тип каждого выражения должен быть совместимым по присваива- нию с соответствующим индексным типом. В случае многомерного массива одинаково можно использо- вать несколько индексов или несколько выражений в индексе. Например: Matrix[I][J] тождественно Matrix[I,J] Строковую переменную можно проиндексировать с помощью одиночного индексного выражения, значение которого должно быть в диапазоне 0...n, где n - указанный в описании размер строки. Это дает доступ к каждому символу в строковом значе- нии, если значение символа имеет тип char. Первый символ строковой переменной (индекс 0) содержит динамическую длину строки; то есть Length(S) тождественно Ord(S[0]). Если атрибуту длины присваивается значение, то компилятор не проверяет, является ли это значение меньше описанного размера строки. Вы можете указать индекс строки и вне ее текущей динамической длины. В этом случае считываемые символы будут случайными, а присваивания вне текущей длины не повлияют на действительное значение строковой переменной. Записи и десигнаторы полей Конкретное поле переменной-записи обозначается с по- мощью ссылки на переменную-запись, после которой указывается обозначение поля, специфицирующее это поле. - -------------------- десигнаторы полей---->(.)--->!идентификатор поля!-----> - -------------------- Приведем несколько примеров десигнаторов полей: Today.year Results[1].count Result[1].when.month В операторе, входящем в оператор with, обозначению поля не должна предшествовать ссылка на переменную, содержащую запись. Переменные-указатели и динамические переменные Значением переменной-указателя является или nil (то есть пустое значение), или адрес значения, указывающий на динамическую переменную. Ссылка на динамическую переменную, на которую указывает переменная-указатель, записывается в виде переменной-указа- теля, после которой ставится символ указателя (^). Динамические переменные и значения их указателей созда- ются с помощью стандартных процедур New и GetMem. Вы можете использовать операцию @ и стандартную функцию Ptr для созда- ния значений указателя, которые рассматриваются как указа- тели динамических переменных. Значение nil не указывает ни на какую переменную. Ре- зультат будет неопределенным, если вы попытаетесь получить доступ к динамической переменной при неопределенном значении указателя или указателе, равном nil. Приведем несколько примеров ссылок (указателей) на ди- намические переменные: P1 P1.sibling Results[1].data Приведение типов переменных Ссылка на переменную одного типа может быть преобразо- вана в ссылку на переменную другого типа с помощью приведе- ния типов переменных. --------------- - ------------ - приведение типа-->!идентификатор!-->(()-->!ссылка на !-->())--> переменной !типа ! - !переменную! - --------------- ------------ Когда приведение типов применяется к ссылке на перемен- ную, ссылка на переменную рассматривается как экземпляр ти- па, представленного идентификатором типа. Размер переменной (число байтов, занимаемых переменной) должен быть равен раз- меру типа, представленного идентификатором типа. После при- ведения типа переменной можно указать один или несколько квалификаторов, если это допускается указанным типом. Приведем несколько примеров приведения типов перемен- ных: type ByteRec = record lo, hi: byte; end; WordRec = record low, high: word; end; PtrRec = record ofs, seg: word; end; BytePtr = ^Byte; var B: byte; W: word; L: longint; P: pointer; begin W := $1234; B := ByteRec(W).lo; ByteRec(W).hi := 0; L := $1234567; W := WordRec(L).lo; B := BytePtr(L)^; P := Ptr($40,$49); W := PtrRec(P).seg; Inc(PtrRec(P).seg; end. Обратите внимание на использование для доступа к млад- шим и старшим байтам слова типа ByteRec: это соответствует встроенным функциям Lo и Hi, только над левой частью в опе- рации присваивание может выполняться приведение типа. Отме- тим также, что для доступа к младшим и старшим словам длин- ного целого, а также к смещению и адресу сегмента указателя используются типы WordRec и PtrRec. ГЛАВА 5 Типизованные константы Типизованные константы можно сравнить с инициализиро- ванными переменными - переменными, значения которых опреде- ляются на входе в их блок. В отличие от нетипизованных конс- тант (см. раздел "Описания констант" в Главе 1) в описании типизованной константы указывается как тип, так и значение константы. описание --------- - ----- - -------------- типизованной-->!иденти-!-->(:)-->!тип!-->(=)-->!типизованная!-> константы !фикатор! - ----- - !константа ! --------- -------------- ----------- типизованная----->!константа!------------------------> константа ! ----------- ^ ! ------------------- ! !-->!константа-массив !---------! ! ------------------- ! ! ------------------ ! !-->!константа-запись!----------! ! ------------------ ! ! --------------------- ! !-->!константа-множество!-------! ! --------------------- ! ! --- ! ---------(nil)------------------- --- Типизованные константы можно использовать точно так же, как переменные того же самого типа, и они указываются в ле- вой части оператора присваивания. Отметим, что типизованные константы инициализируются только один раз - в начале выпол- нения программы. Таким образом, при каждом новом входе в процедуру или функцию локально описанные типизованные конс- танты заново не инициализируются. Константы простого типа Описание типизованной константы с простым типом означа- ет указание значения константы: const Maximum : integer = 9999; Factor : real = -0.1; Breakchar : char = #3; Поскольку типизованная константа фактически представля- ет собой переменную со значением константы, она не является взаимозаменимой для обычных констант. Например, она не может использоваться в описании других констант или типов. Const Min : integer = 0; Max : integer = 99; type Vector = array[Min..Max] of integer; Описание Vector является недопустимым, поскольку Min и Max являются типизованными константами. Константы строкового типа Описание типизованной константы строкового типа содер- жит максимальную длину строки и ее начальное значение: const Heading : string[7] = 'Section'; NewLine : string[2] = #13#10; TrueStr : string[5] = 'Yes'; FalseStr : string[5] = 'No'; Константы структурного типа Описание константы структурного типа содержит значение каждого компонента структуры. Турбо-Паскаль поддерживает описания констант типа массив, запись, множество и указа- тель. Константы файлового типа и константы типа массив или запись, содержащие компоненты файлового типа, не допускают- ся. Константы типа массив Описание константы типа массив содержит значения эле- ментов, заключенные в скобки и разделенные запятыми. - ------------------------ - константа-массив-->(()---->!типизованная константа!---->())--> - ^ ------------------------ ! - ! - ! -------------(,)-------------- - Приведем пример константы типа массив: type Status = (Active,Passive,Waiting); StatusMap = array[Status] of string[7]; const StatStr: StatusMap = ('Active','Passive','Waiting'); В этом примере определяется константа-массив StarStr, которая может быть использована для преобразования значений типа Status в соответствующие им строковые представления. Элементами массива StarStr являются: StatStr[Active] = 'Active' StatStr[Passive] = 'Passive' StatStr[Waiting] = 'Waiting' Тип элемента константы-массива может быть любым, кроме файлового типа. Упакованные константы строкового типа (сим- вольные массивы) могут быть определены и как одиночные сим- волы, и как строки. Определение: const Digits:array[0..9] of char=('0','1',12','3','4','5','6','7','8','9'); можно представить в более удобном виде: const Digits: array[0..9] of char = '0123456789'; При описании константы типа "многомерный массив" конс- танты каждой размерности заключаются в отдельные скобки и разделяются запятыми. Расположенные в середине константы со- ответствуют самым правым размерностям. Описание: type Cube = array[0..1,0..1,0..1] of integer; const Maze: Cube = (((0,1),(2,3)),((4,5),(6,7))); задает следующие начальные значения массива Maze: Maze[0,0,0] = 0 Maze[0,0,1] = 1 Maze[0,1,0] = 2 Maze[0,1,1] = 3 Maze[1,0,0] = 4 Maze[1,0,1] = 5 Maze[1,1,0] = 6 Maze[1,1,1] = 7 Константы типа запись Описание константы типа запись содержит идентифи- катор и значение каждого поля, заключенные в скобки и разде- ленные точками с запятой. - --------- - ------------ - константа-запись-->(()-->!иденти-!-->(:)-->!типизован-!-->())-> - ^ !фикатор! - !ная кон- ! ! - ! !поля ! !станта ! ! ! --------- - ------------ ! --------------(;)<---------------- - Приведем несколько примеров констант-записей: type Point = record x,y: real; end; Vector = array[0..1] of Point; Month = (Jan,Feb,Mar,Apr,May,Jun,Jly,Aug,Sep,Oct,Nov,Dec); Date = record d: 1..31; m: Month; y: 1900..1999; end; const Origon : Point = (x: 0.0; y: 0.0); Line : Vector = ((x: -3.1; y: 1.5),(x: 5.8; y: 3.0)); SomeDay : Date = (d: 2; m: Dec; y: 1960); Поля должны указываться в том же порядке, как они сле- дуют в описании типа запись. Если запись содержит поля фай- лового типа, то для этого типа запись нельзя описать конс- танту. Если запись содержит вариант, то можно указывать только поля выбранного варианта. Если вариант содержит поле признака, то его значение должно быть определено. Константы множественного типа Описание константы множественного типа может содержать несколько элементов, заключенных в квадратные скобки и раз- деленных запятыми. Каждый элемент такой константы представ- ляет собой константу или отрезок типа, состоящий из двух констант, разделенных двумя точками. - - константа-множество-->([)---------------------------->(])--> - ! ^ - ! ---------------- ! ---->! константа- !----- ^ ! элемент ! ! ! ---------------- ! ! - ! ----------(,)<---------- - константа- ----------- элемент-------!константа!-----------------------------> ----------- ! ^ ! -- ----------- ! -->(..)-->!константа!--- -- ----------- Приведем несколько примеров констант-множеств: type Digits = set of 0..9; Letters = set of 'A'..'Z'; const EvenDigits: Digits = [0,2,4,6,8]; Vowels : Letters = ['A','E','I','O','U','Y']; HexDigits : set of '0'..'z' = ['0'..'9','A'..'F','a'..'f']; Константы типа указатель Описание константы с типом указатель (которую называют иногда константой ссылочного типа) может содержать только значение nil (пусто). Приведем несколько примеров: type NamePtr = ^NameRec; NameRec = record Next: NamePtr; Name: string[31]; end; const NameList: NamePtr = nil; NoName: NameRec = (Next: nil; Name: ''); ГЛАВА 6 Выражения Выражения состоят из операций и операндов. Большинство операций в языке Паскаль являются бинарными, то есть содер- жат два операнда. Остальные операции являются унарными и со- держат только один операнд. В бинарных операциях использует- ся обычное алгебраическое представление, например: a+b. В унарных операциях операция всегда предшествует операнду, например: -b. В более сложных выражениях порядок, в котором выполня- ются операции, соответствует приоритету операций (см. Табли- цу 6.1). Порядок выполнения операций Таблица 6.1 ------------------------------------------------------------ Операция Приоритет Вид операции ------------------------------------------------------------ @, not первый (высший) унарная операция *, /, div, mod, второй операция умножения and, shl, shr +, -, or, xor третий операция сложения =, <>, <, >, четвертый (низший) операция отношения <=, >=, in ------------------------------------------------------------ Для определении старшинства операций имеется три основ- ных правила: 1. Во-первых, операнд, находящийся между двумя опера- циями с различными приоритетами, связывается с опе- рацией, имеющей более высокий приоритет. 2. Во-вторых, операция, находящаяся между двумя опера- циями с равными приоритетами, связывается с той операцией, которая находится слева от него. 3. В-третьих, выражение, заключенное в скобки, перед выполнением вычисляется, как отдельный операнд. Операции с равным приоритетом обычно выполняются слева направо, хотя иногда компилятор при генерации оптимального кода может переупорядочить операнды. Синтаксис выражений Правила, определяющие порядок выполнения операций, вы- текают из синтаксиса выражений, которые строятся из множите- лей, термов и простых выражений. Множитель имеет следующий синтаксис: ------------------------ множитель-------------------! ссылка на переменную !-------> ! ! ^ ------------------------ ^ ^ ! ! --- ! --------------------------- ! ! ! ->( @ )-->! идентификатор процедуры !-! ! ! --- ! --------------------------- ! ! ! ! ------------------------- ! ! ! ->! идентификатор функции !---- ! ! ------------------------- ! ! ----------------------- ! !->! константа без знака !--------------------->! ! ----------------------- ! ! --- ----------------- --- ! !->( ( )---->! выражение !--->( ) )-------->! ! --- ----------------- --- ! ! ----- ------------- ! !->( not )--->! множитель !-------------------->! ! ----- ------------- ! ! ----------------- ! !->! вызов функции !--------------------------->! ! ----------------- ! ! ----------------------- ! !->! описатель множества !--------------------->! ! ----------------------- ! ! ---------------------------- ! !->! приведение типа значения !------------------ ---------------------------- Вызов функции активизирует функцию и представляет собой значения, возвращаемые функцией (см. далее в этой главе раз- дел "Вызовы функций"). Описатель множества представляет со- бой значение множественного типа (см. раздел, озаглавленный, как "Описание множеств"). Приведение типа изменяет тип зна- чения (см. "Приведение типа"). Константа без знака имеет следующий синтаксис: ------------------- Константа без знака ----->! число без знака !------------> ! ------------------- ^ ! --------------------- ! !-->! символьная строка !--------! ! --------------------- ! ! --------------------------- ! !-->! идентификатор константы !--! ! --------------------------- ! ! ------ ! !-->( nil )---------------------! ------ Некоторые примеры множителей могут включать в себя: Х (ссылка на переменную) @Х (указатель на переменную) 15 (константа без знака) (Х+Y+Z) (подвыражение) SIN(Х/2) (вызов функции) ['0..''9','А'..'Z'] (описатель множества) not Done (отрицание булевской переменной) сhar(Digit+48) (назначение типа) Термы используются в операциях умножения на множитель: ------------- терм ----------------->! множитель !--------------> ^ ------------- ! ! --- ! !--------------( * )<----------! ! --- ! ! --- ! !--------------( / )<----------! ! --- ! ! ----- ! !-------------( div )<---------! ! ----- ! ! ----- ! !-------------( mod )<---------! ! ----- ! ! ----- ! !-------------( and )<---------! ! ----- ! ! ----- ! !-------------( shl )<---------! ! ----- ! ! ----- ! --------------( shr )<---------- ----- Приведем несколько примеров термов: Х*Y Z/(1-Z) Done or Error (Х <= Y) and (Y < Z) В простых выражениях к термам применяются операции сло- жения и присваивания знака: -------- простое выражение------------------------>! терм !-------> ^ -------- ! ! ! ! ---- ! !----( + )<---! ! ---- ! ! ---- ! !----( . )<---! ! ---- ! ! ---- ! !----( оr )<---! ! ---- ! ! ----- ! -----( хоr )<--- ----- Приведем несколько примеров простых выражений: Х+Y -Х Hue1 + Hue2 I*J + 1 В выражениях к простым выражениям применяются операции отношения. Приведем некоторые примеры выражений: Х = 1.5 Done <> Error (I < J) = (J < К) C in Huel --------------------- выражение-! простое выражение !----------------------------> --------------------- ! ^ ! --- ------------- ! !->( < )-->! простое !-- ! --- ^ ! выражение ! ! --- ! ------------- !->(<= )-! ! --- ! ! --- ! !->( > )-! ! --- ! ! --- ! !->( = )-! ! --- ! ! --- ! !->(>= )-! ! --- ! ! --- ! !->(<> )-! ! --- ! ! --- ! -->(in )-- --- Операции Операции подразделяются на арфметические операции, ло- гические операции, строковые операции, операции над множест- вами, операции отношения и операцию @ (операция получения адреса). Арифметические операции В следующей таблице приведены типы операндов и резуль- таты для бинарных и унарных арифметических операций: Бинарные арифметические операции Таблица 6.2 ----------------------------------------------------------- Операция Действие Типы операндов Тип результата ----------------------------------------------------------- + Сложение Целый Целый Вещественный Вещественный - Вычитание Целый Целый Вещественный Вещественный * Умножение Целый Целый Вещественный Вещественный div Деление Целый Вещественный Вещественный Вещественный mod Целочисленное деление Целый Целый Остаток Целый Целый ---------------------------------------------------------- Примечание: Операция + используется также, как опера- ция для работы со строками и множествами. Операции +, - и * используются также для операций над множествами. Унарные арифметические операции Таблица 6.3 ---------------------------------------------------------- Операция Действие Тип операнда Тип результата ---------------------------------------------------------- + Сохранение знака Целый Целый Вещественный Вещественный - Отрицание знака Целый Целый Вещественный Вещественный ---------------------------------------------------------- Любая операция, включающая операнд, тип которого явля- ется подмножеством перечислимого типа, обрабатывается также, как если бы он был перечислимого типа. Если оба операнда в операциях +, -, *, div или моd яв- ляются операндами целого типа, то тип результата будет таким же, как общий тип обоих операндов. (Определение общего типа см. в разделе "Целый тип" в Главе 3). Если один или более операндов в операциях +, -, или * имеют вещественный тип, то тип результата будет веществен- ным, если использована директива компилятора $N-, или типом с повышенной точностью при использовании директивы компиля- тора $N+. Если при использовании операции сохранения знака или операции отрицания знака операнд имеет целый тип, то резуль- тат будет тоже целого типа. Если операнд вещественного типа, то тип результата будет вещественным или типом с повышенной точностью. Значение выражения х/у всегда будет вещественного типа или с повышенной точностью, независимо от типов операндов. Если у равно 0, то результат будет ошибочным. Значение выражение i div j представляет собой математи- ческое частное от i/j, округленное в меньшую сторону до зна- чения целого типа. Если j равно 0, результат будет ошибоч- ным. Операция mod возвращает остаток, полученный путем деле- ния двух ее операндов, то есть: i mod j = i - (i div j) * j Знак результата операции mod будет тем же, что и знак i. Если j равно нулю, то результатом будет ошибка. Логические операции Типы логических операций показаны в Таблице 6.4. Если операндом операции not является операнд целого типа, то результат будет также целого типа. Если оба операнда в операциях or, and или xor целого типа, то тип результата будет таким же, как тип обоих опе- рандов. Логические операции Таблица 6.4 ---------------------------------------------------------- Операция Действие Типа операндов Тип результата ---------------------------------------------------------- not Отрицание (битовое) Целый Целый and И (битовое) Целый Целый or ИЛИ (битовое) Целый Целый xor Исключающее ИЛИ Целый Целый (битовое) shl Сдвиг влево Целый Целый shr Сдвиг вправо Целый Целый ---------------------------------------------------------- Примечание: Операция not является унарной операцией. Операции i shl j и i shr j сдвигают значение i влево или вправо на j битов. Тип результата будет таким же, как тип i. Булевские операции Типы операндов и результат для булевских операций пока- заны в Таблице 6.5. Булевские операции Таблица 6.5 ----------------------------------------------------------- Операция Действие Типы операндов Тип результата ----------------------------------------------------------- not Отрицание Булевский Булевский and Логическое И Булевский Булевский or Логическое ИЛИ Булевский Булевский xor Логическое исключающее ИЛИ Булевский Булевский ----------------------------------------------------------- Примечание: Операция not является унарной операцией. Результаты этих операций соответствуют обычной булевой логике. Например, выражение a and b является истинным (при- нимает значение Тruе) только в том случае, если оба операнда a и b имеют истинное значение (Тruе). В Турбо-Паскале поддерживаются две различные модели ге- нерации кода для операций or и and - полное вычисление и вы- числение по короткой схеме (частичное вычисление). При полном вычислении подразумевается, что каждый опе- ранд булевского выражения, построенный с помощью операций or и and, всегда будет вычисляться, даже если результат всего выражения уже известен. Эта модель полезна в том случае, когда один или более операндов в выражении представляют со- бой функции с побочными эффектами, которые изменяют смысл программы. Вычисление по короткой схеме обеспечивает строгое вы- числение слева направо. Это вычисление прекращается, как только результат всего выражения становится очевиден. Во многих случаях эта модель удобна, поскольку она обеспечивает минимальное время выполнения и, как правило, минимальный объем кода. Вычисление по короткой схеме делает также воз- можными такие конструкции, которые в противном случае были ба недопустимы, например: while (I<=Lenght(S)) and (S[I]<>' ') do Inc(I); while (P<>nil) and (P^.Value<>5) do P:=P^.Next; В обоих случаях, если результатом первого вычисления будет значение false, вычисление второго выражения не выпол- няется. Схему вычисления можно задавать с помощью директивы компилятора $В. Значением по умолчанию является состояние {$В-} (пока оно не будет изменено с помощью "меню" возмож- ностей компилятора). В этом случае генерируется код с вычис- лением по короткой схеме. В случае директивы {$В+} генериру- ется код с полным вычислением. Поскольку в стандартном Паскале не определяется, какую схему следует использовать для вычисления булевских выраже- ний, то программы, зависящие от действия какой-либо конкрет- ной схемы, в действительности не являются переносимыми. Од- нако, если пожертвовать переносимостью, то очень часто можно получить значительный выигрыш во времени выполнения и прос- тоте, которую позволяет получить вычисление по короткой схе- ме. Операция со строками Типы операндов и результаты для операции со строками показаны в Таблице 6.6. Операции со строками Таблица 6.6 --------------------------------------------------------- Операция Действие Типы операндов Тип результата --------------------------------------------------------- + Конкатенация Строковый, Строковый символьный или упакованный строковый --------------------------------------------------------- Турбо-Паскаль позволяет использовать операцию + для объединения двух строковых операндов. Результатом операции s + t, где s и t имеют строковый тип, символьный тип или упа- кованный строковый тип, будет конкатенация s и t. Результат будет совместим с любым строковым типом (но не с символьным и не с упакованным строковым типом). Если длина результирую- щей строки превышает 255 символов, то она усекается до 255 символов. Операции над множествами Типы операндов для операций над множествами показаны в Таблице 6.7. Операции над множествами Таблица 6.7 ----------------------------------------------------------- Операция Действие Типы операндов ----------------------------------------------------------- + Объединение Множества с совместимыми типами - Разность Множества с совместимыми типами * Пересечение Множества с совместимыми типами ----------------------------------------------------------- Результаты операций соотвествуют правилам логики работы с множествами: 1. Перечислимое значение c содержится в a+b только тогда, когда оно содержится в a или в b. 2. Перечислимое значение c содержится в a-b только тогда, когда оно содержится в a и не содержится в b. 3. Перечислимое значение c содержится в a*b только тогда, когда он содержится в обоих множествах a и b. Если наименьшим перечислимым значением, которое являет- ся членом результата операций над множествами, является a, а наибольшиим - b, то типом результата будет множество a..b. Операции отношения Типы операндов и результаты операций отношения приведе- ны в Таблице 6.8. Операции отношения Таблица 6.8 ----------------------------------------------------------- Операция Действие Типы операндов Тип результата ----------------------------------------------------------- = Равно Совместимый простой, Булевский указатель, множествен- ный строковый или упа- кованный строковый <> Не равно Совместимый простой, Булевский указатель, множествен- ный, строковый или упа- кованный строковый < Меньше чем Совместимый простой, Булевский указатель, множествен- ный, строковый или упа- кованный строковый > Больше чем Совместимый простой, Булевский указатель, множествен- ный строковый или упа- кованный строковый <= Меньше Совместимый простой, Булевский или равно указатель, множествен- ный строковый или упа- кованный строковый >= Больше Совместимый простой, Булевский или равно указатель, множествен- ный строковый или упа- кованный строковый <= Подмножество Множества совместимых Булевский типов >= Надмножество Множества совместимых Булевский типов in Элемент Левый операнд: любой Булевский множества перечислимый тип t; правый: множество, совместимое с t. ------------------------------------------------------------ Сравнение простых типов Когда операции =, <>, <, >, >= или <= применяются для операндов простых типов, то это должны быть совместимые ти- пы. Однако, если один операнд имеет действительный тип, то другой может быть целого типа. Сравнение строк Операции отношения =, <>, <, >, >= или <= могут приме- нятся для сравнения строк согласно порядку расширенного на- бора символов кода АSСII. Любые два значения строковых дан- ных можно сравнить, поскольку все значения строковых данных совместимы. Значения символьного типа совместимы со значениями строкового типа, и при их сравнении символьное значение об- рабатывается как строковое значение с длиной 1. Когда со значением строкового типа сравнивается упакованное строковое значение из n элементов, то оно обрабатывается, как значение строкового типа длиной n. Сравнение упакованных строк Операции отношения =, <>, <, >, >= или <= могут приме- нятся также для двух упакованных значений строкового типа, если они содержат одинаковое число элементов. Если число элементов равно n, то операция соотвествует сравнению двух строк, каждая из которых имеет длину n. Сравнение указателей Операции = и <> могут использоваться для сравнения опе- рандов типа указатель. Два указателя равны только в том слу- чае, если они ссылаются на один и тот же объект. Примечание: При сравнении указателей в Турбо-Паскале просто сравниваются сегменты и смещения. В соответст- вии со схемой размещения сегментов процессоров 80х86 два логически различных указателя могут фактически указывать на одну и ту же физическую ячейку памяти. Например $0040:$0049 и $0000:$0449 являются указателя- ми с одинаковыми физическими адресами. Указатели, возвращаемые стандартными процедурами New и GetMem всегда нормализованы (смещение находится в диапазоне от $0000 до $000F) и, таким образом, всегда будут сравниваться корректно. При создании указателей с по- мощью стандартной функции Рtr, и последующем сравнении таких указателей нужно принимать во внимание указанный особый случай. Сравнение множеств Если операндами являются множества a и b, то при их сравнении получаются следующие результаты: 1. Выражение a=b истинно только когда a и b содержат одни и те же элементы, в противном случае a<>b. 2. Выражение a = b истинно, когда каждый элемент мно- жества а является также элементом множества b. 3. Выражение a = b истинно, когда каждый элемент мно- жества в является также элементом множества a. Проверка на принадлежность к множеству Операция in возвращает истинное значение (Тrue), когда значение элемента перечислимого типа является элементом опе- ранда множественного типа, в противном случае он возвращает значение False. Операция @ С помощью операции @ можно создать указатель на пере- менную. В Таблице 6.9 показан операнд и типы результата. Операция создания указателя Таблица 6.9 ------------------------------------------------------------ Операция Действие Типы операндов Тип результата ------------------------------------------------------------ @ Получение Ссылка на переменную, Указатель указателя процедуру или иденти- (совмести- фикатор функции мый с nil) ------------------------------------------------------------ Операция @ является унарной операцией, в качестве опе- ранда которой используется ссылка на переменную, процедура или идентификатор функции и операнду возвращается указатель. Тип этого значения является таким же, как тип указателя nil, и, таким образом, его можно присвоить любому указателю пере- менной. Использование операции @ для переменной Использование операции @ для обычной переменной (не па- раметра) не вызывает никаких сложностей. Введем описания: type TwoChar = array[0..1] of char; var Int: integer; TwoCharPtr: ^TwoChar; тогда оператор TwoCharPtr := @Int; приводит к тому, что TwoCharPtr для получения ссылки на TwoCharPtr^ становится повторной интерпретацией значения Int, как если бы оно было символьным массивом array[0..1]. Использование операции @ для параметра-значения Использование операции @ для формального параметра-зна- чения приводит к тому, что будет построен указатель, указы- вающий на ячейку стека, в которой содержится фактическое значение параметра. Предположим, что Fоо является формальным параметром-значением процедуры, а FооРtr^ является указате- лем-переменной. Если в процедуре выполняется операция: FooPtr := @Foo; то FооРtr^ будет ссылкой на значение Fоо. Однако, FооРtr^ не указывает на сам параметр Fоо, поскольку он указывает на значение Fоо, которое было взято из Fоо и сохранено в стеке. Использование операции @ для параметра-переменной Применение операции @ к параметру-переменной приведет к тому, что будет сформирован указатель на фактический пара- метр (указатель берется из стека). Предположим, что Оnе - параметр-переменная процедуры, Тwо - переменная, передавае- мая в процедуру в качестве фактического параметра-переменной Оnе, а ОnеРtr является указателем на переменную. Если в про- цедуре выполняется оператор: OnePtr := @One; то ОnеРtr является указателем на переменную Тwо, а ОnеРtr^ - ссылка на саму переменную Тwо. Использование операции @ для процедуры или функции Вы можете применять операцию @ к процедуре или функции. При этом вы получите указатель на ее точку входа. В Тур- бо-Паскале не предусмотрен механизм для использования такого указателя. Единственным применением указателя процедуры мо- жет быть передача его программе на языке ассемблера для ис- пользования в операторе. Вызовы функции Вызовы функции приводят к активизации функции, заданной с помощью идентификатора функции. Идентификатором функции является любой идентификатор, использованный для обозначения функции. Если в соответствующем описании функции содержится спи- сок формальных параметров то в вызове функции должен содер- жаться список фактических параметров. Каждый параметр подс- тавляется вместо соответствующего формального параметра в соответствии с набором правил, который вводится в Главе 22. ------------------------- Вызов функции -->! идентификатор функции !-----------------> ------------------------- ! ^ ------- ! ! ---------------- ! ! ! список ! ! ->! фактических !--- ! параметров ! ---------------- --- ------------------------ --- Список факти- -->( ( )-->! фактический параметр !-->( ) )--> ческих пара- --- ^ ------------------------ ! --- метров ! --- ! ----------( , )<------------ --- ------------- Фактический параметр --->! выражение !--------------> ! ------------- ^ ! ------------------------ ! ---! ссылка на переменную !-- ------------------------ Приведем некоторые примеры вызовов функций: Sum(A,63) Maximum(147,J) Sin(X+Y)ь Зрз(И) Шрнцож(Фгекцф,Кжкийх) Описатели множества Описатель множества определяет значения множественного типа и получается путем записи выражений, заключенных в квадратные скобки ([]). Каждое выражение определяет значение множества. --- --- Описатель множества->( [ )----------------------------( ] )-> --- ! ^ --- ! -------------------- ! -->! группа элементов !--- ^ -------------------- ! ! --- ! --------( , )<---------- --- ------------- Группа элементов ->! выражение !---------------------------> ------------- ! ^ ! ---- ----------- ! ->( .. )->!выражение!--- ---- ----------- Обозначение [ ] означает пустое множество, тип которого совместим по присваиванию с типом любого множества. Любая группа элементов, описанная, как х..у, объявляет элементами множества все значения в диапазоне х..у. Если х больше, чем у, то х..у не описывает никаких элементов и обозначает пус- тое множество. В конкретном описателе множества все значения выражения в группах элементов должны быть одного перечислимого типа. Приведем некоторые примеры описателей множеств: [red, C, green] [1,5,10..K mod 12, 13, 23] ['A'..'Z', 'a'..'z', Chr(Digit+48)] Приведение типа значений Тип выражения можно изменить на другой тип с помощью приведения типа значений. ------------- --- ----------- --- Приведение ---->!идент. типа!->( ( )->!выражение!->( ) )--> типа значений ------------- --- ---------- --- Тип выражения и задаваемый тип должны оба иметь пере- числимый тип или тип указателей. Для перечислимых типов ре- зультирующее значение получается путем преобразования выра- жения (и возможной проверки на нахождение в допустимых гра- ницах). Преобразование может привести к усечению или увели- чению размера исходного значения в том случае, если вновь определяемый тип отличается от типа выражения. В том случае, когда значение расширяется, его знак всегда сохраняется. Та- ким образом, значение является расширяемым по знаку. Синтаксис приведения типа значений почти совпадает с синтаксисом приведения типа переменных (см. раздел "Приведе- ние типа переменных" в Главе 4). Однако при приведении типа значений операции производятся со значениями, а не с пере- менными и, таким образом, могут не участвовать в ссылках на переменные. То есть за приведением типа значения не обяза- тельно следуют квалификаторы. В частности, приведение типа значений не должно встречаться в левой части оператора прис- ваивания. Некоторые примеры приведения типа значений включают в себя: Intereg('A') char(48) boolean(0) Color(2) IntPtr(@Buffer) BytePtr(Ptr($40,$49) ГЛАВА 7 Операторы Операторы описывают те алгоритмические действия, кото- рые должны выполняться. Операторам могут предшествовать мет- ки, которые можно использовать для ссылок в операторах пере- хода. Оператор --------------------------------------------------> ! ------- --- ^ ! -------------------- ^ ->!метка!->( : )-! !--->! простой оператор !---! ------- --- ! -------------------- ! ! --------------------- ! ---->! структурный !--- ! оператор ! --------------------- Простые операторы Простым оператором является такой оператор, который не содержит в себе других операторов. ------------------------- Простой оператор ---------->! оператор присваивания !-----> ! ------------------------- ^ ! ------------------------- ! !----->! оператор процедуры !----! ! ------------------------- ! ! ------------------------- ! ------>! оператор перехода !----- ------------------------- Оператор присваивания Оператор присваивания заменяет текущее значение пере- менной новым значением, которое определяется выражением, или определяет выражение, значение которого должно возвращаться функцией. ------------------------ ---- Оператор --->! ссылка на переменную !------>( := )-- присваивания ! ------------------------ ^ ---- ! ! ------------------------- ! ! ->! идентификатор функции !----- ! ------------------------- ------------ ! ------------- -->! выражение !-> ------------- Выражение должно быть совместимо по присваиванию с ти- пом переменной или типом значения, возвращаемого функцией в качестве результата (см. раздел "Совместимость типов" в Гла- ве 3 "Типы"). Приведем некоторые примеры операторов присваивания: X := Y + Z Done := (I>=1) and (I<100); Huel := [blue,Succ(C)]; I := Sqr(J) - I*K; Операторы процедуры Оператор процедуры определяет активизацию процедуры, обозначенную с помощью идентификатора процедуры. Если соот- ветствующее описание процедуры содержит список формальных параметров, то оператор процедуры должен содержать в себе соответствующий ему список фактических параметров (парамет- ры, список которых приводится в определении, являются фор- мальными параметрами, а в операторе вызова процедуры они яв- ляются фактическими параметрами). При вызове происходит пе- редача фактических параметров формальным параметрам. --------------------------- Оператор --->! идентификатор процедуры !------------------> процедуры --------------------------- ! ^ ! ---------- ! ! ! список ! ! ! ! факти- ! ! ->! ческих !-- ! пара- ! ! метров ! ---------- Приведем некоторые примеры операторов процедур: PrintHeaing; Transpose(A,N,M); Fin(Name,Address); Операторы перехода Оператор перехода вызывает передачу управления операто- ру, которому предшествует метка, указанная в данном операто- ре перехода. Синтаксическая схема оператора перехода имеет следующий вид: ------- --------- Оператор перехода ----->( gоtо )------>! метка !------> ------- --------- При использовании оператора перехода должны соблюдаться следующие правила: 1. Метка, которая указывается в операторе перехода, должна находиться в том же блоке или модуле, что и сам оператор перехода. Другими словами, не допуска- ются переходы из процедуры или функции или внутрь нее. 2. Переход извне внутрь структурного оператора (то есть переход на более глубокий уровень вложенности) может вызвать непредсказуемые эффекты, хотя компи- лятор не выдает сообщения об ошибке. Структурные операторы Структурные операторы строятся из других операторов, порядок выполнения которых должен быть последовательным (составные операторы и операторы над записями), определяемым условной передачей управления (условные операторы) или пов- торяющимся (операторы цикла). ---------------------- Структурный оператор ------>! составной оператор !--------> ! ---------------------- ^ ! ---------------------- ! !--->! условный оператор !----->! ! ---------------------- ! ! ------------------------ ! !-->! оператор цикла !---->! ! ------------------------ ! ! ------------------------- ! --->! оператор над записями !----- ------------------------- Составные операторы Составные операторы задают порядок выполнения операто- ров, являющихся их элементами. Они должны выполняться в том порядке, в котором они записаны. Составные операторы обраба- тываются, как один оператор, что имеет решающее значение там, где синтаксис Паскаля допускает использование только одного оператора. Операторы заключаются в ограничители begin и end, и отделяются друг от друга точкой с запятой. ------- ------------ ----- Составной оператор -->( begin )-->! оператор !-->( end )--> ^ ------- ------------ ! ----- ! --- ! -----------------( ; )<----- --- Приведем пример составного оператора: begin Z := X; X := Y; Y := Z; end; Условные операторы Условные операторы позволяют выбрать для выполнения один из составных операторов (или не выбрать ни одного). ----------------- Условный оператор -------------->! оператор if !--------> ! ----------------- ^ ! ---------------------- ! --->! оператор case !--------- ---------------------- Оператор if Синтаксис оператора if можно представить следующим об- разом: ---- ----------- ------ ---------- Оператор if->( if )->!выражение!->( then )->!оператор!----- ---- ----------- ------ ---------- ! ! ------------------------------------- ! ! ------ -------------- ! -->( else )--->! оператор !-----------> ------ -------------- В выражении должен получаться результат, имеющий стан- дартный булевский тип. Если результатом выражения является истинное значение (Тrue), то выполняется оператор, следующий за ключевым словом then. Если результатом выражения является значение False и присутствует ключевое слово else, то выполнятся оператор, следующий за ключевым словом else. Если ключевое слово else отсутствует, то никакой оператор не выполняется. Синтаксическая неоднозначность, возникающая в конструк- ции: if e1 then e2 else e3 разрешается путем следующей интерпретации этой конструкции: if e1 then begin if e2 then s1 else s2 end В общем случае ключевое слово else связывается с бли- жайшим ключевым словом if, которое еще не связано с ключевым словом else. Приведем два примера оператора if: if X < 1.5 then Z := X+Y else Z := 1.5; if P1 <> nil then P1 := P1^.father; Оператор варианта (case) Оператор варианта (casе) состоит из выражения (переклю- чателя) и списка операторов, каждому из которых предшествует одна или более констант (они называются константами выбора) или ключевое слово else. Переключатель должен иметь перечис- лимый тип и перечислимые значения верхней и нижней границы этого типа должны лежать в диапазоне от -32768 до 32767. Та- ким образом, строковый тип и длинный целый тип являются не- допустимыми типами переключателя. Все константы выбора дожны быть уникальными и иметь перечислимый тип, совместимый с ти- пом переключателя. ------ ----------- ---- -------- Оператор --->( casе )-->!выражение!->( оf )---! casе !---- варианта ------ ----------- ---- ^ -------- ! ! ! --- ! ! ---( ; )---- ! --- ! --------------------------------------------------- ! ----- ----------------------------------------->( еnd )--> ! -------------- ^ ! --- ^ ----- -->! ветвь else !----! --->( ; )---! -------------- --- -----------------------> ! ! ------------- ! ---- ----------- ! --- casе ->! константа !-->( .. )->!константа!--->( : )--- ^ ------------- ---- ----------- ! --- ! ! ---- ! -------- ------------------( , )<--------------- ! ------------ ---- ->! оператор !-> ------------ ------ ------------ ветвь else ------>( else )-->! оператор !--------------> ------ ------------ Оператор варианта приводит к выполнению оператора, ко- торому предшествует константа выбора, равная значению перек- лючателя или диапазону выбора, в котором находится значение переключателя. Если такой константы выбора или такого диапа- зона выбора не существует и присутствует ветвь else, то вы- полнятся оператор, следующий за ключевым словом else. Если же ветвь else отсутствует, то никакой оператор не выполняет- ся. Приведем некоторые примеры оператора варианта: case Operator of plus: X := X+Y; minus: X := X-Y; times: X := X*Y; end; Оператор цикла Оператор цикла задает повторное выполнение определенных операторов. ------------------- Оператор цикла ------------>! оператор repeat !----------> ! ------------------- ^ ! ------------------- ! !--->! оператор while !----! ! ------------------- ! ! ------------------- ! ---->! оператор for !----- ------------------- Если число повторений заранее известно, то подходящей конструкций является оператор for. В противном случае следу- ет использовать операторы while или repeat. Оператор цикла с пост-условием (repeat) В операторе цикла с пост-условием (начинающимся со сло- ва repeat) выражение, которое управляет повторным выполнени- ем последовательности операторов содержится внутри оператора repeat. -------- ---------- ------- ---------- Оператор->( repeat )---!оператор!-->( until )->!оператор!--> цикла с -------- ^ ---------- ! ------- ---------- пост-условием ! --- ! -----( ; )<--- --- Результатом выражения должен быть результат булевского типа. Операторы, заключенные между ключевыми словами repeat и until, выполняются последовательно до тех пор, пока ре- зультат выражения не примет значение Тrue. Последователь- ность операторов выполнится по крайней мере один раз, пос- кольку вычисление выражения производится после каждого вы- полнения последовательности операторов. Приведем примеры оператора цикла с пост-условием: repeat K := I mod J; I := J; J := K; until J = 0; repeat Write('Введите значение (0..9):'); Readln(I); until (I >= 0) and (I <= 9); Операторы цикла с пред-условием (while) Оператор цикла с пред-условием (начинающийся со слова while) содержит в себе выражение, которое управляет повтор- ным выполнением оператора (который может быть составным опе- ратором). ----- ----------- ---- ---------- Оператор цикла->(while)-->!выражение!->( dо )->!оператор!---> с пред-условием ----- ----------- ---- ---------- Выражение, с помощью которого осуществляется управление повторением оператора, должно иметь булевский тип. Вычисле- ние его производится до того, как внутренний оператор будет выполнен. Внутренний оператор выполнятся повторно до тех пор, пока выражение принимает значение Тruе. Если выражение с самого начала принимает значение False, то оператор, со- держащийся внутри оператора цикла с пред-условием, не выпол- няется. Примерами оператора цикла в пред-условием могут слу- жить: while Data[I] <> X do I := I + 1; While I > 0 do begin if Odd(I) then Z := Z * X; I := I div 2; X := Sqr(X); end; while not Eof(InFile) do begin Readln(InFile,Line); Process(Line); end; Операторы цикла с параметром (for) Операторы цикла с параметром (которые начинаются со слова for) вызывает повторяющееся выполнение оператора (ко- торый может быть составным оператором) пока управляющей пе- ременной присваивается возрастающая последовательность зна- чений. ----- ------------------------ ---- Оператор цикла->( for )->!управляющая переменная!->( := )-- с параметром ----- ------------------------ ---- ! -------------------------- ! ----------------! начальное значение !<-------- ! -------------------------- ! ---- !-->( tо )------ ! ---- ! ------------------- ---- ! !->!конечное значение!->( dо )---- ! -------- ! ------------------- ---- ! ->( downto )---- --------------------------- -------- ! ------------------- -->! оператор !-------> ------------------- ---------------------------- Управляющая переменная -->! идентификатор переменной !---> ---------------------------- ------------- Начальное значение ------>! выражение !------------------> ------------- ------------- Конечное значение ------->! выражение !------------------> ------------- В качестве управляющей переменной должен использоваться идентификатор переменой (без какого-либо квалификатора), ко- торый обозначает переменную, объявленную локальной в блоке, в котором содержится оператор for. Управляющая переменная должна иметь перечислимый тип. Начальное и конечное значения должны иметь тип, совместимый по присваиванию с перечислимым типом. Когда начинает выполняться оператор for, начальное и конечное значения определяются один раз, и эти значения сох- раняются на протяжении всего выполнения оператора for. Оператор, который содержится в теле оператора for, вы- полняется один раз для каждого значения в диапазоне между начальным и конечным значением. Управляющая переменная всег- да инициализируется начальным значением. Когда работает опе- ратор for, значение управляющей переменной увеличивается при каждом повторении на единицу. Если начальное значение превы- шает конечное значение, то содержащийся в теле оператора for оператор не выполнятся. Когда в операторе цикла используется ключевое слово downto, значение управляющей переменной уменьшается при каждом повторении на единицу. Если начальное значение в таком операторе меньше, чем конечное значение, то содержащийся в теле оператора цикла оператор не выполнятся. Если оператор, содержащийся в теле оператора for, изме- няет значение управляющей переменной, то это является ошиб- кой. После выполнения оператора for значение управляющей переменной становится неопределенным, если только выполнение оператора for не было прервано с помощью оператора перехода. Если принять во внимание эти ограничения, то оператор for V := Expr1 to Expr2 do Body; эквивалентен оператору: begin Temp1 := Expr1; Temp2 := Expr2; if Temp1 <= Temp2 then begin V := Temp1; Body; while V <> Temp2 do begin V := Succ(V); Body; end; end; end; и оператор цикла for V := Expr1 downto Exp2 do Body; эквивалентен операторам: begin Temp1 := Expr1; Temp2 := Expr2; if Temp1 >= Temp2 then begin V := Temp1; Body; while V <> Temp2 o begin V := Pred(V); Body; end; end; end; где Temp1 и Temp2 - вспомогательные переменные, тип которых совпадает с основным типом переменной V и которые не встре- чаются в другом месте программы. Приведем примеры оператора цикла с параметром: for I := 2 to 63 do if Data[I] > Max then Max := Data[I] for I := 1 to 10 do for J := 1 to 10 do begin X := 0; for K := 1 to 10 do X := X + Mat1[I,K]*Mat2[K,J]; Mat[I,J] := X; end; for C := red to blue do Check(C); Операторы присоединения (with) В операциях над записями оператор присоединения (опера- тор with) удобно использовать для краткого обращения к полям записи. В операторе присоединения к полям одной или более конкретных переменных типа запись можно обращаться, исполь- зуя только идентификаторы полей. Оператор присоединения име- ет следующий синтаксис: ------ ---------------------- ---- Оператор --->( with )-->! ссылка на пере- !-->( dо )-- присоединения ------ ^ ! менную типа запись ! ! ---- ! ! ---------------------- ! ! ! --- ! ! ----------( , )<---------- ! --- ! ------------------------------- ! -------------------- ---->! оператор !---> -------------------- ------------------------ Ссылка на переменную ------>! ссылка на переменную !-----> типа запись ------------------------ Приведем пример оператора with: with Date do if month = 12 then begin month := 1; year := year + 1 end else month := month + 1; Это эквивалентно следующему: if Date.month = 12 then begin Date.month := 1; Date.year := Date.year + 1 end else Date.month := Date.month + 1; В операторе присоединения сначала производится проверка каждой ссылки на переменную, а именно: можно ли ее интерпре- тировать, как поле записи. Если это так, то она всегда инте- рпретируется именно таким образом, даже если имеется доступ к переменной с тем же именем. Допустим объявлены следующие переменные: type Point = record x,y: integer; end; var x: Point; y: Integer; В этом случае и к x, и к y можно обращаться, как к пе- ременной или как к полю записи. В операторе: with x do begin x := 10; y := 25; end; x между ключевыми словами with и dо относится к переменной типа указатель, а в составном операторе x и y ссылаются на x.x и y.y. Оператор with V1,V2,...Vn do s; эквивалентен операторам: with V1 do with V2 do ... with Vn do S; В обоих случаях, если Vn является полем и v1, и v2, то она интерпретируется как v2.Vn, а не как v1.Vn. Если выборка переменной типа запись связана с индекси- рованием массива или вычислением указателя, то эти действия производятся до того, как будет выполняться составной опера- тор. ГЛАВА 8 Процедуры и функции Процедуры и функции позволяют включать в основной прог- раммный блок дополнительные блоки. Каждое описание процедуры или функции содержит заголовок, за которым следует прог- раммный блок. Процедура активизируется с помощью оператора процедуры. Функция активизируется при вычислении выражения, содержащего вызов функции и возвращаемое функцией значение подставляется в это выражение. В данной главе обсуждаются различные способы описания процедуры или функции и их параметры. Описания процедур Описание процедуры позволяет связать идентификатор с процедурным блоком. Процедуру можно затем активизировать с помощью оператора процедуры. ------------- - ------------------ - Описание -->! заголовок !->(;)->! тело процедуры !->(;)--> процедуры ! процедуры ! - ------------------ - ------------- ----------- --------------- Заголовок ->( procedure )->!идентификатор!-----------------> процедуры ----------- --------------- ! ^ --------- ! ! ------------------ ! ! ! список ! ! --! формальных !-- ! параметров ! ------------------ В заголовке процедуры указывается имя процедуры и опи- сывается список формальных параметров (если он присутству- ет): ---------------------- Тип параметра ------->! идентификатор типа !---------> ^ ---------------------- ! ! -------- ! ------------( строка )<---------- -------- -------- Тело ------------------------------->! блок !----> процедуры ! ------------ --- ^ ! -------- ^ !-->( interrupt )->( ; )-! ! ! ! ------------ --- ! ! ! ---------------- ! ! ! --------- ! ! !->( forward )------------->! ! ! --------- ! ! ! ---------- ! ! !->( external )------------>! ! ---------- ! ! -------------------------- ! ----->( директива 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; В описании процедуры перед блоком операторов может ука- зыватся директива прерывания (interrupt). Процедура в этом случае рассматривается, как процедура прерывания. Полное описание процедур прерывания приводится в Главе 15 ("Внут- ренняя организация Турбо-Паскаля". В данный момент отметим, что процедуры обработки прерывания нельзя вызывать с помощью операторов процедуры, и что в каждой из них задается список параметров, который обязательно должен иметь следующий вид: 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; Определяющее описание процедуры может быть внешним опи- санием. Однако, оно не может быть внутренним описанием или другим опережающим описанием. Определяющее описание также не может содержать директиву interrupt. Опережающие описания не допускаются в интерфейсной час- ти модуля. Внешние описания (external) Описания external позволяют связывать отдельно скомпи- лированные процедуры и функции, написанные на языке ассем- блера. С помощью команды "$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: procedure DisableInterrupts: inline($FA); { CLI } procedure EnableInterrupts; inline($FB); { STI } Процедуры типа inline описаны подробно в Главе 15 ("Внутренняя организация Турбо-Паскаля"). Описания функций Описание функции определяет часть программы, в которой вычисляются и возвращается значение. ------------- - -------------- - Описание-->! заголовок !->(;)-->!тело функции!-->(;)----> функции ! функции ! - -------------- - ------------- В заголовке функции определяется идентификатор функции, формальные параметры (если они имеются) и тип результата функции. ---------- ----------------- Заголовок ->( function )->! идентификатор !---------------- функции ---------- ----------------- ! ^ ! ------ ! ! ! -------------- ! ! ! ! список ! ! ! ->! формальных !-- ! ! параметров ! ! -------------- ! --------------------------------------------- ! --- ---------------------- -->( : )---->! тип результата !----------> --- ---------------------- ---------------------- Тип результата ----------->! идентификатор типа !----------> ! ---------------------- ! ! -------- ! ---------->( строка )-------------- -------- -------- Тело функции ---------->! блок !--------------------------> ! -------- ^ ! --------- ! !---->( forward )------------>! ! --------- ! ! ---------- ! !---->( external )----------->! ! ---------- ! ! -------------------- ! ----->! директива 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; Аналогично процедурам функции могут описываться, как опережающие, внешние или внутренние. Однако функции прерыва- ний не допускаются. Параметры В описании процедуры или функции задается список фор- мальных параметров. Каждый параметр, описанный в списке фор- мальных параметров, является локальным по отношению к описы- ваемой процедуре или функции и в модуле, связанным с данной процедурой или функцией на него можно ссылаться по его иден- тификатору. --- ----------------------- --- Список фор- ---->( ( )-->! описание параметров !-->( ) )--> мальных пара- --- ^ ----------------------- ! --- метров ! --- ! ----------( ; )<----------- --- -------------------------- Описание -------------->! список идентификаторов !--------> параметров ! ^ -------------------------- ! ^ ! ----- ! ----------------------- ! ->( var )-- ! --- ----------------- ! ----- ->( : )->! тип параметра !-- --- ----------------- ---------------------- Тип параметра ------------! идентификатор типа !---------> ! ---------------------- ^ ! -------- ! !----------->( string )------------! ! -------- ! ! ------ ! ------------->( file )-------------- ------ Существует три типа параметров: значение, переменная и нетипизованная переменная. Они характеризуются следующим: 1. Группа параметров, перед которыми отсутствует клю- чевое слово var и за которыми следует тип, является списком параметров-значений. 2. Группа параметров, перед которыми следует ключевое слово var и за которыми следует тип, является спис- ком параметров-переменных. 3. Группа параметров, перед которыми стоит ключевое слово var и за которыми не следует тип, является списком нетипизованных параметров-переменных. Параметры-значения Формальный параметр-значение обрабатывается, как ло- кальная по отношению к процедуре или функции переменная, за исключением того, что он получает свое начальное значение из соответствующего фактического параметра при активизации про- цедуры или функции. Изменения, которые претерпевает формаль- ный параметр-значение, не влияют на значение фактического параметра. Соответствующее фактическое значение параметра-значения должно быть выражением и его значение не должно иметь файло- вый тип или какой-либо структурный тип, содержащий в себе файловый тип. Фактический параметр должен иметь тип, совместимый по присваиванию с типом формального параметра-значения. Если параметр имеет строковый тип, то формальный параметр будет иметь атрибут размера, равный 255. Параметры-переменные Параметр-переменная используется, когда значение должно передаваться из процедуры или функции вызывающей программе. Соответствующий фактический параметр в операторе вызова про- цедуры или функции должен быть ссылкой на переменную. При активизации процедуры или функции формальный параметр-пере- менная замещается фактической переменной, любые изменения в значении формального параметра-переменной отражаются на фак- тическом параметре. Внутри процедуры или функции любая ссылка на формальный параметр-переменную приводит к доступу к самому фактическому параметру. Тип фактического параметра должен совпадать с ти- пом формального параметра-переменной (вы можете обойти это ограничение с помощью нетипизованного параметра-переменной). Если формальный параметр имеет строковый тип, ему присваива- ется атрибут длины, равный 255, и фактический параметр дол- жен также иметь строковый тип и атрибут длины, равный 255. Файловый тип может передаваться только, как параметр- переменная. При ссылке на фактический параметр-переменную, связан- ную с индексированием массива или получением указателя на объект, эти действия выполняются перед активизацией процеду- ры или функции. Нетипизованные параметры-переменные Когда формальный параметр является нетипизованным пара- метром-переменной, то соответствующий фактический параметр может представлять собой любую ссылку на переменную, незави- симо от ее типа. В процедуре или функции у нетипизованного параметра-пе- ременной тип отсутствует, то есть он несовместим с перемен- ными всех типов, пока ему не будет присвоен определенный тип с помощью присваивания типа переменной. Приведем пример нетипизованных параметров-переменных: function Equal(var source,dest; size: word): boolean; type Bytes = array[0..MaxInt] of byte; var N: integer; begin N := 0; while (N |