Реферат: Object Pascal

1. Основы языка Object Pascal

1.1. Алфавит языка

Основными символами языка Object Pascal являются:

·    символы _ + —

·    26 больших и 26 малых латинских букв A,B, …Y,Z, a,b, …, y,z

·    10 арабских цифр 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

·    специальные символы * / = ^ < > ( ) [ ] { }.,:; ' # $ @

Буквы русского алфавита не входят в состав алфавитаязыка. Их использование допустимо только в строковых и символьных значениях.

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

1.2. Краткие сведения о структуре программы

Программа, написанная в среде Delphi при помощи языкаObject Pascal, всегда состоит из нескольких модулей. Как минимум таких модулейдолжно быть два. Один модуль всегда является головной программой и имеетназвание program. Прочие модули играют вспомогательную и зависимую от головнойпрограммы или от других модулей роль и называются unit. Минимальноструктурированная программа имеет один модуль program и один модуль unit.Серьезные программы помимо модуля program могут содержать до несколькихдесятков авторских модулей unit и большое количество ссылок на фирменные илиразработанные как самим автором, так и другими разработчиками модули unit.

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

Исходный программный текст каждого модуля составляется наязыке Object Pascal и помещается в отдельный файл, который всегда имеетрасширение .pas. Текст модуля program имеет расширение .dpr.

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

1.3. Лексическая структура языка

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

Зарезервированные (служебные) слова. Этот класс состоит изслов, построенных только с помощью букв алфавита. Служебные слова можноиспользовать только по прямому назначению, т. е. так, как их назначениеопределил разработчик языка. Ни в каком другом виде, например в качестве именпеременных, их использовать нельзя.

Нижепредставлен список таких слов:

And

asm

class

destructor

do

end

file

for

if

inherited

interface

library

not

or

procedure

raise

resource

shl

then

try

until

while

with

array

begin

const

dispose

downto

except

finalization

function

implementation

initialization

in

interface

is

mod

object

out

program

record

string

shr

threadvar

type

uses

as

case

constructor

div

else

exports

finally

goto

in

line

label

nil

of

packed

property

repeat

set

string

to

unit

var

xor

Кроме того, нельзя использовать следующиеслова, не принадлежащие к этому классу:private, protected, public, published, automated, directives, on,virtual.

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

Object Pascal в именах не различает больших и малых букв.Так следующие имена будут идентичны:

SaveToFile, SAVETOFILE, savetofile,sAVEtOfILE.

Среди программистов установилось хорошее правило, всоответствии с которым имена формируются таким образом, чтобы одновременновыпол-нять роль комментария, поясняющего назначение имени. Так, в приведенномпримере имя переводится с английского как «сохранить в файле». Крометого, с учетом невозможности вставки внутрь такого имени пробелов, первые буквыслов обычно пишут заглавными, а прочие строчными. Из приведенного примерахорошо видно, что именно такой способ записи наиболее нагляден для визуальноговосприятия имени. Нередко в качестве заменителя пробела используют символ"_". Однако это удлиняет и без того длинные имена. Преимуществадлинных имен совсем не означают, что нельзя применять короткие имена. Понятно,что проще набрать с клавиатуры и использовать оператор

a := a + 1,

чем идентичный ему оператор

Disk_C_DirctoryCounter :=Disk_C_DirctoryCounter +1.

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

Изображения. К их числу относятся константы, символьныестроки и некоторые другие значения.

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

Разделители используются с целью большего структурированиямодуля, с тем чтобы повысить визуальное восприятие длинных текстов. К их числуможно отнести; := (.

Комментарии. Эти лексемы используют для пояснения отдельныхфрагментов текста программы. Они представляют собой последовательностьсимволов, заключенную в фигурные скобки { } или в разделители (* и *), а такжепоследовательность символов, расположенных в строке справа от двух следующихдруг за другом символов /.

Примеры комментариев:

{ Функция вычисления количества дней между двумя датами }

(* Функция вычисления количества дней между двумя датами *)

// Неправильный ответ

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

1.4. Некоторые важные понятия

Остановимся на этих понятиях для того, чтобы короткоопределить их для понимания большинства примеров, которыми сопровождаетсяматериал. Эти компоненты языка имеют исключительную важность, в последующихразделах они будут описаны более подробно.

Ячейка. Этот несколько устаревший, но весьма удобный терминобозначает фрагмент памяти, который можно представить как некий контейнер дляхранения данных определенной структуры. Ячейка всегда имеет свое уникальноеимя, которое служит адресом, по которому расположены находящиеся в ней данные.Примером ячейки могут служить любые разрешенные имена, например a1, Imk12,Count и т. д. Термин «ячейка» не является языковым термином ObjectPascal и используется здесь только для большей наглядности при описании основязыка.

Значение – это постоянная величина или структурный комплекспостоянных величин, выраженных в явном виде. Значение не имеет имени.

Примеры значений:

-55.455051 { обыкновенное вещественное число},

'Расчет посадки с натягом' {строка символов}.

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

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

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

Тип – это структура и описание множества значений, которыемогут быть присвоены переменной.

Оператор присваивания – это команда, предназначенная дляизменения содержимого ячейки. С его помощью происходит изменение значенияпеременной (или типизованной константы).

Синтаксис оператора присваивания:

x := y; { читается «x присвоить y» }

Здесь x – переменная, y – выражение. Выражением могут быть,в частности, переменная, константа или значение. Последовательность символов":=" обозначает операцию присваивания, в соответствии с которойсначала вычисляется выражение y, затем получившийся результат в виде значениязаписывается в переменную x (см. подробнее гл. 9).

Примеры:

d := 5; { значение 5 записывается в переменную D },

h := d + 12.5; { выч. 5+12.5, рез. 17.5 записывается впеременную h }.

2. Система типов

В языке Object Pascal все переменные, т. е. ячейки памяти,предназначенные для записи, чтения и хранения значений, должны бытьпредварительно описаны. Это означает, что всякая переменная должна быть явноотнесена к какому-либо типу.

Тип – это одновременно структура и описание множествазначений, которые могут быть присвоены такой переменной.

Язык Object Pascal имеет множество разнообразных типов.Более того он позволяет самому пользователю конструировать самые разнообразныетипы, которые могут быть ему необходимы. Конструирование таких типовпроизводится из сравнительно ограниченного количества стандартных типов.

Типы имеют свою иерархию. На верхнем этаже иерархиирасположены следующие типы: простые, составные, ссылочные и процедурные.

3. Стандартные простые типы

Основными типами языка являются стандартные простые типы истандартные структурные типы.

Простые типы делятся на скалярные и ограниченные типы.Cкалярные типы делятся на стандартные и перечислимые. Стандартные скалярныетипы делятся на пять видов:

·    целые [Integer],

·    вещественные [Real],

·    логический (булевский) [Boolean],

·    символьные [Char],

·    строковые [String].

К ним примыкает особый вариантный тип [Variant].

3.1. Целые типы

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

Целыми типами являются ShortInt, SmallInt, LongInt, Int64,Byte, Word и LongWord, характеристики которых приведены в табл. 1.

 Таблица1

№ Тип Диапазон значений Размер памяти

1.

2.

3.

4.

5.

6.

7.

ShortInt

SmallInt

LongInt

Int64

Byte

Word

LongWord

–128… 127

–32768… 32767

–2147483648… 2147483647

–2^63… 2^63–1

0...255

0...65535

0… 4294967295

1 байт

2 байта

4 байта

8 байтов

1 байт

2 байта

4 байта

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

Так если значения переменной будут только положительными,то можно ее отнести к одному из типов Byte, Word, LongWord. Если известнотакже, что ее значения никогда не выйдут за 255 (например, если переменнаяпредназначена для хранения номера месяца текущего года), то лучше использоватьтип Byte. При этом память будет расходоваться наиболее экономно.

Не следует, однако, стремиться к излишней экономии памятина переменных. Нередко экономно описанная переменная может привести к ситуации,когда программа попытается записать в нее такую константу, которая превышаетдопустимый диапазон значений. Это приведет к немедленному аварийному завершениюпрограммы с сообщением «Range check error» (выход за допустимыеграницы диапазона). Сообщения такого рода могут генерироваться самыми разнымиоперациями и в самых разных местах программы. По этой причине поиск ошибки впрограмме, особенно если она многомодульна и сложна, может надолго затянуться.

Не следует также злоупотреблять многообъемными типами, т.к.это может привести к излишнему перерасходу, а иногда и нехватке памяти, с однойстороны, и замедлению работы программы – с другой.

Примеры:

Var

A, A_Par: Integer;

T1, T2, T3: LongInt;

CircleCounter: byte;

Значения целых типов изображаются в обычном десятичном илив шестнадцатеричном видах. Они отличаются тем, что при изображениишестнадцатеричных значений в его начале ставится символ $ и сами значенияформируются из шестнадцатеричных цифр 0… 9, A… F.

Максимально допустимый диапазон значений определяется ихтипом.

Примеры:

0 9877 -56 $F1 ( то же, что 241)

Над целыми значениями можно выполнять четыре обыкновенныхарифметических действия: сложение (+), вычитание (-), умножение (*), деление(/) и два дополнительных действия: деление нацело (div) и взятие остатка отделения (mod). При выполнении деления результатом будет вещественное значение, вовсех остальных операциях – целое.

3.2. Вещественные типы

Эта группа типов охватывает вещественные значения.

Вещественные типы не могут быть использованы:

·    в качестве индексов массивов;

·    в операторах For и Case;

·    в качестве базисного типа при определении множеств;

·    при определении подтипов.

При описании вместо Real48 можно указывать Real.

Ниже в табл. 2 приведен список типов и их характеристики.

Таблица2

№ Тип Диапазон значений Значащих цифр в мантиссе Размер памяти

1.

2.

3.

4.

5.

6.

Real48

Single

Double

Extended

Comp

Currency

2.9 x 10^–39… 1.7 x 10^38

1.5 x 10^–45… 3.4 x 10^38

5.0 x 10^–324… 1.7 x 10^30

3.6 x 10^–4951… 1.1 x 10^4932

-2^63+1… 2^63 -1

-922337203685477.5808… 922337203685477.5807

11 – 12

7 – 8

15 – 16

19 – 20

19 – 20

19 – 20

6 байтов

4 байта

8 байтов

10 байтов

8 байтов

8 байтов

Примеры:

Var

rA, rA_Par: Real;

T: Integer;

Вещественные значения можно изобразить:

·    в форме с фиксированной десятичной точкой;

·    в форме с плавающей десятичной точкой.

Первая форма представления вещественного значенияпредставляет привычное число, в котором целая и дробная части разделеныдесятичной точкой, например

12.455

-988.45

-8.0

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

<значение с фиксированной точкой > E <порядок>

Примеры:

-45.2E6 ( то же, что -45,2 106)

5.245E-12 ( то же, что 5,24 10-12)

Порядок таких чисел должен быть всегда целым числом.

3.3. Логический (булевский) тип

Логические переменные имеют тип boolean. Такая переменнаязанимает один байт памяти и может иметь одно из двух возможных значений – True(истина) или False (ложь).

Примеры:

Var

b: boolean;

b1, Ti: boolean;

3.4. Символьный тип

Типы AnsiChar и WideChar описывают множество отдельныхсимволов языка, включая буквы русского алфавита. AnsiChar описывает множествоиз 256 ASCII-кодов и занимает один байт памяти, WideChar описывает мно-жествоUnicode – универсальное множество кодов и занимает два байта памя-ти. ТипAnsiChar эквивалентен базовому типу Char прежних версий языка.

Примеры:

Var

Ch, k: AnsiChar;

Char_Massivr: array[1..100] of Char;

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

'h' 'X' '#' '$' ''''

3.5. Строковые типы

Этот тип во многом схож с типом Array of Char, т. е.массивом символов. Отличие состоит в том, что переменная этого типа может иметьдинамическое количество символов (от нуля до верхней границы), в то время какмассив символов всегда статичен и имеет одинаковое количество символов.

Таблица3

№ Тип Длина строки Занимаемая память

1.

2.

3.

ShortString

AnsiString

WideString

0 – 256 символов

0 – 2 Гб символов

0 – 2 Гб символов

(Кол-во символов) х 1 байт

(Кол-во символов) х 1 байт

(Кол-во символов) х 2 байта

Максимальная длина строковой переменной должна быть указанаявно. Размер строки на единицу больше ее объявленной длины, т. к. в ее нулевомбайте содержится фактическая длина строки. Длину в нулевом байте можнопринудительно менять.

Особо следует выделить тип String. Если длина String-строкине объявлена, то при действии директивы компилятора {$H+} илибез ее указания такое объявление равносильно AnsiStrig. Если установленадиректива {$H-}, то тип String равносилен типу ShortString.

Строковое значение изображают в виде последовательностисимволов, заключенной в апострофы. Пустую строку изображают двойным апострофом.

Примеры значений строковых типов:

'Иванов И.И.' '' 'Газета«ИЗВЕСТИЯ»' 'Строкасимволов'

Примеры описания переменных строковых типов:

Var

s1, s2: ShortString [12];

st1, st2: AnsiString [580];

ChMassiv: array [1..15] of String;

3.6. Строковый тип PChar

Для связи с функциями Windows в язык Object Pascal введенновый тип строк – PChar-строки с завершающим нулем. В обычной и привычной дляпрежних версий языка String-строке нулевой байт отведен для хранения реальногоколичества символов этой строки, а сами символы последовательно располагаютсяначиная с первого байта. В PChar-строке, наоборот, символы располагаютсяначиная с нулевого байта, а их последовательность заканчивается завершающимнулем.

Строки PChar можно объявлять как обычные символьныемассивы. Например, строку длины 3000 плюс один байт, зарезервированный подзавершающий нуль, можно определить следующим образом:

Var

s: array[1… 3000] of Char;

П р и м е ч а н и е. Без необходимости не используйтеPChar-строки. Строковые String-типы и функции для обработки таких строк хорошоотлажены, они легче в использовании, и, как правило, надежнее PChar-строк.

3.7. Динамические PString-строки

Этот тип строк так же, как PChar, введен в язык дляобращения к функциям Windows. Подробнее PString-строки описаны далее.

3.8. Перечислимые типы

Этот тип переменных может быть сформирован самимпользователем. Он создается простым перечислением возможных значенийпеременной.

Примеры перечислимых типов:

Type

MaleNames = (Ivan, Peter, Serge);

SwithOpts = (On, Off);

SostTypes = (Active, Passive, Waiting);

Sides = (Left, Right, Top, Down);

В первом примере переменная объявленного типа можетпринимать значение одного из трех мужских имен. Во втором – одно из двухзначений – On (включено) или Off (выключено) и т. д.

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

Например, описания вида

Type

Days1 = (Monday, Wednesday, Friday);

Days2 = (Tuesday, Wednesday, Saturday,Sunday);

содержат ошибку, т. к. константа Wednesday используетсядважды.

3.9. Ограниченные типы

Этот тип формируется самим пользователем посредствомсужения значений ранее определенного или стандартного типов.

Примеры:

Type

Diapason = 1… 30;

Letters = 'a'… 'v';

TList = (t1, t2, t3, t4, t5, t6,t7, t8,t9, t10);

TlistSmall = (t2… t8);

3.10. Вариантный тип (Variant)

Тип Variant – особый тип языка Object Pascal. Значение этого типа наперед неизвестно,однако может быть определено через присваиваемое значение одним из следующихтипов: все целые, вещественные, строковые, символьные и логические типы, заисключением Int64.

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

Var

V1, V2, V3, V4, V5: Variant; {описание Variant-переменных }

I: Integer;

D: Double;

S: string;

...

begin

V1 := 1; { integer-значение }

V2 := 1234.5678; { real-значение }

V3 := 'Иванов'; { string-значение }

V4 := '1000'; { string-значение }

V5 := V1 + V2 + V4; { real-значение 2235.5678}

I := V1; { I = 1 (integer-значение) }

D := V2; { D = 1234.5678 (real-значение) }

S := V3; { S = ' Иванов' (string-значение) }

I := V4; { I = 1000 (integer-значение) }

S := V5; { S = '2235.5678' (string-значение) }

end;

3.11. Тип «дата – время»

В языке имеется несколько типов, предназначенных для работыс датами и временем. Они имеют вид

Type

TDateTime = Double;

TDate = TDateTime;

TTimeStamp = Record

Time: Integer; { время в миллисекундахот полуночи }

Date: Integer; { единица + число дней с 01.01.0001 г.}

end;

Тип TDateTime предназначен для хранения даты и времени.

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

Описание переменной или группы переменных начинается словомVar. Область видимости переменной будет подробно описана ниже.

Общий вид описания переменных одного типа:

<переменные>: <тип>;

Пример:

Var

t_s1, t_q1: String[255];

rt1, rt2: (Opened, Closed, Unknown);

Re1, Re2, Re3: Real;

i: Integer;

В этом примере переменные t_s1 и t_q1 описаны как строковыепеременные типа String[255]. При работе программа выделит под каждую из них сучетом нулевого байта по 256 байтов памяти для хранения символьных значений.Переменные rt1, rt2 объявлены как переменные, которые могут принимать в определенныймомент времени одно из перечисленных значений: Opened, Closed, Unknown.Переменные Re1, Re2, Re3 объявлены вещественными, а переменная i –целочисленной типа Integer.

Переменными могут быть объявлены не только переменныепростых типов. Ниже будут рассмотрены переменные более сложных – структурных –типов. Более того, переменными могут быть объявлены структуры структур,примером которых являются классы. Например:

type

TKdnClass = class(TObject)

End;

Var

Ts: Record

A, N: Integer;

End;

Cl: TKdnClass;

 

5. Описание констант

В Object Pascal различается два вида констант –обыкновенные и типизованные. Описание констант следует после слова Const.

5.1. Обыкновенные константы

Описание константы строится по правилу

<имя константы> = <выражение>;

Примеры:

Const

T_Par = 12899;

M_ArrayCount = 16;

Middle_M_Array = M_ArrayCount div 2;

RealMax = 1.7e38;

StarString = '* * * * * * * * * * * * *';

Log10 = 2.302585;

Log10_Invert = 1/Log10;

LeftArrayBound = -M_ArrayCount;

Тип константы определяется автоматически по виду еезначения.

Существует несколько констант, которые заранеепредопределены и не требуют описания:

Pi = 3.1415926536E+00 (тип Real)

False, True ( Boolean)

MaxInt = 32767 ( Integer)

MaxLongInt = 2147483647 ( LongInt)

Nil ( Pointer).

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

Const

ArrMax = 100;

nSt = 46;

Var

IntArr: Array[1..ArrMax] of Integer;

StrArrr: Array[1..ArrMax] of String[nSt];

Такой способ описания позволяет при необходимости легкоизменить размер массивов, изменив всего лишь значение константы (в данномслучае константы ArrMax). При этом произойдут автоматические измененияопределений как в текущем, так и в других модулях, если они содержатопределения, опирающиеся на эту константу. Явное определение массивов череззначение 100 потребовало бы соответствующих явных изменений этого значения надругое всюду, где такие описания имели бы место. Для больших программ это моглобы стать причиной ошибок, если часть изменений не была бы выполнена понедосмотру.

Типизованные константы

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

Простые типизованные константы. Общий вид константы:

<имя константы>: <тип> = <значение>;

Примеры:

Const

CurrentPosition: Word = 11000;

LastLetter: Char = 'z';

HeadOfModule: String[26] = 'Начало программного модуля';

Типизованные константы нельзя использовать для описаниядинамических структур. Следующий пример демонстрирует недопустимое описаниединамического массива Arr через типизованную константу ArrMax:

Const

ArrMax: Integer = 100;

Var

IntArr: Array [1..ArrMax] of Integer; { Ошибка }

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

Типизованные константы типа «массив». Этот типконстант позволяет обозначить постоянной величиной целый массив однотипныхзначений, например:

Type

tVotes = (Yes, No, UnDef);

tVoteArr = array [tVotes] of String [7];

Const

Votes: tVoteArr = ( 'Да', 'Нет', 'Не знаю');

Var

V: tVoteArr;

S: String[20];

V:= Votes;

S:=V[UnDef];

Здесь в секции Type сначала описан перечислимый тип tVoteиз трех значений Yes, No, Undef. Затем через этот тип определен новый типtVoteArr как тип-массив из трех элементов, каждый из которых является строкойдлины 7. Далее в секции Const определена типизованная константа Votes, котораяопределена как массив трех строковых значений ('Да', 'Нет', 'Не знаю'). Затем всекции Var описаны переменные V и S разных типов. Предпоследняя и последняястроки являются исполняемыми операторами присваивания. Сначала переменной Vприсвоено начальное значение – константа Votes. Затем переменной S присвоенозначение третьего элемента массива V. В результате значением строковойпеременной S будет 'Не знаю'.

Типизованные константы типа «запись». Этокомбинированный тип констант, основанный на конструкциях типа Record (см.параграф 7.1), которые состоят из полей. Константы такого типа определяются поправилу «имя поля: значение поля».

Пример:

Type

tDayOfYear = Record {день года}

Week: (Mon,Tue,Wed,Thu,Fri,Sat,Sun); {деньнедели}

Num: 1… 31; {день месяца}

Month: 1 ..12; {месяц }

Year: 1951… 2050; {год}

End;

Const

D1: (Week: Sun; Num: 26; Month: 10; Year: 2001 D1,которая представляет конкретную дату «Воскресенье, 26, октябрь);

Здесь в секции Type описана запись, состоящая из четырехполей и характеризующая день года. Назначения этих полей понятно изсопроводительных комментариев. Затем в секции Const описана типизованнаяконстанта (Sun; 26; 12; 2001), т. е. » Воскресенье, 26 октября 2001года".

Типизованные константы типа «множество». Этиконстанты могут быть построены как подмножества базовых или производных от нихтипов.

Примеры:

Type

tChildNameSet = set of ['Таня', 'Валя', 'Володя','Гена'];

Const

Girls2Set: tGirlsNamesSet = ['Валя', 'Гена'];

Indexes: set of integer = [300… 500,1777,3700];

Здесь в секции Type описано множество из четырех имендетей. Ниже в секции Const описано две константы, каждая из которыхпредставляет подмножества. Первая – подмножество имен, объявленных ранее вtChildNameSet, вторая – подмножество целых чисел типа Integer.

6. Описание типов

Ранее уже приводились примеры описания переменных, вкоторых их тип указывался в Var-секции явно или на основе ранее объявленногопользовательского типа.

Описание секции типов начинается словом Type.

В табл. 4 дан пример двух идентичных способов описанияпеременных t, u, n.

Таблица4

Явный способ описания

переменных

Описание переменных с предварительным описанием их типа

Var

t,u,n:(Mon, Tue,Wed, Thu,Fri,Sat,Sun);

Type

DaysOfWeek = (Mon, Tue,Wed, Thu,Fri,Sat,Sun);

Var t,u,n: DaysOfWeek;

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

а) конкретизировать тип;

б) четко выделить множество переменных этого типа;

в) повысить уровень структурированности программы;

г) снизить вероятность путаницы в типах, когда переменныефактически того же типа объявлены разными способами;

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

В этой связи важно подчеркнуть, что даже при совпадениибазовых типов различие в пользовательских типах может привести кнепредсказуемому поведению программы. Например, в нижеследующей секции Type двапроизводных типа t1 и t2 имеют одинаковый базовый тип byte. Однако объявленныениже в Var-секции переменные p1 и p2 будут расценены системой как переменныеразных типов. Это обстоятельство может послужить причиной недоразумений в ходесоставления и/или выполнения программы.

Type

t1 = byte;

t2 = byte;

Var

p1: t1;

p2: t2;

Корректным можно считать следующий аналог:

Type

t1 = byte;

Var

p1,p2: t1;

 

7. Структурные типы

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

К числу структурных относятся следующие типы:

множественные типы [Set],

регулярные типы (массивы) [Array],

комбинированные типы (записи) [Record],

файловые типы [File],

классы [Class],

классовые ссылки [Class reference],

интерфейсы [Interface].

Ниже будут подробно описаны первых четыре структурных типа– регулярный, комбинированный, множественный и файловый.

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

7.1. Регулярные типы (массивы)

Массив – это структура языка Object Pascal, представляющаясобой упорядоченную совокупность элементов одного типа.

Следует различать два вида массивов: массив-тип имассив-переменную.

Массив-тип. Синтаксис маcсива-типа:

<имя массива> = Array [<тип индекса>, <типиндекса>, …, <тип индекса>]

Of <тип элемента>;

Всякий массив имеет размерность. Размерность определяетсяколичеством типов индексов, которые заключены в квадратные скобки [… ].

Массив-тип предназначен для описания:

структуры массива как типа;

размерности массива;

типов индексов массива;

типа каждого элемента массива.

Так, в следующем примере

Type

tA1: array [1… 10] of Real;

описана структура одномерного массива вещественныхэлементов (Real), в котором индекс может изменяться в диапазоне целых значенийот 1 до 10. Его элементами являются вещественные типы tA1[1], tA1[2], tA1[3],…, tA1[9], tA1[10].

Другой пример:

Type

Color: (Red, Green); { перечислимый тип }

Z: array [1… 3, Color ] of Boolean; { массив }

В нем сначала описан простой перечислимый тип Color. Нижена его основе описан двумерный массив Z логических (Boolean) элементов. Первыйиндекс массива имеет целый тип, а второй – тип Color. Таким образом, массивсостоит из шести элементов – логических типов:

Z [1, Red], Z [1, Green], Z[2, Red], Z[2,Green], Z[3, Red], Z[3, Green].

Массив-переменная. Синтаксис маcсива-переменной:

<имя массива >: Array[<тип индекса>,<тип индекса>, …, <тип индекса>]

Of <тип элемента>;

Массив-переменная отличается от массива-типа тем, что всеего элементы – это отдельные независимые переменные, которые могут содержатьразличные значения одного типа.

Массив-переменная может быть описан явно или с помощьюранее определенного в секции Type типа.

В следующем примере массивы y, Z описаны идентично, причемy – явно, Z – на основе ранее определенного типа в секции Type, т. е. неявно.

Type

tA1: array [1… 10] of Real;

Var

y: array [1… 10] of Real; {массив}

Z: tA1; {массив}

Этот пример демонстрирует разные способы описанияодинаковых по структуре, но разных по типу массивов. С точки зрения корректногопрограммирования он одновременно является примером того, как не следуетописывать идентичные переменные. Причина этого заключается в том, чтоидентичные структуры во избежание непредсказуемого поведения программы следуетописывать одним типом.

В этой связи корректным будет любой из вариантов,приведенных в табл. 5.

Таблица5

Корректный неявный способ Корректный явный способ

Type tA1: array [1… 10] of Real;

Var Y, Z: tA1;

Var y, Z: array [1… 10] of Real;

Многомерные массивы содержат два и более индексов,например:

Var h: array[1 ..3, boolean, -7… 7]of Word;

что эквивалентно

Var h: array[1 ..3] of array[boolean] ofarray[-7… 7] of Word;

Для упакованных массивов

Var packed array[Boolean,1..10,TShoeSize]of Char;

что эквивалентно

Var

packed array[Boolean] of packed array[1..10]

of packed array[TShoeSize] of Char;

Манипуляции с отдельными элементами массивов. Обращение котдельному элементу массива возможно через его индексы. В следующем примере всекции Var описаны простая переменная i и два одномерных массива A и V какцелые переменные типа Integer. Затем в блоке begin … end расположены тривычислительных оператора.

Var

i: Integer;

A, V: array[1..100] of Integer;

begin

i:= 5;

V[8]:= i+9;

A[45]:= V[i+3]*2;

end;

При выполнении первого из них переменная i примет значение5. При выполнении второго – восьмой элемент массива V примет значение 14. Втретьем операторе сначала будет вычислен индекс i + 3 = 8, затем значениевосьмого элемента массива V (значение 14) будет умножено на 2 и полученныйрезультат – значение 28 – будет присвоено 45-му элементу массива A.

Манипуляции с массивами. Язык допускает с помощью одногооператора присваивания выполнить операцию над массивом в целом. Пусть,например, массивы A, V объявлены как квадратные матрицы. Тогда оператор

V:= A;

выполнит копирование значений всех элементов массива A вмассив V, а после выполнения оператора

V:= A * V;

будет выполнено умножение матрицы А на матрицу V ирезультат будет помещен в матрицу V с предварительным затиранием старыхзначений.

Упакованные массивы. Элементы упакованного массива хранятсяв памяти максимально плотно. При записи он предварительно упаковывается с цельюэкономии памяти. При чтении, наоборот, распаковывается. Операции упаковки ираспаковки требуют дополнительного времени. Поэтому использование упакованныхмассивов несколько замедляет работу программы. От обычного массива Arrayописание упакованного массива отличается тем, что перед этим словом добавляетсяслово Pаcked, например:

Var W: packed array [1..100] of Integer;

7.2. Комбинированные типы (записи)

Запись – это объединение элементов разных типов. Как и вмассивах, следует различать запись-тип и запись-переменную. Один элемент записиназывается полем.

Запись-тип. Синтаксис записи-типа:

<имя записи> = Record

<имя поля 1>: <тип>;

<имя поля 2>: <тип>;

...

<имя поля N>: <тип>;

<вариантная часть >

End;

Записи очень удобны для описания и хранения разнотипныхданных о каких-либо однотипных структурах.

Примером могут служить сведения о студентах. Сведения олюбом из них могут включать поля: Фамилия, Имя, Отчество, Год рождения, Группа,Год поступления в вуз, Курс. Такие структуры являются однотипными и могут бытьописаны следующим типом:

Type

TStud = Record { Сведения о студенте как запись }

Fio: String[40]; { ФИО как строка из 40 символов }

Name: String[20]; { Имя как строка из 20 символов }

Otch: String[30]; { Отчество как строка из 30 символов }

BirthYear: Word; { Год рождения как целое типа Word }

Group: String[8]; { Группа как строка из 8 символов }

ElectYear: Word; { Год поступления как целое типа Word }

Curs: Byte; { Курс как целое типа Byte }

End;

В этом примере типы полей записи типа tStud назначены сучетом максимально возможных значений этих полей. Так, структура может хранитьфамилию из не более чем 40 символов. Полю Curs назначен тип Byte, который имеетдиапазон значений 0… 255, т. к. значением этого поля может быть одно иззначений 1… 6, которые полностью охватываются диапазоном типа Byte. Этомуполю можно было бы назначить любой другой целый тип, например Word. Однако, вцелях экономии памяти, повышения скорости чтения и записи данных, следуетназначить именно тип Byte, который занимает всего 1 байт памяти, а не тип Word,который требует 2 байта памяти. В то же время, например, для поля ElectYear(год поступления) тип Byte непригоден, т. к. имеет недостаточный диапазон значений.

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

Type

tPensioner = Record { пенсионер }

FioIO: String[100]; { Фамилия, имя, отчество одной строкой}

Age: Byte; { Возраст }

Case Citizen: boolean of{Горожанин ли ?}

TRUE: (Town: String[30];) {Город, в котором проживает}

FALSE: (Address: String[100]; {Полный адрес одной строкой}

Transport: String[200];) {Транспорт, которым можнодобраться до города}

End;

В этом примере запись tPensioner содержит понятные поляFioIO и Age, а также поле нового вида – логическое поле Citizen вариантноготипа. От значения этого поля зависит появление и непоявление некоторыхпотенциальных полей записи. Так если значение этого поля TRUE (истина), то взаписи появляется (становится доступным) поле Town (город), при значении FALSE(ложь) – поля Address и Transport.

При использовании вариантных полей в записях следуетподчиняться следующим правилам синтаксиса:

Вариантная часть должна начинаться со строки, в началекоторой располагается слово Case, а в ее конце – слово Of. Между нимирасполагается поле-признак.

Запись должна содержать только один вариант, который долженрасполагаться в конце всех описанных полей непосредствено перед словом End.

Имена полей во всех вариантах должны быть разными. Онидолжны также отличаться от имен полей фиксированной части.

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

Запись-переменная. Синтаксис записи-переменной:

<имя записи>: Record

<имя поля 1>: <тип>;

<имя поля 2>: <тип>;

...

<имя поля N>: <тип>;

<вариантная часть >

End;

т.е. синтаксисы переменной и типа отличаются одним символом(":" и "=").

Пример:

Type

tMass: Array [1… 2, 1… 50] of Real;

tRec: Record

Name: String [10];

Mass2: tMass;

End;

Var

J: Integer;

S: String[70];

F,Gri: Record

a,b,c: Integer;

k: Array [1..10] of String [60];

z: tMass;

r: tRec;

End;

В секции Var описаны две простые переменные J и S и двезаписи F и Gri, имеющих одинаковую, но достаточно сложную структуру:

первых три поля записей F и Gri имеют имена a,b,c и типInteger;

поле k представляет собой одномерный строковый массив из 10элементов;

поле z имеет тип, описанный в секции Type как двумерныйвещественный массив, в котором первый индекс может изменяться в диапазоне 1…2, а второй индекс – в диапазоне 1… 50;

поле r в свою очередь само является записью, поля которойописаны типом tRec в секции Type.

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

Примеры квалификационных полей вышеприведенных записей:

F.a Gri.a F.k[6] Gri.z [2, 34] F.r.Name F.r.Mass2[1, 50]

Примеры операторов присваивания с участием полей записей:

S := 'Иванов Иван Петрович';

J := 123;

F.a := J + 9;

Gri.a := ( F.a + J ) * ( F.c + F.b — Gri.c);

Gri.a := ( F.a + J ) * ( F.c + F.b — Gri.c);

F.k [1] := F.z [2,30];

Gri.r.Name := 'Студент ' + F.k [8];

Gri.a := 12 * (Gri.a + Gri.b + Gri.c);

Доступ к полям записей с помощью оператора With. Дляупрощения обращения к полям одной и той же записи можно использовать операторWith.

Пример:

With Gri do

Begin

a:= 12 * (a + b + c + F.a);

b:= 64 * ( b — c);

End;

Эти операторы выполняют те же операции, что и операторы

Gri.a:= 12 * (Gri.a + Gri.b + Gri.c + F.a);

Gri.b:= 64 * (Gri.b — Gri.c);

7.3. Множественные типы

Множество – это совокупность однотипных элементов. Вомногом оно похоже на типизованную константу, однако имеет от негопринципиальное отличие. Это отличие состоит в том, что значениями множестваявляются все его допустимые подмножества.

Как и в массивах, следует различать множество-тип имножество-переменную.

Множество-тип. Синтаксис множества-типа:

<имя множества> = Set of <базовый тип >;

Пример:

Type

TSomeInts = 1..250;

TIntSet = set of TSomeInts;

создает тип множества с именем TIintSet, которое содержитмножество целых чисел в диапазоне от 1 до 250. Это же множество могло бытьописано явно:

type TIntSet = set of 1..250;

Множество-переменная. Синтаксис множества-переменной:

<имя множества>: Set of <базовый тип >;

В соответствии с вышеописанными типами можно объявитьмножества:

Var Set1, Set2: TIntSet;

а затем в операторной части задать эти множества:

...

Set1 := [1, 3, 5, 7, 9];

Set2 := [2, 4, 6, 8, 10];

Можно объявить множество явно, перечислив его элементы:

Var

MySet1: set of 'a'… 'z';

MySet2: set of Byte

MySet3: set of (Club, Diamond, Heart,Spade)

MySet4: set of Char;

...

MySet 1:= ['a','b','c']; {оператор определения множества}

Операции над множествами. Допустимые операции надмножествами приведены в следующей табл. 6:

Таблица6

Опера-ция Наименование операции Тип операндов Тип результата Пример

+

*

<=

>=

=

<>

in

Объединение

Вычитание

Пересечение

Не меньше

Не больше

Равенство

Неравенство

Принадлежание

set

set

set

set

set

set

set

элемент set

set

set

set

boolean

boolean

boolean

boolean

boolean

Set1 + Set2

S — T

S * T

Q <= MySet

S1 >= S2

S2 = MySet

MySet <> S1

A in Set1

Объединение, вычитание и пересечение множеств.

Результатом любой из операций будет также множество.

Пример:

Var

S1, S2,S3: set of Byte;

...

S1:= [1, 2, 3, 4]; {оператор определения множества}

S2:= [3, 4, 5, 6, 78]; {оператор определения множества}

S3:= S1 + S2; {объединение множеств}

{результат S3 = [1, 2, 3, 4, 5, 6, 78] }

S3:= S2 — S1; {вычитание множеств}

{результат S3 = [1, 2, 5, 6, 78] }

S3:= S2 * S1; {пересечение множеств}

{результат S3 = [3, 4] }

Операции сравнения множеств.

Результатом любой из операций будет логическая константаTrue (истина) или False (ложь).

Пример:

Var

S1, S2, S3: set of Byte;

B: boolean;

...

S1:= [3, 4]; {оператор определения множества}

S2:= [1, 3, 4]; {оператор определения множества}

S3:= [3, 4, 5, 6, 78]; {оператор определения множества}

B:= S1 <= S3; {True, т. к. S1 является подмножеством S3}

B:= S3 >= S2; {False, т. к. S2 не является подмножествомS2}

B:= S3 = S2; {False, т. к. мн-ва S2 и S3 не равны другдругу }

B:= S3 <> S2; {True, т. к. мн-ва S2 и S3 не равныдруг другу }

Проверка вхождения элемента во множество. Результатомоперации in будет логическая константа True (истина) или False (ложь). Пример:

Var

S1: set of Integer;

B: boolean;

...

S1:= [3, 4, 18… 178, 3101, 4427];{оператор определения множества}

B:= ( 4 in S1); {True, т. к. 4 является элементом множестваS1}

B:= (200 in S1); {False, т. к. 200 не является элементомS1}

7.4. Файловые типы

В языке Object Pascal есть три типа файлов:

текстовые файлы,

файлы с типом,

файлы без типа.

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

Текстовой файл – это последовательность символьных строкперемен-ной длины. Всякая такая строка завершается маркером конца строки CR/LF.Текстовые файлы можно обрабатывать только последовательно. Ввод и вывод нельзяпроизводить для открытого файла, используя одну файловую переменную. Текстовойфайл имеет тип TextFile, или просто Text. Пример описания файловой переменнойтекстового типа:

Var Fi: TextFile;

Файлы без типа состоят из компонент одинакового размера,структура которых не известна или не имеет значения. Допустим прямой доступ клюбой компоненте файла. Пример объявления файловой переменной файла без типа:

Var F: File;

Файлы c типом состоят из однотипных компонент известнойструктуры. Допустим прямой доступ к любой компоненте файла. Пример объявленияфайловых переменных для файлов с типом:

Type

TRec = Record

A: Real;

B: Integer;

C: Char;

End;

Var

F: File of Real;

Q: File of String[100];

Fr: File of TRec;

В этом примере F объявлена как файловая переменнаявещественного типа. Это означает, что компонентами файла могут быть тольковещественные значения. Файловая переменная Q предназначена для доступа кфайлам, которые состоят из символьных строк длины 100. Файловая переменная Frпредназначена для работы с файлами, которые состоят из записей типа TRec,объявленного в секции Type.

8. Совместимость типов

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

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

8.1. Совместимость по вычислению

Вычисление выражений возможно только при соблюденииследующих условий.

Типы операций и операндов эквивалентны.

Например, нельзя применять арифметические операции клогическим переменным и, наоборот, логические операции – к арифметическимпеременным:

Type

R1, R2: Real;

L1, L2: Integer;

B1, B2: boolean;

...

Not (R1 + R2) B1 + B2 'Иванов' + ' ' +'Петр' {недопустимые выражения}

Типы операндов эквивалентны.

Типы операндов целые или вещественные, например:

R1 + R2 L1 + R2 L2 / R1 / L1 {допустимые выражения}

Один тип является базовым, а второй – ограниченным типомэтого же базового типа.

Type

L11, L12: Integer;

K: -199… 199;

Типы являются множествами, причем их базовые типысовместимы.

Type

L: set of 21… 2141;

K: set of -199… 199;

Один тип является строковым, а другой – также строковымлибо символьным.

Type

L: String [34]; Q: String [23]; K: Char;

Один тип является ссылочным, а другой – также ссылочнымлибо безтиповым указателем.

Оба типа являются упакованными символьными массивами содинаковым числом элементов.

Один тип является строковым, а другой – также строковымтипом, либо упакованным символьным массивом, либо символьным типом.

Один имеет тип Variant, а другой – тип integer, real, string, character или Boolean.

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

8.2. Совместимость по присваиванию

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

Оба типа T1 и Т2 идентичны. Недопустимо присваиваниефайловых типов или структур, содержащих файловые типы (о файловых типахподробнее см. гл. 17).

T1 и Т2 имеют совместимые простые типы.

T1 и Т2 имеют вещественные типы.

T1 и Т2 имеют целые типы.

T1 и Т2 имеет тип PChar или другой строковый тип, ивыражение представляет строковую константу.

T1 и Т2 имеют строковые типы.

T1 имеет строковый тип, а Т2 – символ или упакованнаястрока.

T1 – длинная строка, а Т2 имеет тип PChar.

T1 и Т2 имеют совместимые типы упакованных строк.

.T1 и Т2 имеют совместимые множественные типы.

.T1 и Т2 имеют совместимые Pointer-типы.

.T1 имеет тип PChar или PWideChar, а T2 есть символьный массиввида array[0… n] of Char.

.T1 и T2 имеют совместимые процедурные типы.

.T1 имеет типVariant, а T2 – один из типовinteger, real, string, character или Boolean.

.T1 имеет типinteger, real, string, character или Boolean, а Т2 – тип Variant.

9. Выражения

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

9.1. Арифметические выражения

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

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

В языке Object Pascal существует шесть арифметическихопераций. Учитывая, что арифметические операции образуют подмножество множествавсех операций языка, в табл. 7 показано абсолютное значение приоритета каждойоперации.

Таблица7

Операция Наименование Приоритет

+

-

*

/

div

mod

Сложение

Вычитание

Умножение

Деление

Деление нацело

Остаток от деления

2

2

1

1

1

1

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

Таблица8

Операция Тип операндов Тип результата Пример

+

*

/

div

mod

Integer, real

Integer, real

Integer, real

Integer, real

Integer

Integer

integer, real

integer, real

integer, real

real

integer

integer

X + Y

Result — 1

P * InterestRate

X / 2

Total div UnitSize

Y mod 6

Примеры:

4*5 = 20, 6/5 = 1.2, 8+7 = 15, 7-3 = 4, 16 div 5 = 3, 16mod 5 = 2.

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

Пример:

Выражение: 15 * ((25/5-5*9 + (j-8) * 7.55) / 8.67) Порядок выполнения операций: 8 2 5 3 6 1 4 7

9.2. Логические выражения

Результатом вычисления логического выражения может бытьодно из двух логических значений: True (истина ) или False (ложь).

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

В языке существует четыре логических операций. Приоритетопераций показан в табл. 9.

Таблица9

Операция Наименование Приоритет

Not

And

Or

Xor

Отрицание

Конъюнкция

Дизъюнкция

Спец. дизъюнкция

3

4

5

6

Значения элементарных логических выражений, поясняющихназначение этих операций, приведены в табл. 10.

Таблица 10

A B not A A and B A or B A xor B

True

True

False

False

True

False

True

False

False

False

True

True

True

False

False

False

True

True

True

False

False

True

True

False

В табл. 11 представлены логические отношения.

Таблица11

Отношение Наименование

>

<

>=

<=

=

<>

Больше

Меньше

Больше или равно

Меньше или равно

Равно

Не равно

Все отношения равноприоритетны.

Порядок выполнения операций при вычислении логическоговыражения следующий:

сначала вычисляются арифметические выражения;

затем – отношения;

в последнюю очередь вычисляются логические операции.

Примеры (для x=12, z = 0, y=1):

Таблица12

Выражение Результат

5 > 8

(5 < 3) and (z = 0)

((4 + 8) < 0) or not (y = 0)

not ((x < y) and (z > y))

('ab' = 'ac') and (x=z)

(4 in [ 2… 23 ])

True

False

True

True

False

True

Пример, демонстрирующий порядок выполнения операций привычислении логического выражения:

Выражение:

Not ((x > 6 + 8 * 2) and (y < 7) or (z > 7)) and (x <> y)

Порядок: 9 3 2 1 6 4 7 5 10 8

9.3.Строковые выражения

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

Пример:

Выражение: 'Object '+'Pascal '+' для Delphi' Результат: 'Object Pascal для Delphi'

10. Операторы

Оператор – языковая конструкция, представляющая описаниекоманды или комплекса команд по обработке и преобразованию данных.

Все операторы делятся на две части – простые операторы иструктурные операторы.

11. Простые операторы

К их числу относятся: оператор присваивания, операторбезусловного перехода, составной оператор, оператор процедур, специальныеоператоры.

11.1. Оператор присваивания

В параграфе 1.4 было дано краткое определение этого оператора– одного самых простых и наиболее часто используемых операторов. Напомним, чтоего синтаксис имеет вид

x := y;

где x – имя переменной или функции; y – совместимое потипу выражение (о совместимости типов см. гл. 8). Символы ":="обозначают операцию присваивания, в соответствии с которой вычисленное значениевыражения y присваивается переменной x.

Примеры операторов присваивания (комментарии показываютприсвоенные значения):

Var

Ch: Char;

S: String[5];

Q: String[18];

L, J: Integer;

P: Byte;

R: Real;

B: Boolean;

Rec: Record

 A: Word;

 B: String[20];

 End;

Таблица13

Оператор присваивания Значение

Q:= 'd:\Dir1\Word\a.txt';

S:= Q;

Q:= S+'r\';

Ch:=Q[2];

L:= 450;

P:= L;

J:= 100;

R:= -L / J;

J:=-L / J;

J:=-L — 200;

B:= J > L;

B:= (J < L) and (Q[5] = 'i');

Rec.A:= J-20;

Rec.B:= 20;

Rec.B:= S[1]+S[3]+'\d';

'd:\Dir1\Word\a.txt'

'd:\Di'

'd:\Dir\'

':'

450

Ошибка, т. к. max P = 255

100

-4.5

Ошибка. Несоответствие типов

250

False

True

230

Ошибка. Несоответствие типов

'd\\d'

 

11.2. Оператор безусловного перехода

Этот оператор выполняет передачу управления оператору,которому предшествует метка. Синтаксис оператора:

Goto Метка;

Язык допускает в качестве меток использовать имя илизначение целого типа из диапазона 1… 9999.

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

Все метки, используемые в блоке, должны быть описаны словомLabel.

Пример оператора описания меток:

Label 1, 2, Met1, Met2, Met3;

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

Недопустим переход к метке внутрь структурного оператора(например, внутрь цикла, минуя его заголовок). Компилятор не реагирует на этуситуацию, однако поведение программы может быть непредсказуемо. Недопустимытакже вход или выход по метке в процедурах или функциях.

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

11.3. Оператор обращения к процедуре

Этот оператор вызывает активизацию операторов,расположенных в теле процедуры (см. параграф 15.1). После выполнения процедурыуправление передается к оператору, расположенному вслед за операторомпроцедуры.

При вызове процедуры её формальным параметрам должны строгосоответствовать по совместимости типов и количеству фактические параметры.

Примеры обращения к процедурам:

Val (s, r, Code);

Sort (a, n * 2);

SaveParameters;

11.4. Обращение к функции

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

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

после выполнения оператора процедуры управление передаетсяк следующему за ним оператору;

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

Поясним это на примере оператора присваивания, содержащегообращение к функции Func8:

G:= 2 * Pi * Func8(m, n, a) / Sqr (z);

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

12. Стандартные процедуры и функции

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

12.1. Строковые процедуры и функции

Function Length (St): LongInt;

Возвращает длину строки символов St, т. е. количествосимволов в ней (не путать с размером строки).

St:= '1234abc';

L:= Length(St); {L= 7}

Procedure Delete (St, Pos, Num);

Удаляет Num символов из строки St начиная с позиции Pos,если Pos<Length (St).

St:= '1234abc';

Delete(St, 4, 2); { St= '123bc'}

Delete(St, 3, 120); { St= '12'}

Procedure Insert (Obj, Target, Pos);

Вставляет строку Obj в строку Target начиная с позиции Pos.Если Pos>Length (Target), то результат Target + Obj.

St1:= '***';

St2:= '1234abc';

Insert (St1, St2, 3) { St2= '12***34abc'}

Procedure Str (Value, St);

Преобразует значение Value числового типа в строку символовSt. Value может сопровождаться форматом.

L:=19;

Str (L, g); {g= '19'}

R:= 2.123155;

Str (R: 8:3, h); {h= ' 2.123' (длина 8, в дробной части 3}

Procedure Val (St, Vr, Code);

Преобразует строку символов St в числовую величину Vrцелого или вещественного типа. Code = 0, если преобразование прошло успешно,иначе в Code будет записан номер первого ошибочного символа конвертируемойстроки, при этом значение Vr не определено.

St:='319';

Val (St, k, Cod); {k= 319, Cod = 0}

St:='81yy9';

Val (St, k, Cod); {k= ?, Cod = 3}

Function Copy (St, Pos, Num): String;

Выделяет из строки St подстроку символов длиной Num начинаяс позиции Pos. Если Pos>Length, то возвращает пустую строку.

St1:='АБВГДЕ';

St2:= Copy(St1, 2, 3); {St2= 'БВГ'}

St2:= Copy(St1, 2, 27); {St2= 'БВГДЕ'}

St2:= Copy(St1, 44, 2); {возвращает пустую строку St2= ''}

Function Concat (St1, St2{, …, StN}): String;

Объединяет строки в одну строку.

St:='abc';

St1:=Concat( 'sss', St, '1234'); {St1='sssabc1234'}

St1:=Concat( St, '123'); {St1= 'abc123'}

Function Pos (Obj, Target): Integer;

Возвращает номер символа, начиная с которого строка Objпервый раз входит в строку Target. Если строка Obj отсутствует в строке Target,то Pos = 0.

Q:= 'Иванов Сергей Петрович';

H:= Pos ('Сергей', Q); {H= 7}

H:= Pos ('Игорь', Q); {H= 0}

Function SizeOf (Obj): Integer;

Возвращает размер переменной Obj.

Function FormatFloat(const Format: string; Value: Extended): string;

Возвращает форматированное вещественное значение в видестроки. Format – формат числа, Value – число. В табл. 14 даны форматы функцииFormatFloat.

Таблица14

Формат Описание

#

.

,

E+, E-

 

;

Поле для цифры. Недостающие позиции заменяются нулями

Поле для цифры. Если в позиции имеется значащая цифра, то оно выводится. Нули не выводятся

Разделитель целой и дробной частей

Поле разделителя тысяч, миллионов

Формат представления чисел с плавающей точкой. Если "+" указан, то перед порядком выводится знак. Если указан "-", то минус выводится только для отрицательных порядков

Разделитель форматов

 

Примерыдействия форматов при конвертации числа в строку представлены в табл. 15.

Таблица15

Формат Число 1 Число 2 Число 3 Число 4

0

0.00

#.##

#,##0.00

#,##0.00;(#,##0.00)

#,##0.00;;Zero

0.000E+00

#.###E-0

1234

1234

1234.00

1234

1,234.00

1,234.00

1,234.00

.234E+03

1.234E3

-1234

-1234

-1234.00

-1234

-1,234.00

(1,234.00)

-1,234.00

-1.234E+03

-1.234E3

0.5

1

0.50

.5

0.50

0.50

0.50

5.000E-01

5E-1

0.00

0.00

0.00

Zero

0.000E+00

0E0

 

12.2. Стандартные функции

Function Char (X: byte): Char;

Возвращает символ с номером X.

Ch:= Char(74); {Ch= 'J'}

Function Ord (X): LongInt;

Возвращает порядковый номер скалярного аргумента.

j:= Ord('J'); {j= 74}

Function Round (X: Real): LongInt;

Возвращает округленное до целого значение вещественногоаргумента.

j:= Round(12.8235); {j= 13}

Function Trunc (X: Real): LongInt;

Возвращает целое путем отбрасывания дробной частивещественного аргумента.

j:= Round(12.8235); {j= 12}

12.3. Арифметические процедуры и функции

Function Frac (X: Extended): Extended;

Возвращает дробную часть аргумента, например:

r:= Frac(-12.82); {r = -0.82,Frac(12.82)=0.82 }

Function Abs (X: Extended): Extended;

Возвращает абсолютное значение аргумента, например:

r:= Abs(-12.82); {r = 12.82}

Function ArcTan (X: Extended): Extended;

Возвращает арктангенс аргумента.

Function Cos (X: Extended): Extended;

Возвращает косинус аргумента.

Function Sin (X: Real): Real;

Возвращает синус аргумента.

Function ArcCos(X: Extended): Extended;

Возвращает арккосинус аргумента, значение которого должнопринадле-жать отрезку [-1, 1]. Возвращает значение из отрезка [0, Pi].

Function ArcSin(X: Extended): Extended;

Возвращает арксинус аргумента, значение которого должнопринадле-жать отрезку [-1, 1]. Возвращает значение из отрезка [-Pi/2, Pi/2].

Function ArcTan2(Y, X: Extended): Extended;

Возвращает арктангенс аргументов, вычисляя ArcTan(Y/X) всоответ-ствии с квадрантами координатной плоскости xOy. Возвращает значение изотрезка [-Pi, Pi].

Function Exp (X: Real): Real;

Возвращает экспоненту аргумента.

Function Sinh(X: Extended): Extended;

Возвращает гиперболический синус аргумента.

Function Cosh(X: Extended): Extended;

Возвращает гиперболический косинус аргумента.

Function Tanh(X: Extended): Extended;

Возвращает гиперболический тангенс аргумента.

Function ArcSinh(X: Extended): Extended;

Возвращает гиперболический арксинус аргумента.

Function ArcCosh(X: Extended): Extended;

Возвращает гиперболический арккосинус аргумента.

Function ArcTanh(X: Extended): Extended;

Возвращает гиперболический арктангенс аргумента.

Function Ln (X: Real): Real;

Возвращает натуральный логарифм аргумента.

Function Sqr (X: Real): Real;

Возвращает квадрат аргумента.

Function Sqrt (X: Real): Real;

Возвращает квадратный корень аргумента.

FunctionCeil(X: Extended):Integer;

Возвращает наибольшее целое аргумента.

Сeil(-2.8) = -2

Ceil(2.8) = 3

Ceil(-1.0) = -1

Function Floor(X: Extended): Integer;

Возвращает наименьшее целое аргумента.

Ceil(-2.8) = -3

Ceil(2.8) = 2

Ceil(-1.0) = -1

Function Dec (X, [n]: LongInt): LongInt;

Уменьшает значение аргумента на величину второго параметра.Если он отсутствует, то уменьшает на 1.

J:=67;

K:=Dec(J); {j= 66}

K:=Dec(J, 13); {j= 53}

Function Inc (X, [n]: LongInt): LongInt;

Увеличивает значение аргумента на величину второгопараметра. Если он отсутствует, то увеличивает на 1.

J:=67;

K:=Inc(J); {j= 68}

K:=Inc(J, 13); {j= 81}

12.4. Скалярные функции

Function Odd (X: LongInt): Boolean;

Возвращает True, если аргумент четный.

J:=67;

K:=Odd(J); {K= False}

Function Pred (X);

Возвращает предшествующее значение типа аргумента.

Function Succ (X);

Возвращает последующее значение типа аргумента.

12.5. Процедуры завершения

Procedure Exit; Выход из процедуры.

Procedure Halt([Code:Word]);

Выход в операционную систему с кодом возврата, если онуказан.

 

12.6. Процедуры и функции для работы с типами«дата/время»

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

Function Now: TDateTime;

Возвращает текущую дату и время.

Function Date: TDateTime;

Возвращает текущую дату.

Function Time: TDateTime;

Возвращает текущее время.

Function DateToStr (D: TDateTime): String;

Преобразует дату в строку символов, например:

S:= DateTimeToStr(Date); {текущая дата '26.10.99'}

Function TimeToStr(T: TDateTime): String;

Преобразует время в строку символов, например:

S:= TimeToStr(Time); { текущее время '13.58.13'}

Function DateTimeToStr(DateTime: TDateTime): String;

Преобразует дату/время в строку символов, например:

S:= DateTimeToStr(Now); { текущие датаи время '26.10.99 14.01.51'}

Function DateTimeToTimeStamp(DateTime: TDateTime): TTimeStamp;

Конвертирует TDateTime в TTimeStamp, например:

TS:= DateTimeToTimeStamp(Now); {type TS =TTimeStamp}

s:= IntToStr(Ts.Date) + ' ' + IntToStr(Ts.Time); {'73005351095810' – (прошло дней с 00.00.0000 г. и миллисекунд от полуночи текущегодня}

FunctionTimeStampToDateTime(const TimeStamp: TTimeStamp): TDateTime;

Конвертирует TTimeStamp в TDateTime.

Procedure DecodeDate(Date: TDateTime; var Year, Month, Day: Word);

Раскладывет дату Date на год, месяц и день, например:

DecodeDate(Now, Y, M, D);

s:= IntToStr(Y) + ' ' + IntToStr(M) + ' '+ IntToStr(M); {'1999 10 26'}

.ProcedureDecodeTime(Time: TDateTime; var Hour, Min, Sec, MSec: Word);

Раскладывет время Time на час, минуты, секунды имиллисекунды, например:

DecodeTime(Now, H, M, S, MS);

ss:= IntToStr(H) + ' ' + IntToStr(M) + ' '+ IntToStr(S) + ' ' + IntToStr(MS);

{'14 22 34 567', т.е. 14 ч 22 мин 34 с 567 мс}

.Function EncodeDate(Year, Month, Day:Word): TDateTime;

Противоположна DecodeDate;

.FunctionEncodeTime(Hour, Min, Sec, MSec: Word): TDateTime;

Противоположна DecodeTime;

.FunctionFormatDateTime(const Frmt: string; DateTime: TDateTime): string;

Преобразует DateTime в строку с заданным форматом. Еслиформат пуст, то функция возвращает строку в формате «c». Следующийоператор присвоит строковой переменной s значение 'Встреча состоится: пятница,6 Ноябрь, 1999, в 10:30 AM'.

s:= FormatDateTime('«Встреча состоится:» dddd,mmmm d, yyyy, ' +'«в» hh:mm AM/PM', StrToDateTime('6.11.9910:30am'));

Виды форматов даны в табл. 16.

12.7. Прочие процедуры и функции

Function Hi(X): byte;

Возвращает старший байт своего целочисленного аргумента.

Function Lo(X): byte;

Возвращает младший байт своего целочисленного аргумента.

Procedure Swap(X);

Меняет старший и младший байты целочисленного аргументаместами.

Procedure Randomize;

Инициализирует генератор случайных чисел.

Function Random(N: Integer): Integer;

Возвращает случайное число из интервала (0, N).

Function SizeOf(X): Integer;

Возвращает число байт, занимаемых аргументом.

Procedure Move(Var Source, Dest; Count: Integer);

Копирует Count байт из переменной Source в переменную Dest.В случае перекрытия областей памяти пересылка в перекрывающуюся область непроизводится.

Function ParamCount: Word;

Возвращает число параметров, переданных в командной строке.

Procedure Break;

Оператор безусловного завершения цикла, процедуры илифункции.

.Procedure Continue;

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

Таблица16

Формат Описание

C

 

D

Dd

Ddd

Dddd

Ddddd

Dddddd

M

Mm

Mmm

Mmmm

Yy

Yyyy

h

hh

n

nn

s

ss

t

tt

am/pm

ampm

a/p

/

:

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

Показывает день без лидирующего нуля (1 – 31)

Показывает день с лидирующим нулём (01 – 31)

Показывает день недели в укороченном формате (Вос – Суб)

Показывает день недели в полном формате (Воскресенье – Суббота)

Показывает дату в формате дд.мм.гг.

Показывает дату в формате д Месяц год

Показывает месяц без лидирующего нуля (1 – 12)

Показывает месяц с лидирующим нулём (01 – 12)

Показывает месяц в сокращенном виде (Янв – Дек)

Показывает месяц в полном формате (Январь – Декабрь)

Показывает год в виде двух последних цифр (00 – 99)

Показывает год в виде четырех цифр (00000 – 9999)

Показывает час без лидирующего нуля (0 – 23)

Показывает час с лидирующим нулем (00 – 23)

Показывает минуту без лидирующего нуля (0 – 59)

Показывает минуту с лидирующим нулем (00 – 59)

Показывает секунду без лидирующего нуля (0 – 59)

Показывает секунду с лидирующим нулем (00 – 59)

Показывает время в формате чч: мм

Показывает время в формате чч: мм: сс

Показывает время в 12-часовом формате (am – до полудня, pm – после полудня)

Показывает время в 12-часовом формате без указателя до/после полудня

Использует Windows-разделитель даты.

Использует Windows-разделитель времени

 

. procedure Abort;

Используется в контексте с другим оператором; отменяет«задним числом» оператор в случае его аварийного завершения,блокирует выдачу сообщения об ошибке, удобен к использованию в блоке try …finally.

13. Структурные операторы

К их числу относятся:

·    составной оператор,

·    условный оператор If,

·    оператор варианта Case,

·    оператор цикла For – Do,

·    оператор цикла While – Do,

·    оператор цикла Repeat – Until,

·    оператор записи With,

·    оператор Try – Except – End,

·    оператор Try – Finally – End,

·    оператор On – Do,

·    оператор Try – Finally – End.

13.1. Составной оператор

Это простая структура следующих друг за другом операторов,заключенных в операторные скобки begin … end.

Синтаксис составного оператора:

Begin

Оператор1

Оператор2

ОператорN

End;

Составной оператор применяется в тех случаях, когдакакое-либо действие необходимо применить не к одному, а сразу к несколькимоператорам.

Пример:

Begin

R:= X;

X:= Y;

Y:= R;

End;

13.2. Условный оператор If

Синтаксис допускает два вида оператора:

if логическое выражение then оператор1 else оператор2;

и его усеченный вариант:

if логическое выражение then оператор1;

Оператор работает следующим образом. Сначала вычисляетсялогичес-кое выражение. Если оно истинно, то выполняется оператор1, иначе –оператор2. Усеченный оператор выполняет оператор1 только в случае истинногозначения логического выражения и не производит никаких действий в случае еголожности.

Примеры:

if (x < 10.7) then a[4]:= 5 else a[4]:=6;

if (x < 10.7) then a[4]:= 5;

Допустима вложенность условных операторов внутри составногоусловного оператора. Например, оператору

if L1 then if L2 then St1 else St2 elseSt3;

эквивалентен оператор

if L1 then

begin

if L2 then St1 else St2;

end

else St3;

В этом операторе для повышения структурированностииспользованы операторные скобки begin … end. При конструировании сложногоусловного оператора во избежание логических ошибок следует отдавать предпочтениеструктурному способу записи такого оператора.

13.3. Оператор варианта Case

Синтаксис оператора:

Case Selector of

Const1: Оператор1;

Const2: Оператор2;

ConstN: ОператорN

[else Оператор];

End;

Selector может быть любой простой тип кроме Real. Каждая изConst1 … ConstN может быть значение, несколько перечисленных через запятуюзначений или отрезок типа. Оператор Else, как видно из описания, можетотсутствовать. В том случае, если он присутствует, то действует общее правило:перед словом Else не должно быть символа ";" (точка с запятой).Поясним работу оператора Case на примере:

Case i of

0: x := 0;

1,3: x := 1;

10… 15: x := 2

else x := 3;

End;

При выполнении оператора Case управление будет переданотому оператору, который помечен константой, являющейся значением переменной i.Например, если к моменту выполнения Case-оператора i = 0, то будет выполненоператор x := 0. Иначе, если i = 1 или i = 3, то будет выполнен оператор x :=1; иначе, если значение i в диапазоне 10… 15, то будет выполнен оператор x :=2; наконец, если i не равно ни одной из вышеперечисленных констант, то будетвыполнен оператор x := 3, следующий за словом else (иначе).

13.4. Оператор цикла For – Do

Синтаксис оператора имеет две разновидности:

For счетчик цикла:=нач.знач. To конеч.знач. Do оператор

For счетчик цикла:=нач.знач. Downto конеч.знач. Do оператор

Здесь конструкция For… Do называется заголовком цикла,оператор – телом цикла.

Для циклов должны соблюдаться следующие правила иограничения:

начальное и конечное значения счетчика цикла должны бытьодинаковых простых типов, кроме Real;

в теле цикла счетчик не должен менять значение;

вход в цикл минуя заголовок запрещен;

для первой разновидности начальное значение не должно бытьбольше конечного;

для второй разновидности начальное значение не должно бытьменьше конечного.

Первая разновидность оператора цикла For работает следующимобразом. Сначала счетчик цикла принимает нач.знач. и выполняется оператор,расположенный вслед за словом Do. Затем значение счетчика будет увеличено нашаг счетчика 1 и вновь будет выполнен оператор и т. д., пока счетчик непереберет все значения от нач.знач. до конеч.знач.

Пример.

s:= 0;

For i:=1 to 44 do s:= s + z[i];

В результате в переменной s будет накоплена сумма первых 44элементов массива z.

Другая разновидность оператора For отличается лишьотрицательным шагом –1 счетчика.

Пример.

s:= 0;

For i:=44 Downto 1 do s:= s + z[i];

Будет получен тот же результат.

13.5. Оператор цикла While – Do

Синтаксис оператора:

While логическое выражение Do оператор;

Цикл выполняет оператор, расположенный вслед за словом Doдо тех пор, пока истинно логическое выражение, расположенное за словом While(«выполняй, пока истинно»).

Пример.

x:= 0;

i:=0;

While (x < 101.667) do

Begin

Inc (i);

X:= X + 5.617;

Y[i]:= Func (i + 6, 9 * i, X);

End;

В этом примере цикл будет выполняться до тех пор, пока невыполнится условие x < 101.667. В теле цикла переменная X с каждым шагомцикла увеличивает свое значение на 5.617 так, что на определенном шаге условиеx < 101.667 впервые не будет выполнено. В этот момент без входа в тело циклзакончит работу.

13.6. Оператор цикла Repeat – Until

Синтаксис оператора:

Repeat

Оператор1;

Оператор2;

ОператорN;

Until логическое выражение;

Цикл работает, пока логическое выражение ложно(«повторяй, пока не выполнится»).

Пример.

s:= 0;

i:=0;

Repeat

Inc (i);

s:= s + z[i];

Until (i = 44);

В этом примере цикл будет выполняться до тех пор, пока невыполнится условие i = 44. Результат будет тот же, что в примере для For-цикла.

13.7. Операторы Break и Continue

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

Пример.

s:= 0;

i:=0;

Repeat

Inc (i);

s:= s + z[i];

if (s > 14) then Break;

Until (i = 44);

В этом примере цикл будет выполняться до тех пор, пока невыполнится условие i = 44 или если в операторе if переменная s превыситзначение 14.

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

Пример.

s:= 0;

i:=0;

Repeat

Inc (i);

s:= s + z[i];

if (s > 20) then Continue;

if (s > 14) then Break;

Until (i = 44);

В этом примере если в первом операторе if выполнитсяусловие s > 20, то сработает оператор Continue. Он сразу передаст управлениена первый оператор в теле цикла – Inc (i), предотвратив тем самым выполнениениже-следующих операторов – второго if и Until.

13.8. Вложенные циклы

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

все вложенные циклы For – Do должны иметь различныесчетчики (иначе это противоречило бы требованию на запрет изменения значениясчетчика внутри цикла);

нет никаких ограничений на досрочный выход из внутреннегоцикла наружу;

недопустим вход во внутренний цикл For – Do, минуя егозаголовок, что соответствует общему требованию о корректном входе в цикл.

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

Пример.

Const

n = 15;

m = 24;

Var

i,j: Byte;

R,Tau,s: Real;

z: array[1..n, 1..m] of Real;

{заполнение массива z с использованием вложенных циклов}

Tau:= Pi/m;

For i:=1 to n do begin

R:=4.0*Pi*Sin(i*Tau); {первый оператор в теле цикла по i}

For j:=1 to m do z[i, j] := R+j; {второй оператор в телецикла по i}

end {i};

{вычисление суммы положительных элементов массива z сиспользованием вложенных циклов }

s:=0;

For i:=1 to n do

For j:=1 to m do

 if ( z[i, j] > 0) then s:= s + z [i,j];

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

/>

Наружный цикл со счетчиком i в теле цикла содержит дваоператора – оператор присваивания (вычисление значения вспомогательнойпеременной R с целью сокращения времени вычислений) и оператор внутреннегоцикла со счетчиком j. Поскольку наружный цикл в своем теле содержит несколькооператоров, то они заключены в операторные скобки begin … end.

Эта структура работает следующим образом. После входа внаружный цикл переменная i (счетчик этого цикла) примет значение 1. Далее будетвычислено значение переменной R при i = 1. После этого будет выполненвнутренний цикл со счетчиком j, где j на каждом шаге будет последовательнопринимать значения 1, 2, 3, … m (i при этом остается неизменным и равным 1). Врезультате будут вычислены элементы z11, z12,…,<sub/>z1m первой строки массива. Затем будет выполнен возврат кзаголовку наружного цикла, где значение счетчика i будет увеличено на 1 (т. е.i станет равно 2) и вновь будет выполнены операторы, расположенные в его теле.В результате будут определены элементы z21,<sub/>z22,…,<sub/>z2m второй строки массива и т.д.

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

13.9. Оператор записи With

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

Пример:

Var

Student: Record

Fam: String[30];

Name: String[20];

Age: Word;

End;

Student.Fam:= 'Колокольников';

Student.Name:= 'Павел';

S:=Student.Fam + ' '+Student.Name;

{предыдущих три оператора эквивалентны следующим}

With Student do

Begin

Fam:= 'Колокольников';

Name:= 'Павел';

S:= Fam + ' '+ Name;

End;

13.10. ОператорTry – Except – End

Это новейшее средство языка. Блок Try – Except – Endиспользуется для предотвращения исключительных ситуаций (ИС), которые могутвозникнуть при выполнении программы. К их числу относятся сбои в работеаппаратуры, ошибки вычислений (например деление на нуль), попытки присвоитьзначение, выходящее за пределы типа и т. д.

Синтаксис:

Try

{операторы, способные генерировать ИС}

Except

{операторы, обрабатывающие генерированные ИС}

end;

Блок Try – Except – End работает следующим образом.Выполнение начинается с операторов, расположенных в блоке Try – Except. Если вкаком-либо операторе возникает ИС, то она подавляется и затем выполняются всеоператоры, расположенные в блоке Except – End. В результате предотвращаетсяаварийное прерывание программы. Использование блока Try – Except – Endоткрывает возможность программного контроля за ИС.

Пример.

i:= 0;

n:= 8;

Try

i:= n div i; {Деление на нуль. Оператор генерирует ИС}

n:= i + 9;

Except

ShowMessage('Ошибка. Деление на нуль в операторе i := n /i');

End;

Результатом выполнения блока операторов будет появление наэкране формы с сообщением «Ошибка. Деление на нуль в операторе i := n /i», после чего программа продолжит работу с оператора, следующего за словомEnd, а не с оператора n := i + 9.

Если бы оператор i := n div i не был защищен блоком Try –Except – End, то возникшая при его выполнении ИС привела бы к нежелательномуаварийному завершению программы.

13.11. Оператор On – End

При возникновении ИС язык позволяет не только предотвратитьпрерывание программы, но и определить, какого именно вида была ИС. Для этого вблоке Except – End можно использовать оператор On –Do.

Пример

i:= 0; n:= 8;

Try

i:= n div i; {Деление на нуль. Оператор генерирует ИС}

n:= i + 9;

Except

On Ex: EdivByZero do ShowMessage('Деление на нуль');

End;

В этом примере сообщение о возникновении ИС будет выданотолько в случае, когда ИС будет только деление на нуль (EdivByZero). Во всехостальных случаях ИС будет предотвращена, однако никакого сообщения о еевозникновении выдано не будет. Объявленная в блоке Except – End переменная Exможет быть любым именем (здесь Ex используется только для примера).

13.12. ОператорTry – Finally – End

Блок Try – Finally – End также используется для предотвращенияИС, которые могут возникнуть при выполнении программы. В отличие от блока Try –Except – End блок Try – Finally – End используется для освобождения ресурсовпамяти, закрытия файлов и пр. в случае возникновения ИС.

Синтаксис:

Try

{операторы, способные генерировать ИС}

Finally

{операторы освобождения ресурсов памяти }

end;

Блок Try – Finally – End работает следующим образом.Выполнение начинается с операторов блока Try – Finally, которые в правильнонаписанной программе должны содержать операторы выделения ресурсов памяти. Еслив каком-либо операторе возникает ИС, то управление сразу передается коператорам блока Finally – End, где производится освобождение памяти, закрытиефайлов и пр. В результате, с одной стороны, предотвращается аварийное прерываниепрограммы и, во вторых, корректно освобождается ранее зарезервированная память,выполняется ряд других необходимых операций.

Отметим, что блок Finally – End выполняется всегда внезависимости от того, была или не была сгенерирована ИС.

Пример.

i:= 0;

n:= 8;

Try

GetMem (p, 8000); {выделение памяти}

i:= n div i; {Деление на нуль. Оператор генерирует ИС}

n:= i + 9;

Finally

FreeMem (p, 8000); {освобождение памяти}

End;

14. Указатели

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

Тип Pointer образует указатель без типа. Указатель на типимеет синтаксис:

^ Имя типа

Примеры объявления указателей:

Type

tDinArr = Array[1… 1000, 100] ofString[255]; {обычный тип}

tDinArrPtr = ^tDinArr; {указатель натип tDinArr}

tRecPtr = ^tRec; {указатель на тип записи, который описан ниже}

tTRec = Record {обычныйтип-запись}

A: Integer;

B: Real;

C: String[255];

End;

Var

DinArr: tDinArr; {обычная запись}

DinArrPtr: tDinArrPtr; {указатель натип}

RecPtr: tRecPtr; {указатель на тип-запись}

Pn1, Pn2: Pointer; {указатели без типа}

Модули System и SysUtils содержат большое количество типовдля работы с указателями. Эти типы могут быть использованы для повышенияэффективности пользовательских программ, в которых используются указатели. К ихчислу относятся: PAnsiString, PString, PByteArray, PCurrency, PExtended и ряддругих указателей. Впрочем, эти типы могут быть легко заменены стандартнымитипами. Например PString эквивалентен ^String и т.д.

14.1. Операции с указателями

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

содержимое указателя такого же типа;

константу Nil (пустой указатель);

адрес объекта с помощью функции Addr;

адрес с помощью оператора @;

адрес, построенный функцией Ptr.

Пример:

TrPt:= Nil;

Klo1Ptr:= Klo2Ptr;

P1:=@Pp; {эквивалентно P1:= Addr(Pp);}

P2:= Ptr($B701);

14.2. Стандартные процедуры и функции для работы суказателями

Procedure GetMem(Var: P: Pointer; Size: Word);

Выделяет блок памяти размера Size и присваивает адресначала блока указателю P.

Procedure FreeMem(Var: P: Pointer; Size: Word);

Освобождает блок памяти размера Size, адресованныйуказателем P.

Ниже приведен подробный пример, демонстрирующий экономныйпроцесс копирования текстового файла 't1.txt' в файл 't2.txt' с использованиемуказателя Buffer.

Var

F1, F2: file; {объявление файловых переменных}

Buffer: PChar; {объявление указателя на строку }

begin

AssignFile(F1, 't1.txt'); {связывание F1 с файлом 't1.txt'}

Reset(F1, 1); {файл открыт для ввода/вывода}

AssignFile(F2, 't2.txt'); {связывание F2 с файлом'text.txt'}

Rewrite(F2, 1); {файл открыт для вывода}

try

Size := FileSize(F1); {вычисления размера файла}

GetMem(Buffer, Size); {выделение памяти под чтение файла}

try

BlockRead(F1, Buffer^, Size); {считывание всего файла't1.txt'}

BlockWrite(F2, Buffer^, Size); {записьв файл 't2.txt'}

finally

FreeMem(Buffer); {освобождение памяти}

end;

finally

CloseFile(F1); {закрытие файла F1}

CloseFile(F2); {закрытие файла F2}

end;

end;

В этом примере объявлен указатель на строку Buffer сзавершающим нулем, которая будет использована для копирования файла 't1.txt' вфайл 't2.txt'. Для этого оператором GetMem для переменной Buffer^ будетдинамически выделен блок памяти размером, равным размеру файла. Далееоператором BlockRead файл 't1.txt', связанный файловой переменной F1, будетсчитан в Buffer^ и затем оператором BlockWrite переменная Buffer^ будетзаписана в файл 't2.txt', связанный с файловой переменной F2. Дляпредотвращения исключительных ситуаций пример содержит два вложенных блока try– finally – end. Внутренний блок обслуживает возможный сбой в ситуации, когдапо какой-либо причине файл не удалось прочитать или записать операторамиBlockRead или BlockWrite. Такой способ гарантирует освобождение памятиоператором FreeMem как в случае успешного копирования, так и в случаевозможного сбоя. Внешний блок обслуживает ситуацию, когда у системы возможнонет того объема памяти, который запрашивает оператор GetMem. В любых вариантах– при успешном или безуспешном копировании файла – следующие за последнимfinally операторы CloseFile закроют открытые операторами Reset и Rewrite файлыF1 и F2 и позволяет программе продолжить работу.

Procedure New(Var: P: Pointer);

Создает новую динамическую переменную того типа, на которыйссылается указатель. Эквивалентна оператору GetMem(P, SizeOf(P^));

Procedure Dispose(Var: P: Pointer);

Уничтожает динамическую переменную, на которую указывает P.Эквивалентна оператору FreeMem(P, SizeOf(P^));

Procedure ReallocMem(var P: Pointer; Size: Integer);

Процедура работает следующим образом:

если P= Nil и Size = 0, то оператор не выполнит никакихдействий;

если P= Nil и Size > 0, то оператор сработает аналогичноGetMem;

если P <> Nil и Size = 0, то оператор сработаетаналогично FreeMem.

Function Addr(X): Pointer;

Адрес указанного имени.

14.3. Прочие процедуры и функции для работы с указателями

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

Function GetHeapStatus: THeapStatus;

Расположена в модуле System. Дает сведение о состояниираспределен-ной и доступной программе памяти. Тип функции имеет вид

THeapStatus = record

TotalAddrSpace: Cardinal;

TotalUncommitted: Cardinal;

TotalCommitted: Cardinal;

TotalAllocated: Cardinal;

TotalFree: Cardinal;

FreeSmall: Cardinal;

FreeBig: Cardinal;

Unused: Cardinal;

Overhead: Cardinal;

HeapErrorCode: Cardinal;

end;

Function AllocMem(Size: Cardinal):Pointer;

Выделяет блок памяти и устанавливает каждый байт «внуль». Освобо-ждение памяти можно выполнить с помощью процедуры FreeMem.

Procedure GetMemoryManager(var MemMgr:TMemoryManager);

Дает текущее состояние менеджера памяти – специальнойзаписи с типом:

TMemoryManager = record

GetMem: function(Size: Integer): Pointer;

FreeMem: function(P: Pointer): Integer;

ReallocMem: function(P: Pointer; Size:Integer): Pointer;

end;

Procedure SetMemoryManager(var MemMgr: TMemoryManager);

Устанавливает менеджер памяти – выполняет операциивыделения и освобождения памяти в соответствии с предварительно установленнымив менеджере памяти значениями.

14.4. Глобальные переменные AllocMemCount и AllocMemSize

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

AllocMemCount – количество блоков выделенной памяти.

AllocMemSize – размер блоков выделенной памяти.

15. Подпрограммы

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

В языке Object Pascal есть два вида подпрограмм – процедурыи функции. Структура всякой подпрограммы во многом напоминает структуруисходного модуля. Каждая такая подпрограмма перед ее использованием должна бытьописана. Описанием подпрограммы называется ее исходный код, а обращением кподпрограмме является оператор или его часть, которые содержат код вызова такойподпрограммы. Таким образом, описание – это технология, а обращение – этодействия по предписанной технологии.

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

15.1. Процедуры

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

Procedure procedureName(parameterList);directives;

localDeclarations;

begin

statements;

end;

Здесь

Name – имя процедуры,

parameterList – список формальных параметров,

directives – директивы,

localDeclarations – внутренние описания,

statements – операторы тела процедуры.

procedureName – имя процедуры. Именем процедуры может быть любое имя, не совпадающее ни скаким другим описанным в том же, что и процедура, блоке, именем.

parameterList – список формальных параметров может бытьлибо пуст (в этом случае скобки можно не использовать), либо должен содержатьпоследовательность входных и/или выходных величин. Отдельная величина вописании заголовка может быть:

объявленной с помощью слова var переменной с типом или безтипа;

константой;

выходной величиной (т. н. out-параметром).

Пример описания процедуры.

procedure ByRef(var X: Integer; L, K:Integer);

begin

X := X * 2 * L; {правильно}

K := 2 + L; {ошибка}

end;

Процедура c именем ByRef содержит три параметра –переменную X и две константы L и K. Тело процедуры состоит из операторов,заключенных в операторных скобках begin – end. Переменные L, K являются тольковходными и не могут быть изменены в теле процедуры. По этой причине операторK:= 2 + L не даст результата и значение К останется таким же, каким оно было дообращения к процедуре. Напротив, переменная X, объявленная с помощью слова var,будет вычислена и на выходе будет иметь то значение, которое будет вычисленовнутри процедуры ByRef.

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

procedure GetInfo(out Info:SomeRecordType);

var MyRecord: SomeRecordType;

...

Proc1(MyRecord);

переменная MyRecord не может передавать данные в процедуруProc1. Напротив, только Proc1 может сформировать данные и передать их вMyRecord.

Тип параметра может быть любым. Однако нельзя объявлятьновый тип прямо в заголовке подпрограммы. Такой тип должен быть объявлен раньшеи только после этого может быть использован в заголовке подпрограммы.

localDeclarations – внутренние описания могут содержатьописание локальных имен типов или переменных. В предыдущем примере переменнаяMyRecord типа SomeRecordType, объявленная внутри процедуры GetInfo, являетсяобразцом такого описания.

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

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

Program {начало программы}

Const n = 10; {размер матриц}

Type tMatr = array[1… n, 1… n] of Real;{тип матриц}

Var Q1, Q2, Q3: tMatr; {описание матриц}

Procedure MatrMult(M1, M2: tMatr; Var M3:tMatr); {заголовок}

Var i, j, k: Integer;

Begin

For i:= 1 to n do

For j:= 1 to n do

Begin {блок определения одного элемента матрицы M3}

M3[i, j]:=0;

For k:=1 to n do

M3[i, j]:= M3[i, j] + M1[i, k] * M2[k, j];

End;

End; {конец описания процедуры MatrMult}

Procedure Prim; {заголовок процедуры Prim}

Var i, j: Integer;

Begin

For i:= 1 to n do {блок задания элементовматриц Q1, Q2}

For j:= 1 to n do

Begin

Q1[i, j]:=i + j;

Q2[i, j]:=i — j;

End;

MatrMult(Q1, Q2, Q3); {оператор обращения к процедуреMatrMult}

End; {конец описания процедуры Prim}

… {текст головной программы}

Prim; {обращение к процедуре Prim}

Q3[2, 3]:= 1;

Приведенный в данном примере текст программы можноразделить на четыре части:

описание глобальных констант и переменных;

текст процедуры MatrMult;

текст процедуры Prim;

текст головной программы.

В верхней части описана глобальная константа n = 10,которая используется во всех следующих за этим структурах. Так, в секции Typeобъявлен тип вещественного квадратного массива tMatr. Далее в секции Varобъявлены три переменные – матрицы Q1, Q2, Q3 типа tMatr.

Далее дан текст процедуры MatrMult, которая представляеталгоритм перемножения матриц M1 и M2 с записью результата в переменную M3.Матрицы M1, M2 в процедуре не меняются, поэтому их необязательно объявлятьпеременными (с позиций процедуры MatrMult переменные M1, M2 выступают вкачестве констант). Напротив, матрица M3 получается как результат, определяемыйвнутри процедуры MatrMult, поэтому она объявлена в заголовке словом Var.Необъявление M3 как переменной привело бы к ошибке: измененные внутри процедурызначения этой матрицы не были бы зафиксированы. Переменные i, j, k, объявленныевнутри процедуры, являются локальными и действуют только в пределах этойпроцедуры.

Описание процедур заканчивается процедурой Prim. В телеэтой процедуры объявлено две локальные переменные i, j, выполняющихвспомогательную роль счетчиков циклов. Далее расположено два вложенных друг вдруга цикла, с помощью которых определяются элементы матриц Q1 и Q2. Поотношению к процедуре Prim эти матрицы являются глобальными переменными и,следовательно, они доступны не только во внешнем блоке, но и внутри процедурыPrim. И наконец, в нижней части расположен оператор обращения к процедуреMatrMult, который предназначен для вызова алгоритма перемножения матриц.

В нижней части программы дан фрагмент текста головнойпрограммы, содержащей вызов процедуры Prim.

Опишем механизм вычислений, который запускается приведеннойпрограммой. Сначала будет выполнена процедура Prim (обращение к ней содержитсяв самой нижней части текста примера). Эта процедура без параметров, поэтомууправление будет сразу передано в тело процедуры, где начнется последовательноевыполнение содержащихся в нем операторов. Сначала будут выполнены два вложенныхцикла For, где элементы матриц Q1, Q2 будут заполнены значениями (например Q1[1,1] = 2, Q2[1, 1] = 0 и т. д.). Далее уже внутри Prim произойдет обращение кпроцедуре MatrMult. При этом сначала произойдет подстановка фактическихпараметров Q1, Q2, Q3 на место соответствующих формальных параметров M1, M2,M3. Далее управление будет передано внутрь процедуры MatrMult, где аналогичнопоследовательным выполнением ее операторов произойдет перемножение матриц.После выполнения процедуры MatrMult управление будет передано в ту же точку, скоторой производился ее вызов. Поскольку вызов MatrMult производился из Prim,то управление будет вновь возвращено в процедуру Prim к оператору,расположенному вслед за оператором вызова MatrMult. Поскольку в Prim больше нетневыполненных операторов, то она также заканчивает свою работу и управлениепередается в ту алгоритмическую единицу, которая и вызывала Prim, а именно вголовную программу, к оператору Q3[2, 3] := 1.

15.2. Функции

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

Синтаксис функции имеет вид

function functionName(parameterList):returnType; directives;

localDeclarations;

begin

statements;

end;

Здесь functionName – имя функции; ParameterList, directives, localDeclarations, statements имеюттот же смысл, что и в процедуре;ReturnType – тип возвращаемого результата.

Пример.

Function Fact(n: Word): LongInt; {заголовокфункции Fact}

Var i: Word; j: LongInt;

Begin

j:=1;

if (n > 1) then

For i:= 2 to n do j:= j * i;

Result:= j;

End; {конец описания функции Fact }

 

… {текст головной программы}

 

Var r: Real;

r:= 3.4 * Fact(3) / 2.5 / (122 — Fact(5)); {обращение кфункции Fact}

В этом примере описана функция с именем Fact вычисленияфакториала n! неотрицательного целого числа. Тип функции определен как LongInt.В теле функции размещен оператор Result:= j, который определяет возвращаемыйфункцией результат. Способ обращения к функции демонстрирует последний операторпримера. Видно, что способ обращения к функции имеет существенное отличие отспособа обращения к процедуре. Так, в этом операторе обращение к функции Factпроизводится дважды – один раз с фактическим параметром 3, другой – спараметром 5. Далее возвращенные результаты (соответственно, 6 и 120) будутподставлены в выражение правой части оператора, после чего последний будетвычислен и переменная r получит вещественное (Real) значение 4.08.

15.3. Параметры без типа

Это особый вид параметров, который может быть использовантолько в заголовках имен процедур и функций. Каждый параметр без типа долженбыть описан как var, const или out-параметр. Например:

procedure TakeAnything(const C);

описывает С как константу без типа.

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

Однако имеется одно исключение: при обращении кподпрограммам, содержащим параметры без типа, нельзя использовать числовыезначения или нетипизированные числовые константы.

Следующий пример использует параметры без типа в функцииCompare, которая сравнивает размеры двух переменных V1 и V2 и возвращает ответв виде константы –1, если размер V1 меньше размера V2, нуль – если размерыодинаковы, 1 – если размер V1 меньше размера V2.

function Compare(var V1, V2): ShortInt;

Var i, j: LongInt;

Begin

I:=SizeOf(V1);

J:=SizeOf(V2);

If (I < J) then Result:= -1

Else

If (I > J) then Result:= 1

Else Result:= 0;

End;

Примеры обращений к функции Compare:

type

TVector = array[1..10] of Integer;

TPoint = record

X, Y: Integer;

end;

var

Vec1, Vec2: TVector;

N,i: Integer;

P: TPoint;

i:= Compare(Vec1, Vec2); {0, размерыодинаковы}

Vec[1]:= Compare(i, Vec1); {-1, размер i меньше размера Vec1}

P.X:= Compare(Vec1, P); {1, размер Vec1 больше размера P}

P.Y:= Compare(i, P); {-1, размер i меньше размера P}

Vec2[8]:= Compare(i, P.X); {0, размеры i и поля P.X одинаковы}

15.4. Декларации процедур и функций

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

1. Декларации о вызове по соглашению (calling convention).К их числу относятся декларации register, pascal, cdecl, stdcall и safecall,например:

function MyFunction(X, Y: Real): Real;cdecl;

Этот вид деклараций предназначен для задания способапередачи параметров в процедуру. Декларации register, pascal передают параметрыслева направо, cdecl, stdcall и safecall – наоборот, справа налево.

Директивы near, far и export предназначены дляразграничения способов обращения в 16-разрядных приложениях. Для современных32-разрядных приложений они не имеют значения.

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

2. Директива Forward указывает на то, что заголовокпроцедуры или функции объявлен раньше, чем описана сама подпрограмма, например:

function Calculate(X, Y: Integer): Real; forward;

function Calculate;

...

begin

...

end;

3. Директива External указывает на то, что текст процедурысодержится в отдельном объектном (откомпилированном) модуле. Такой способпозволяет присоединить объектный код к компилируемой программе из указанногомодуля. Для этого необходимо указать директиву компилятора со ссылкой намодуль, в котором содержится объектный код декларируемой процедуры, например:

{$L BLOCK.OBJ}

procedure MoveWord(var Source, Dest;Count: Integer); external;

procedure FillWord(var Dest; Data:Integer; Count: Integer); external;

Этот пример показывает, что при компиляции коды процедурMoveWord и FillWord следует искать в объектном коде BLOCK.OBJ.

 

4. Директива OverLoad позволяет использовать одно имя длянескольких подпрограмм, например:

function Divide(X, Y: Real): Real;overload;

begin

Result := X/Y;

end;

function Divide(X, Y: Integer): Integer;overload;

begin

Result := X div Y;

end;

В этом примере описаны две функции с одним именем Divide.При обращении к функции с таким именем вызвана будет та функция, фактическиепараметры которой соответствуют формальным параметрам. Так, при обращении ввиде Divide (6.8, 3.2) будет вызвана первая функция, т. к. ее формальныепараметры также вещественны, а при обращении Divide(6, 8) будет вызвана втораяфункция.

Директива Overload разрешена для подпрограмм, в которыхмогут различаться только типы параметров, поэтому недопустимы описания вида

function Cap(S: string): string; overload;

procedure Cap(var Str: string); overload;

 

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

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

function Calc(X,Y: Integer): Integer;

Эта функция может быть определена как тип для переменной F:

var F: function(X,Y: Integer): Integer;

и связана с этой переменной оператором присваивания:

F := Calc;

Точно так же можно определить любой другой новыйпроцедурный тип и переменную:

Type {объявление процедурных типов}

TIntegerFunction = function: Integer;

TProcedure = procedure;

TStrProc = procedure(const S: string);

TMathFunc = function(X: Double): Double;

Var {объявление процедурных переменных}

F: TIntegerFunction; {F функция без параметров,возвращающая целое}

Proc: TProcedure; {Proc – процедура без параметров}

SP: TStrProc;

M: TMathFunc;

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

F: function(X: Integer): Integer;

I: Integer;

 

function SomeFunction(X: Integer):Integer;

...

В операторной части первый оператор связывает F сконкретной функцией, не производя над ней никаких действий:

F := SomeFunction;

напротив, оператор

I := F(4);

вызывает эту функцию (запускает ее алгоритм) и послеобработки возвращает результат вычислений переменной I.

 

15.6. Формальные и фактические параметры

В Object Pascal есть понятия формального и фактическогопараметров. Формальным называется параметр, который содержится в заголовкеописания подпрограммы, а фактическим – параметр в обращении к подпрограмме.Так, в вышеприведенном примере параметр X является формальным, а значение 4 –фактическим.

При вызове подпрограмм необходимо иметь в виду следующее:

фактические значения или константы должны быть совместимыпо типу с объявленными формальными параметрами;

фактические var или out-параметры должны быть идентичны потипу объявленным формальным параметрам, исключением являются тольконетипизированные параметры;

формальным параметрам без типа не могут соответствоватьтакие фактические параметры, как числовые значения и нетипизированные число-выеконстанты.

Приведем еще один пример, демонстрирующий способы обращенияк подпрограммам.

Const

IntCount = 1200;

Type

TFunc12 = Function(c1, c2: Integer):Integer;

Function Func12_1(k1, k2: Integer):Integer;

Begin

Result:= k1 + k2;

End;

Function Func12_2(g1, g2: Integer):Integer;

Begin

Result:= g1 — g2;

End;

Procedure AnyPro(u1: Real; Var u2: Real;Var u3; Const u4: Integer; F: tFunc12);

Begin

u2:= u1 + u4 + F(u4, Round(u4 * 3.14));

u3:= u1 — u4 — F(u4, Round(u4 * 3.14));

End;

Var

k: Integer;

v1, v2: Real;

ss: String;

{примеры обращения к процедуре AnyPro:}

AnyPro(v1, v2, v1, v2, Func12_1);

AnyPro(v1, v2, ss, v1, Func12_2);

AnyPro(k, v1, ss, v2, Func12_1);

AnyPro(k + 8, v2, ss, IntCount, Func12_1);

AnyPro(8, v2, ss, v1+6.7, Func12_2);

Параметры u1, u2, u3, u4, F в заголовкепроцедуры AnyPro, являются формальными параметрами: u1 – константа типа Real; u2 – переменнаятипа Real; u3 – переменная без типа;u4 – константа типа Integer; F – параметр-функция типа TFunc12, который объявленвыше в секции Type.

Параметры, заключенные в скобки в примерах обращения кпроцедуре AnyPro, являются фактическими параметрами. Такие параметры могут бытьзначениями (8), константами (IntCount), переменными (v1), выражениями (k + 8),именами процедур или функций (Func12_1) и др.

 

15.7. Область действия имен

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

Глобальные параметры могут быть описаны в том же модуле,который содержит использующую их подпрограмму, или в другом модуле, на которыйимеется ссылка в списке uses. Если два параметра имеют одинаковое имя и один изних описан внутри подпрограммы, а другой – вне ее, то действует тот параметр,который описан в подпрограмме. Аналогично определяется область доступностипараметров описанных в разных модулях. Таким образом, при описании имен действуетследующий принцип: более позднее объявление отменяет облаcть действия ранееописанных имен. Внутри одной подпрограммы нельзя объявлять двух и болееодинаковых имен.

Поясним область действия имен на примере следующего модуля

Unit Mod4;

interface

uses Mod1, Mod2, Mod3;

….

Type

Vr = Integer; {допустимо}

Var

Vr: Real; {недопустимо}

implementation

Var Vr: Char; {недопустимо}

procedure Pro1; {не содержит внутреннего объявления имениVr}

procedure Pro2; { содержит внутреннее объявление имени Vr}

Var

Vr: String; {допустимо}

Vr: Real; {недопустимо}

В приведенном тексте модуля Mod4 содержится несколькоописаний имени Vr, часть которых допустима, другая часть ошибочна. Недопустимоописание этого имени:

в var-секции в разделе interface, так как оно ужеиспользовано в этом же разделе выше – в секции type;

в var-переменной в разделе implementation, так как оно ужеиспользовано в этом же модуле в разделе interface;

как переменной типа Real в теле процедуры Pro2, т. к. оноуже использовано в этой же процедуре при описании String-переменной.

Более позднее объявление отменяет действие ранее описанногоимени. Так, внутри процедуры Pro2 имя Vr представляет переменную типа String, авнутри процедуры Pro1 имя Vr действует как глобальный тип Integer, объявленныйвыше – в секции type.

Если бы это имя вообще не было описано в модуле Mod4, нобыло бы объявлено в одном или нескольких модулях, указанных в ссылочном спискеuses, то оно могло бы быть использовано как глобальный параметр внутри этогомодуля (Mod4). При этом действовало бы то имя, которое объявлено в разделеinterface самого последнего содержащего его модуля списка uses. Например, еслиимеется описание имени Vr в модулях Mod1 и Mod2, то действовало бы описание изMod2. Если в списке uses поменять Mod1 и Mod2 местами, то будет действоватьописание, которое выполнено для этого имени в модуле Mod1.

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

Function Deccy(x: Integer): Integer;

Begin

d:= d — x;

Deccy:= Sqr(x);

End;

d:= 3; a:= Deccy(3) * Deccy(d); {a= 0, d=0}

d:= 3; a:= Deccy(d) * Deccy(3); {a= 81, d=-3}

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

 

15.8. Рекурсивные процедуры и функции

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

В качестве иллюстрации приведем пример простой ичрезвычайно эффективной процедуры сортировки (расстановки элементов в порядкенеубывания) фрагмента целочисленного одномерного массива A:

procedure QuickSortPart(var A: array ofInteger; iLo, iHi: Integer);

var

Lo, Hi, Mid, T: Integer;

begin

Lo := iLo;

Hi := iHi;

Mid := A[(Lo + Hi) div 2]; {среднийэлемент фрагмента}

repeat {деление фрагмента на левую и правую части}

while A[Lo] < Mid do Inc(Lo);

while A[Hi] > Mid do Dec(Hi);

if Lo <= Hi then

begin

T := A[Lo];

A[Lo] := A[Hi];

A[Hi] := T;

Inc(Lo);

Dec(Hi);

end;

until Lo > Hi;

if Hi > iLo then QuickSortPart(A, iLo,Hi); {сортировка левой части}

if Lo < iHi then QuickSortPart (A, Lo,iHi); {сортировка правой части}

end;

Процедура QuickSortPart сортирует фрагмент одномерногомассива A, начинающийся индексом iLo и заканчивающийся индексом iHi. Процедураоснована на методе половинного деления. В соответствии с этим методом сначалавыбирается элемент, расположенный в середине сортируемого фрагмента, затемэлементы меньшие его отправляются в левую часть фрагмента, прочие – в правуючасть. Далее сортируются левая и правая части разделенного массива какотдельные фрагменты по той же схеме, т. е. к каждой из них применяется та жепроцедура QuickSortPart. Именно обращение процедуры к самой себе и делает еерекурсивной.

Ниже приведена обычная (нерекурсивная) процедура QuickSortсортировки всех элементов массива, которая выполняется обращением к рекурсивнойпроцедуре QuickSortPart, где фрагмент – весь массив A.

procedure QuickSort (var A: array ofInteger);

begin

QuickSortPart(A, Low(A), High(A));

end;

 

15.9. Параметры и конструкторы открытых массивов

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

array of type (предпочтительнее array[X… Y] of type)

Например, операторы

procedure NullChar(A: array of Char);

begin

for i:= Low(A) to High (A) do A[i]:= '0';

end;

объявляют процедуру NullChar, которая содержит одинпараметр – открытый символьный массив А любого размера. В теле процедурыиспользуется оператор цикла, который заполняет каждый элемент массива символом'0'. Для определения нижней границы индекса использована стандартная функцияLow, для верхней – High.

Если к такой процедуре обратиться оператором NullChar(z),где тип переменной z = array[5… 55] of Char, то весь массив z будет заполненсимволами «нуль».

Конструкторы открытых массивов допускают конструированиезначений таких массивов прямо внутри оператора обращения к подпрограмме.

Пример:

var I, J: Integer;

procedure Add (A: array of Integer);

В этом случае можно обратиться к процедуре Add, например,так:

Add ([5, 7, I, I + J]);

16. Структура программы

В среде Delphi программа как единое целое представляется ввиде проекта. В новой версии языка Object Pascal для представления проектаиспользуется пять основных типов файлов:

dpr-файл головной программы;

текстовые pas-файлы;

откомпилированные dcu-файлы;

res-файлы ресурсов;

dfm-файлы ресурсов экранных форм;

готовые к использованию программные exe-файлы.

Исходная программа, написанная в среде Delphi на языкеObject Pascal всегда состоит из нескольких модулей, каждый из которыхразмещается в отдельном текстовом файле. Один модуль является головнойпрограммой. Он начинается словом Program и размещается в файле с расширением.dpr. Все остальные модули являются подчиненными и начинаются словом Unit.Такие модули размещаются в файлах с расширением .pas. Все модули заканчиваютсяоператором End, после которого ставится символ «точка».

Всякий модуль может использовать другие модули, к числукоторых могут относиться текстовые файлы, res- и dfm-файлы ресурсов илиоткомпилированные файлы Unit-модулей. Сcылка на необходимые к использованиюмодули содержится в секциях Uses. Текстовые или скомпилированные файлы обычносодержат необходимые для использующего их модуля величины – константы, типы,переменные, процедуры и функции. Файлы ресурсов необходимы для подключенияконстант, описывающих используемые внешние ресурсы.

Вышеперечисленные модули, размещенные в *.pas-, *.dcu-,*.res-, *.dfm-файлах, играют вспомогательную роль: они предназначены для компиляциии последующей сборки в полноценный программный модуль – exe-файл, готовый кисполнению на компьютере.

Ниже приведен пример исходных текстов головной программыKdnBread и одного подчиненного (используемого) ею модуля Main.

Program KdnBread; {начало текста головной программы}

{текст содержится в файле'c:\Borland\Projects\KdnBread.pas'}

uses {ссылки на модули типа unit }

Forms, {ссылка на модуль Forms }

main in 'main.pas' {Form1}; {ссылка на модуль main }

{$R *.RES}

begin

Application.Initialize;

Application.CreateForm(TForm1, Form1);

Application.Run;

end. {конец текста головной программы}

unit Main; {начало текста модуля Main}

{ текст модуля содержится в файле'c:\Borland\Projects\Main.pas' }

interface {начало интерфейснойчасти модуля}

uses

Windows, Messages, SysUtils, {ссылки на другие модули }

Graphics, Controls, Forms, StdCtrls;

Type {описание типов}

TForm1 = class(TForm)

Button1: TButton;

L1: TLabel;

procedure Button1Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

Var{описание переменных}

Form1: TForm1;

b: boolean;

i: Integer;

IterationPar: Word;

function OneSymbString(c: Char; d: byte): String; {заголовок функции}

implementation {начало процедурного блока модуля}

{$R *.DFM}

procedure TForm1.Button1Click(Sender:TObject); {заголовок процедуры}

begin

if (i > 12) and b then

L1.Caption:='Студент:'+AnsiUpperCase ('Иванов ВладимирИванович');

end; {конец процедуры}

function OneSymbString(c: Char; d: byte): String; {заголовок функции}

begin

Result:=CharStr(c, d);

end; {конец функции}

initialization

IterationPar:= 0;

end. {конец текста модуля Main}

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

16.1. Структура модуля

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

Unit <имя>;

interface

<интерфейсная часть>

implementation

<выполняемая часть>

initialization

<блок инициирования>

finalization

<блок завершения>

end.

16.2. Раздел Interface

Раздел Interface модуля Unit предназначен для описаниявнешних компонент: используемых модулей, типов, констант, переменных,заголовков процедур и функций. Так, в вышеприведенном примере в разделеInterface содержатся:

в списке Uses – ссылки на модули Windows, Messages, SysUtils, Graphics, Controls, Forms, StdCtrls;

в секции Type – описание типа экранной формы – классTForm1;

в секции Var – описание переменных Form1, b, i и описаниезаголов-ка функции OneSymbStr, предназначенной для создания строки повторяю-щихсяd раз символов Ch.

16.3. Раздел Implementation

Раздел Implementation модуля Unit предназначен для описаниявнутренних, т.е. доступных к использованию только внутри данного Unit,компонент: типов, констант, переменных, процедур и функций. Так, в вышеприведенномпримере в разделе Implementation содержится описание процедурыTForm1.Button1Click(Sender: TObject) и функции OneSymbStr.

16.4. Инициирование и завершение модуля

Всякий модуль может содержать блок инициирования и блокзавершения. Эти блоки располагаются в нижней части модуля, непосредственнопримыкая к последнему оператору end. Первый блок начинается словомinitialization, второй – словом finalization.

Блок инициирования initialization заканчивается последнимоператором end модуля либо, при наличии блока завершения, продолжается до словаfinalization.

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

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

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

Initialization {инициирование}

Ga:= 0;

AssignFile(f, 'c:\Projects\BreadPro\First.dat');

Reset(f, SizeOf(Rec1));

New(AppMem);

Finalization {завершение}

Dispose(AppMem);

CloseFile(f);

End. {последний оператор модуля}

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

17. Файлы

Файлом называется область данных на внешнем носителе –жестком диске, дискете и пр. Всякий файл имеет имя, представляющее собой строкусимволов. Различают обычное имя (или просто имя) и полное имя. Под полнымименем понимается абсолютный адрес файла, состоящий из пути и имени файла.Например, строка 'C:\Program Files\Folder1\Students.dat' является полнымименем. Оно состоит из пути 'C:\Program Files\Folder1' к файлу и собственноимени файла 'Students.dat'. Это означает, что файл 'Students.dat' расположен надиске C в папке (директории) Program Files непосредственно в подпапке(субдиректории) Folder1.

Ранее упоминалось, что в языке Object Pascal существует тритипа файлов:

<имя> = TextFile; {текстовые файлы}

<имя> = File; {файлы без типа}

<имя> = File of <тип даных>; {файлы с типом}

17.1. Файловая переменная

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

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

AssignFile(<файловая переменная >, <имяфайла>);

Например, фрагмент

Var

f1: TextFile;

FullPath: String[60];

FullPath:= 'a:\a1.res';

AssignFile(f1, FullPath);

cодержит объявление файловой переменной f1 текстового типаи строки FullPath, которые затем используются в исполнительной части дляуказания полного имени файла и связывания его с файловой переменной f1.

17.2. Текстовые файлы

Текстовой файл – это последовательность символьных строкперемен-ной длины. Всякая строка завершается маркером конца строки, всякийтекстовой файл завершается маркером конца файла. Такие файлы можно обрабатыватьтолько последовательно. Один и тот же текстовой файл не может быть открытодновременно для ввода и вывода. Файловая переменная этого файла имеет типTextFile или просто Text.

Для текстовых файлов есть две стандартные файловыепеременные – Input и Output, которые не нужно объявлять отдельно.

17.2.1. Процедуры и функции для работы с текстовым файлом

Procedure AssignFile(f: TextFile; FileName: String);

Связывает файловую переменную f с дисковым файлом FileName.

Procedure Append(f: TextFile);

Открывает существующий файл для добавления строк в конецфайла. При отсутствии файла возникает ошибка ввода/вывода.

Procedure Rewrite(f: TextFile);

Создает новый файл и открывает его для вывода. Если файлсуществует, то он уничтожается и создается как новый. Когда новый текстовойфайл закрывается, к нему автоматически добавляется маркер конца файла.

Procedure Reset(f: TextFile);

Открывает существующий файл для чтения и устанавливаетуказатель на первую строку файла. При его отсутствии возникает ошибкаввода/вывода.

Procedure Read( f: TextFile[; v1, v2, …,vN]);

Читает данные из файла и заносит их в переменные v1, v2, …,vN. Переменные могут иметь символьный, строчный или арифметические типы.

Procedure Readln( f: TextFile[; v1, v2, …,vN]);

Читает данные из файла целыми строками и заносит их впеременные v1, v2, …, vN. Если список переменных пуст, то происходитперемещение указателя на следующую строку.

Procedure Write( f: TextFile[; v1, v2, …,vN]);

Записывает данные из переменных v1, v2, …, vN в файл всимвольном виде.

Procedure SetTextBuf ( f: TextFile; Var Buf[; Size: Integer]);

Устанавливает буфер чтения текстового файла. Процедурадолжна быть вызвана после AssignFile, но до первого вызова процедур чтения.Буфер используется для чтения больших фрагментов файла, включая символы концастрок. Если размер буфера не указан, то по умолчанию он принимается равным 128.

Procedure CloseFile( f: TextFile);

Закрывает текстовой файл.

Procedure Flush( f: TextFile);

Выводит содержимое внутреннего буфера в файл.

FunctionEof( f: TextFile): boolean;

Возвращает True, если достигнут конец файла.

Function Eoln( f: TextFile): boolean;

Возвращает True, если достигнут конец текущей строки.

Function SeekEof( f: TextFile): boolean;

Возвращает статус конца файла.

Function SeekEoln( f: TextFile): boolean;

Возвращает статус конца строки.

Пример:

Var

F1, F2: TextFile;

Ch: Char;

St: String[255];

Buf: array[1..4096] of Char; { текстовойбуфер размером 4K}

begin

AssignFile(F1, 'T1.TXT');

SetTextBuf(F1, Buf); { большой буфердля ускорения чтения}

Reset(F1); {F1 открыт для чтения}

AssignFile(F2, 'WOOF.DOG');

Rewrite(F2); {F2 создан как новый длявывода }

while not Eof(F1) do {пока не достигнутконец файла – выполнять}

begin

Read(F1, Ch); {читает один символ изфайла F1}

Write(F2, Ch); {пишет один символ вфайл F2}

end;

CloseFile(F1); {файл F1 закрыт}

CloseFile(F2); {файл F2 закрыт}

Reset(F1); {F1 снова открыт для чтения}

Rewrite(F2); {F2 снова создан для вывода }

while not Eof(F1) do {пока не достигнутконец файла – выполнять}

begin

Readln(F1, St); {читает строку из файла F1}

Write(F2, St); {пишет строку в файл F2}

end;

CloseFile(F1); {файл F1 закрыт}

CloseFile(F2); {файл F2 закрыт}

end;

Приведенный фрагмент модуля является демонстрационным ипредназначен для копирования файла 'T1.TXT' в файл 'WOOF.DOG'. В первом циклеWhile – do копирование ведется посимвольно, во втором цикле – построчно.

Пример процедуры, записывающей в конец текстового файластроку символов:

Procedure AddStrToTextFile(nF, St:String);

Var f: Text;

Begin

AssignFile(f, nF);

If not FileExists(nF) then Rewrite(f) {несуществует, создать и открыть}

Else {иначе}

Begin

Reset(f); {существует, открыть }

While not Eof(f) doReadln(f); {передвинуть указатель в конец файла}

End;

Writeln(f, St); {записать строку }

CloseFile(f); {закрыть файл}

End;

К процедуре можно обратиться, например, так:

Var

S1: String[58];

S2: String[189];

AddStrToTextFile('c:\Files\ring.txt', 'Строкасимволов');

AddStrToTextFile('ring.txt', S1);

AddStrToTextFile('ring.txt', S2);

17.3. Файлы с типом

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

Примеры объявления файловой переменной для файлов с типом:

Var

F1: File of String[45];

F2: File of Real;

F3: File of tRecord24;

После каждого чтения или вывода записи указательавтоматически устанавливается на следующую запись.

17.3.1. Процедуры и функции для работы с типизированнымфайлом

Procedure AssignFile( f: File of Type; FileName: String);

Связывает файловую переменную f с дисковым файлом FileName.

Procedure Rewrite( f: File of Type);

Создает новый файл и открывает его. Если файл существует,то он уничтожается и создается как новый.

Procedure Reset( f: File of Type);

Открывает существующий файл и устанавливает указатель напервую запись. При отсутствии файла возникает ошибка ввода/вывода.

Procedure Read( f: File of Type[; v1, v2,…,vN]);

Читает записи из файла и заносит их в переменные v1, v2, …,vN. Чтение начинается с той записи, на которую установлен указатель. Типы файлаи переменных должны быть одинаковы.

Procedure Write( f: File of Type[; v1, v2, …,vN]);

Записывает данные из переменных v1, v2, …, vN в файл. Выводданных начинается с той записи, на которую установлен указатель. Если указательустановлен на существующую запись, то при выводе она будет замещена новойзаписью. Если одновременно выводится несколько записей, то будет замещено такоеже количество существующих записей. Типы файла и переменных должны бытьодинаковы.

Procedure Seek( f: File of Type; N: LongInt);

Перемещает указатель на запись с номером N. Первая записьимеет порядковый номер 0.

Function FilePos( f: File of Type): LongInt;

Возвращает номер записи, на которую установлен указатель.

Procedure CloseFile( f: File of Type);

Закрывает файл.

Function Eof(f: File of Type): boolean;

Возвращает True, если достигнут конец файла.

Function FileSize(f: File of Type): LongInt;

Возвращает количество записей в файле. Например, Seek(f,FileSize(f)) установит указатель в конец файла (после последней записи).

Procedure Truncate(f: File of Type);

Уничтожает (отрубает) конец файла начиная с записи, накоторую установлен указатель.

17.4. Файлы без типа

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

Файловая переменная может быть объявлена так:

Var F: File;

После каждого чтения или вывода записи указательавтоматически устанавливается на следующую запись.

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

17.4.1. Процедуры и функции для работы с файлом без типа

Procedure AssignFile( f: File; FileName: String);

Связывает файловую переменную f с дисковым файлом FileName.

Procedure Rewrite( f: File);

Создает новый файл и открывает его. Если файл существует,то он уничтожается и создается как новый.

Procedure Reset( f: File[; Size: Word]);

Открывает существующий файл и устанавливает указатель напервую запись. При отсутствии файла возникает ошибка ввода/вывода. ПараметрSize указывает размер записи открываемого файла. При его отсутствии размерзаписи по умолчанию равен 1.

Procedure BlockRead( f: File; Var Buf; Count: Word[; Var Result: Word]);

Читает из файла Count записей в переменную Buf. Result –реально прочитанное количество записей.

Procedure BlockWrite( f: File; Var Buf; Count: Word[; Var Result: Word]);

Пишет в файл первых Count записей из переменной Buf. Result– реально записанное количество записей.

Procedure Seek( f: File; N: LongInt);

Перемещает указатель на запись с номером N. Первая записьимеет порядковый номер 0.

Function FilePos( f: File): LongInt;

Возвращает номер записи, на которую установлен указатель.

Procedure CloseFile( f: File);

Закрывает файл.

Function Eof(f: File): boolean;

Возвращает True, если достигнут конец файла.

Function FileSize(f: File): LongInt;

Возвращает количество записей в файле. Например, Seek(f,FileSize(f)) установит указатель в конец файла (после последней записи).

Procedure Truncate(f: File of Type);

Уничтожает (отрубает) конец файла начиная с записи, накоторую установлен указатель.

Язык Object Pascal не накладывает никаких ограничений надлину записи (теоретически она может иметь размер до 2 Гб).

Пример описания и обращения к функции ReadFromFile,читающей из файла nF в позиции Pos запись r размером Sz.

function ReadFromFile(nF: String; Pos:Word; Var r; Sz: Word): boolean;

Var

g: File;

Recs, ReadReal: Integer;

RecRead: boolean;

Begin

Assign(g, nF);

Recs:= FileSize(g) div Sz; {количествозаписей в файле}

RecRead:= (Pos < Recs); {запись сномером Pos есть ?}

if RecRead then begin {если запись есть}

Reset(g, Sz); {открыть файл}

try

Seek(g, Pos); {установить указательна запись}

BlockRead(g, r, 1, ReadReal); {прочитатьзапись}

RecRead:= (ReadReal = 1); {прочитаноуспешно ?}

finally

Close(g); {закрыть файл}

end;

end;

Result:= RecRead;

end {ReadFromFile};

Type

tStud = Record

Fio: String [60];

Curs: byte;

Stipendiya, Room: boolean;

End;

Var Stud: tStud;

if ReadFromFile('base2.ff1', 12, Stud, SizeOf(Stud))

then Writeln('Запись из 12-й позиции прочитана');

Приведем еще пример. В директории 'c:\Bases\SdudBase'находится файл 'AllStuds.bs', в котором хранятся данные о студентах в видезаписей типа

Type

TStud = Record {студент}

Fio: String[50]; {'Фамилия Имя Отчество'}

Born: byte; {Год рождения, например, 1979}

Faculty: String[4]; {Факультет, например, 'МТФ'}

Group: String[8]; {Группа, например, 'МТ 17-2'}

End;

Ниже приведена универсальная процедура, которая копирует изэтого файла в другой файл данные только о тех студентах, которые имеют заданныйгод рождения:

Procedure StudsCopy(nF1, nF2: ShortString;BornYear: byte;

Var Count: Word; Var: Ind: ShortInt);

{nF1 – файл-источник, nF2 – файл-приёмник,

BornYear – требуемый год рождения,

Count – скопировано записей,

Ind – индикатор контроля:

 0 – нормально, 1 – было неверное чтение, была невернаязапись}

Var

g: tStud;

K, Sz, i,j: Word;

f1, f2: File;

Begin

Count:= 0; {инициализация счетчика}

Ind:=0; {изначально предполагаем нормальныйпроцесс, иначе Ind изменим}

Sz:= SizeOf(g); {размер одной записи}

K:= KdnFileSize(nF1, Sz); {количествозаписей в файле-источнике}

If (K > 0) then {если в файле-источнике есть записи }

Begin

Assign(f1, nF1); {файл-источник связываем переменной f1}

Reset(f,Sz); {открываем файл-источник с записями размера Sz}

Assign(f2, nF2); {файл-приёмник связываем переменной f2 }

Rewrite(f2,Sz); {создаем новый файл-приёмник под записи размера Sz}

try

For j:=1 to K do

Begin

BlockRead(f1, g, 1, i); {чтение записи}

Case i of

1: {запись прочитана}

if (g.Born = BornYear) then { студентимеет требуемый год рождения}

begin

BlockWrite(f2, g, 1, i); {запись в файл-приёмник}

If (i > 0) then Inc(Count) {еслизаписано правильно}

else

begin Ind:= 1; Break; End; {записаноневерно, сразу выход из цикла}

end; {if}

0: begin Ind:= -1; Break; end; {запись не прочитана, сразувыход из цикла}

end; {Case}

end; {цикла For}

finally

CloseFile(f1); {закрываем файл-источник}

CloseFile(f2); {закрываем файл-приёмник}

end; {блока try –finally – end}

End {If };

End {StudsCopy};

Операторы, реализующие копирование требуемых данных в файл'1979.bs':

StudsCopy ('AllStuds.bs', '1979.bs', 1979,Count1979, Ind1979);

Case Ind1979of

-1: Writeln('Зафиксирована ошибка чтения');

1: Writeln('Зафиксирована ошибка записи');

0: Writeln('Процесс прошел нормально');

end; {Case}

Writeln('Скопировано записей: ' + IntToStr(Count1979));

В этом примере использована внешняя процедура KdnFileSize{количество записей в файле }. Приведем ее текст:

function KdnFileSize(nF: ShortString, Siz: Word): LongInt;

{nF – имя файла, Siz – размер одной записи }

Var

F: File;

L: LongInt;

Begin

L:=0;

If FileExists(nF) then

begin

Assign(f, nF);

Reset(f,1);

L:= SizeOf(f);

If not (L mod Siz = 0) then Writeln('Файл ' + nF + имеет другой тип');

L:= L div Siz;

CloseFile(f);

End;

Result:= L;

End;

17.5. Процедуры и функции для работы с файлами

Эти подпрограммы предназначены для работы с файлами,папками (директориями) и дисками.

Procedure ChDir(Dir: String);

Делает папку Dir текущей. Пример: ChDir('c:\');

Procedure GetDir(D: Byte; Var Dir: String);

Возвращает текущую папку на заданном устройстве. (D= 0 –текущий диск, 1 – диск А, 2 – диск B и т.д.). Пример:GetDir(0, s);

Procedure RmDir(Dir: String);

Уничтожает заданную папку. Папка не должна содержатьвложенных папок или файлов. Пример: RmDir('Folder66');

Procedure Erase(f);

Удаляет файл, связанный с файловой переменной f. Файлдолжен быть закрыт.

Procedure Rename(f, FileName: String);

Переименовывает файл, связанный с файловой переменной f.Файл должен быть закрыт. Пример: Rename(g, 'studs.txt');

Function DiskFree(D: byte): LongInt;

Возвращает количество свободной памяти в байтах наустройстве D. Код драйвера задается так же, как в процедуре GetDir. Если кодуказан неверно, то возвращает -1.

Function DiskSize(D: byte): LongInt;

Возвращает количество свободной памяти в байтах наустройстве D. Код драйвера задается так же, как в процедуре GetDir. Если кодуказан неверно, то возвращает -1.

FunctionFindFirst(const Path: string; Attr: Integer;

var F: TSearchRec): Integer;

Находит имя первого файла с заданными атрибутами Attr впапке Path. Результат поиска выводит в переменную F. Если поиск успешен, тофункция вернет 0, иначе вернет код ошибки Widows. К FindFirst можно обращатьсяне только как к функции, но и как к процедуре.

Атрибуты файла приведены в табл. 17.

Таблица17

Атрибут Описание файлов

faReadOnly

faHidden

faSysFile

faVolumeID

faDirectory

faArchive

faAnyFile

Файлы «Только для чтения»

Скрытые файлы

Системные файлы

Файл ID-значений

Папки (директории)

Архивы (файлы)

Все файлы

Тип, характеризующий найденный файл, представляет запись вида:

type

TSearchRec = Record

Time: Integer; {время}

Size: Integer; {размер файла в байтах}

Attr: Integer; {атрибуты файла}

Name: TFileName; {DOS-путь файла}

ExcludeAttr: Integer;

FindHandle: THandle;

FindData: TWin32FindData; {дополнительная информация офайле}

end;

Пример:

Var

SR: TSearchRec;

S: String;

FindFirst('c:\ProgramFiles\delphi4\bin\*.*', faAnyFile, SR);

if (SR.Attr = faArchive) then

S:= 'Файл ' +SR.Name + ' имеет размер ' + IntToStr(SR.Size) + ' байт';

В данном примере процедура FindFirst ищет первый файл помаске '*.*' (все файлы) в папке 'c:\Program Files\delphi4\bin'. Атрибут faAnyFile означает,что поиск производится по всем видам файлов, под которыми понимаются папки(директории), '.', '..' – ссылки на текущую и родительскую папку, внутренниепапки и собственно файлы. Последние в терминологии файловой атрибутикиназываются архивами. Далее, если найденный файл есть архив, т е. файл вобщепринятой терминологии, то в строку S будет помещено сообщение. Например,если найденный файл имеет имя Ig.ttg и его размер равен 15899, то S= 'ФайлIg.ttg имеет размер 15889 байтов'.

Function FindNext(var F: TSearchRec): Integer;

Находит следующий файл, атрибуты которого указаны вFindFirst.

Procedure FindClose(var F: TSearchRec);

Закрывает действие FindFirst/FindNext.

Function DeleteFile(const FileName: string): Boolean;

Удаляет файл по имени. Если файл не может быть удален илине существует – возвращает False.

Function CreateDir(const Dir: string): Boolean;

Создает новую папку.

FunctionGetCurrentDir: string;

Возвращает текущую папку.

Function GetCurrentDir: string;

Возвращает текущую папку.

Function SetCurrentDir(const Dir: string): Boolean;

Установка новой текущей папки.

Function RemoveDir(const Dir: string): Boolean;

Удаление папки. Перед удалением папка должна быть пустой.

Function ExtractFileDir(const FileName: string): string;

Выделяет из полного имени файла FileName папку, в которойсодержится это файл.

Function ExtractFilePath(const FileName: string): string;

Выделяет из полного имени файла FileName путь до файла.

Function ExtractFileExt(const FileName: string): string;

Возвращает расширение файла FileName.

Function ExtractFileName(const FileName: string): string;

Возвращает имя файла FileName (без расширения).

Function DirectoryExists(Dir: string): boolean;

Проверяет существование директории. Пример:

if DirectoryExists('C:\APPS\SALES\LOCAL')then ;

Function FileExists(FileName: string): boolean;

Проверяет существование файла. Примеры:

B:=FileExists('C:\APPS\SALES\LOCAL\Fort.pas'); {полное имя}

B:= FileExists('Fort.pas'); {указано усеченное имя файла,проверка его существования только в текущей директории}

Procedure ForceDirectories(Dir: string);

Создает новую директорию.

Procedure ForceDirectories(C:\APPS\SALES\LOCAL).

П р и м е ч а н и е. К моменту обращения к процедуредиректории APPS и SALES должны существовать.

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

Procedure DelInsideDir(FullDir: tPathStr);

Var

L: Integer;

Sr: TSearchRec;

dr, q: tPathStr;

begin

if ExistDir(FullDir) then {такая директорияесть}

begin

GetDir(0,dr); {запомнить текущую директорию}

ChDir(FullDir); {текущей становится удаляемая директория}

L:=FindFirst(Slash(FullDir)+'*.*',faAnyFile,Sr);{поискпервого файла}

try

While (L = 0) do begin {пока файлы находятся}

Case Sr.Attr of

faDirectory:{найденный файл – внутренняя директория}

if (Sr.Name<>'.') and (Sr.Name<>'..') then {этоне ссылка, директория}

begin

{удаление внутреннего содержимого директории}

DelInsideDir(Slash(FullDir)+Sr.Name);

q:= Slash(FullDir)+Sr.Name;

ChDir(ExtractFilePath(q));

{удаление самой директории (можно, т. к. она теперь пуста)}

if NotEmpStr(ExtractFileName(q)) thenRmDir(ExtractFileName(q));

end;

faArchive: DeleteFile(Sr.Name); {этофайл, удаляется}

end; {Конец Case-оператора}

L:= FindNext(Sr); {следующий файл директории}

end; {цикла While}

finally

FindClose(Sr); {закрыть поиск файлов}

end; {try – finally – end}

ChDir(dr); {вернуться в текущую директорию}

end; {if}

end;{процедуры}

Например, если необходимо стереть данные с дискеты, то этоможно сделать с помощью оператора: DelInsideDir('A:\');

18. Классы и объекты

В Object Pascal классами называются специальные типы,которые содержат поля, методы и свойства. Предшественником класса является устаревшийныне тип языка Turbo Pascal, называемый объектом. Объект был введен в TurboPascal до создания Delphi. С появлением Delphi в новой версии языка ObjectPascal объекты, для совместимости со старым программным продуктом, сохранены.Однако ныне использование объектов не актуально.

Класс представляет собой указатель. Однако в отличие оттрадиционных указателей это указатель особого типа: в нем нельзя использоватьсимвол "^" при обращении к классу.

18.1. Инкаспуляция, наследование и полиморфизм

Класс, объединяя в себе поля, методы и свойства в единоецелое, является законченной структурной единицей, предназначенной для решенияотдельной задачи. Обычно такой задачей является задача разрешения некоторогокруга сопутствующих проблем. Так, класс TRichEdit представляет собой мощныйтекстовой редактор rtf-файлов (файлов в формате Rich Text Format), которыйпредназначен для организации просмотра и редактирования файла, сохранения иизменения размеров и типов шрифтов, поиска строк символов и многого другого.Такое объединение полей, методов и свойств в единое целое называетсяинкаспуляцией.

В языке существует множество классов (около 300), которыесозданы разработчиками языка Object Pascal – сотрудниками фирмы InpriseInternational – для программистов, использующих среду Delphi. Такие классыможно назвать фирменными.

Программист, составляя программу, всегда создает своипользовательские классы. Эти классы создаются либо неявно, когда программистконструирует программу визуальными средствами Delphi, а текст классов при этомсоставляет сама Delphi, либо явно, когда программист пишет код классасредствами языка Object Pascal.

Новый класс строится на основе другого, более простого,класса. Для этого в заголовке класса указывается его класс-родитель. Синтаксисзаголовка нового класса имеет вид

type className = class (ancestorClass)

Здесь className – имя нового класса; ancestorClass – имякласса-родителя. Новый класс автоматически наследует поля, методы и свойствасвоего родителя и может пополниться своими полями, методами и свойствами. Этосвойство классов называется наследованием. Возможность наследования позволяет,следуя методу от простого к сложному, создавать классы какой угодно степенисложности. Простейшим классом является класс TObject, который не содержит полейи свойств, однако имеет некоторое множество методов, обеспечивающих создание,уничтожение и обслуживание этого класса и необходимых для нормальногофункционирования программы. Именно на основе этого общего для всех классовпрародителя строится дерево наследования классов. Например:

type TPersistent = class (TObject),

type TComponent = class (TPersistent),

type TControl = class (TComponent).

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

18.2. Синтаксис класса

Синтаксис всякого класса имеет вид

type className = class (ancestorClass)

memberList

end;

Здесь className – имя класса; class – ключевое слово; ancestorClass – типкласса-родителя; memberList – списокполей, методов и свойств. Нижеприведен текст модуля main, содержащий класс TForm1.

unit main;

interface

uses

Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms;

type

TForm1 = class(TForm) {объявление класса TForm1}

Button1: TButton; {поле}

L1: TLabel; {поле}

L2: TLabel; {поле}

Button2: TButton; {поле}

procedure Button1Click(Sender: TObject); {метод}

procedure FormActivate(Sender: TObject); {метод}

end;

Var i: Integer;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender:TObject); {описание метода}

begin

L1.Caption:= DateTimeToStr(Date);

L2.Caption:= TimeToStr(Time);

end;

procedure TForm1.FormActivate(Sender:TObject); {описание метода}

begin

i:=125;

end;

end.

18.3. Поля класса

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

type

TKdnClass = class(TObject)

i, j: integer;

s: String;

TKdn1: TKdn0;

End;

Если потомком является TObject, то в заголовке его можноопустить.

Класс-потомок имеет доступ ко всем полям своих предков, ноне может их переопределять, т. к. он станет недоступен. Пример:

type

TPredok = class {объявление класса-предка}

Value: Integer;

end;

TPotomok = class(TPredok) {объявление класса-потомка}

Value: string; {перекрытие наследуемого поля}

end;

var

My1: TPredok; {объявление переменной класса}

My2: TPotomok; {объявление переменной-класса}

begin

My1 := TPotomok.Create; {создает класс типа TPredok !}

My2 := TPotomok.Create; {создает класс типа TPotomok}

My1.Value := 'Hello!'; {ошибка, не тот тип поля TPredok}

My2.Value := 'Hello!'; {правильно, работает поле Value:String}

My2.Value := 8; {ошибка: поле Value: Integer перекрыто}

end;

В этом примере описано два класса: TPredok – предок иTPotomok – потомок. Каждый из классов содержит одноименные поля Value разныхтипов.

Далее в var-секции объявлены две различные переменные My1 иMy2 типа class. На первый взгляд может показаться, что оператор-конструкторобъекта My1:= TPotomok.Create создаст объект My1 (выделит под него память) типаTPotomok. Однако это не так, поскольку My1 имеет другой тип. По этой причинеконструктор создаст объект родительского типа, т. е. объект типа TPredok.Теперь становится понятен источник ошибок, которые имеют место в несколькихоператорах приведенного примера.

18.4. Методы класса

Методом класса является инкаспулированная процедура илифункция. Эти подрограммы объявляются так же, как обычные подпрограммы. Методдолжен быть объявлен в описании класса в виде отдельного заголовка, а кодметода – описан в секции implementation с указанием через символ "."принадлежности метода к своему классу, например:

type

TMyClass = class(TObject){объявлениекласса}

...

procedure DoSomething; {объявление метода DoSomething}

...

end;

Описание для DoSomething должно быть приведено позже всекции implementation модуля:

procedure TMyClass.DoSomething;{вид заголовка класс.метод}

begin

...

end;

При обращении к методу возможно использование составногоимени либо оператора With, например:

Var KdnClass: TKdnClass;

KdnClass.MyProc1; // два примера обращения к методам

X:= KdnClass.MyFunc2; // с помощью составных имен

With KdnClass do // те же обращения

Begin // с помощью оператора With

MyProc1;

X:=MyFunc2;

End;

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

Для расширения возможностей чаще используется динамическоеперекрытие. Для этого родительский метод должен иметь директиву dinamic(динамический метод) или virtual (виртуальный метод), а перекрывающий метод –директиву override. Пример:

type

TFigure = class

procedure Draw; virtual; {виртуальныйметод}

end;

TRectangle = class(TFigure)

procedure Draw; override; {перекрывающийметод}

end;

TEllipse = class(TFigure)

procedure Draw; override; {перекрывающийметод}

end;

В этом примере объявлен виртуальный метод Drawродительского класса TFigure и два одноименных метода в классах-потомкахTRectangle и TEllipse. Последние объявлены перекрывающими (override).

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

var

Figure: TFigure;

begin

Figure := TRectangle.Create; //созданиекласса

Figure.Draw; // вызов TRectangle.Draw

Figure.Destroy; // уничтожение класса

Figure := TEllipse.Create; //созданиекласса

Figure.Draw; // вызов TEllipse.Draw

Figure.Destroy; // уничтожение класса

end;

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

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

procedure DoSomething; virtual; abstract;

Обращение к неперекрываемому абстрактному методу вызываетошибку времени выполнения (run time error), например:

Type

TClass2 = class(TClass0)

procedure Paint; virtual; abstract;

end;

TClass1 = class(TClass0)

procedure Paint; override;

end;

var

jClass1: TClass1;

jClass2: TClass2;

begin

jClass1.Paint; // правильно

jClass2.Paint; // неправильно: обращение к абстрактномуметоду

end;

Каждый класс имеет два особых метода – конструктор идеструктор. Конструктор предназначен для создания класса, т. е. для выделенияпод него динамической памяти. Деструктор, наоборот, предназначен дляуничтожения класса, т. е. для освобождения участка памяти, занятого этимклассом. В классе TObject имеются стандартные методы Create (создать) и Destroy(уничтожить). В этом классе объявлен также метод Free, который сначалапроверяет корректность адреса и только потом вызывает метод Destroy. В этойсвязи предпочтительнее использовать метод Free вместо метода Destroy. Всякийкласс по умолчанию содержит переменную Self, в которую после выделениядинамической памяти помещается адрес класса. Прежде чем выполнить обращение кметодам класса, его нужно создать. Хотя конструктор и деструктор являютсяпроцедурами, они объявляются специальными словами. Конструктор объявляетсясловом Constructor, деструктор – словом Destructor. Часто для обеспечениядоступа к полям предка в конструкторе необходимо предварительно создатькласс-предок. Это можно сделать c помощью слова Inherited.

Пример:

type

TShape = class(TGraphicControl)

Private {внутренние объявления}

FPen: TPen;

FBrush: TBrush;

procedure PenChanged(Sender: TObject);

procedure BrushChanged(Sender: TObject);

 

public {внешние объявления}

constructor Create(Owner: TComponent);override;

 

destructor Destroy; override;

...

end;

 

constructor TShape.Create(Owner:TComponent);

begin

inherited Create(Owner); // созданиекласса-предка TGraphicControl

Width := 65; // изменение наследуемых свойствTGraphicControl

Height := 65;

FPen := TPen.Create; // создание отдельного поля TPen типаclass

FPen.OnChange := PenChanged;

FBrush := TBrush.Create; // созданиеотдельного поля TBrush типа class

FBrush.OnChange := BrushChanged;

end;

Некоторые простые классы могут быть созданы и уничтоженыбез объявления конструкторов и деструкторов. Например, если класс являетсяпотомком TObject, то в нем явно Constructor и Destructor в некоторых случаяхобъявлять нет нужды:

Type TClassy = class;

..

var Classy: TClassy;

Classy:= TClassy.Create; {создание класса}

Classy:= TClassy.Free; {уничтожениекласса}

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

Пример:

Type TClassy = class;

Procedure HH(i, j: byte; var s: String);reintroduce; overload;

Procedure HH(q: String); reintroduce;overload;

Procedure HH(a: array oh Integer);reintroduce; overload;

implementation

Procedure TClassy.HH(i, j: byte; var s:String);

Begin

S:=IntToStr(i + j);

End;

 

Procedure TClassy.HH(q: String);

Begin

L2.Cattion:= q;

End;

 

Procedure TClassy.HH(a: array oh Integer);

Begin

L1.Cattion:= IntToStr(a[6] + a[4]);

End;

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

18.5. Свойства класса

Свойства, как и поля, являются атрибутами класса. Свойстваобъявляются с помощью слов property, read и write. Слова read и writeконкретизируют назначение свойства. Синтаксис свойства таков:

property propertyName[indexes]: type indexintegerConstant specifiers;

где propertyName – имя свойства; [indexes] – параметры-имена в формеимя1, имя2,…, имяN: type; index – целая константа; read,write, stored, default (или nodefault) и implements – спецификации. Всякоеобъявление свойства должно иметь одну из спецификаций read или write или обевместе.

Примеры:

property Objects[Index: Integer]: TObjectread GetObject write SetObject;

property Pixels[X, Y: Integer]: TColorread GetPixel write SetPixel;

property Values[const Name: string]:string read GetValue write SetValue;

property ErrorCount: Integer readGetErrorCount;

property NativeError: Longint readFNativeError;

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

Каждое свойство может иметь спецификацию read или write илиоба вместе в форме

read fieldOrMethod

write fieldOrMethod

где fieldOrMethod – имя поля или метода, объявленного вклассе, или свойство класса-предка.

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

property Color: TColor read GetColor writeSetColor;

тогда метод GetColor должен быть описан как

function GetColor: TColor;

и метод SetColor должен быть описан как

procedure SetColor(Value: TColor);

или

procedure SetColor(const Value: TColor);

Если свойство имеет спецификацию read, то оно имеет атрибут«read only» (только для чтения). Если свойство имеет спецификациюwrite, то оно имеет атрибут «write only» (только для чтения).

18.6. Структура класса

Всякий класс имеет структуру, которая состоит из секций.Каждая секция объявляется специальным зарезервированным словом. К их числуотносятся: published (декларированные), private (частные), protected(защищенные), public (доступные), automated (автоматизированные). Внутри каждойсекции сначала объявляются поля, затем – свойства и методы.

Пример:

type

TMyClass = class(TControl)

private

… { частные объявления здесь}

protected

… { защищенные объявления здесь }

public

… { доступные объявления здесь }

published

… { декларированные объявления здесь }

end;

Секции определяют области видимости компонент класса:

Private – компоненты класса доступны только внутри этогокласса;

Public – компоненты класса доступны в текущем и любомдругом модуле, который содержит ссылку в списке uses на модуль, в которомобъявлен класс;

Published – то же, что Public, однако в ней должны бытьперечислены свойства, которые доступны не только на этапе выполнения программы,но и на этапе ее визуального конструирования средствами Delphi;

Protected – cекция доступна только методам текущего классаи методам классов-предков;

Automated – секция используется для объявления свойств иметодов обработки OLE-контейнеров в рамках OLE-технологии.

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

18.7. Операции над классами

Над классами разрешено выполнять две операции – is и as.

1. Операция is. Синтаксис выражения, содержащего операциюis, имеет вид

object is class

Это выражение имеет логический тип (boolean) и возвращаетTrue, если переменная object имеет тип class класса, иначе – False.

Пример:

if ActiveControl is TEdit thenTEdit(ActiveControl).SelectAll;

В этом примере: если класс ActiveControl имеет тип TEdit, то будетвыполнен метод TEdit(ActiveControl).SelectAll.

2. Операция as. Синтаксис выражения, содержащего операциюas:

object as class

Результатом вычисления этого выражения является ссылка наобъект того же типа, что и тип класса class. При выполнении программы objectможет иметь тот же тип, или тип класса-потомка, или nil.

Примеры:

with Sender as TButton do // если Sender имеет тип TButton

begin // или тип-потомок от TButton

Caption := '&Ok';

OnClick := OkClick;

end;

(Sender as TButton).Caption := '&Ok';//свойству Caption переменной

// Sender типа TButton или его потомка присваиваетсязначение '&Ok'

Приложение

Перечень

отлаженных процедур и функций,

написанных автором

Ниже использованы глобальные типы и переменные:

Type

CompareType = (Less, Equal, Greater);

Var

Lon, Lon2: LongInt;

Serv: String[255];

Procedure Delay(MilliSec: LongInt);

{задержка времени на MilliSec миллисекунд}

Var k: LongInt;

begin

k:=GetTickCount; {в модуле Windows.pas}

While GetTickCount<(MilliSec+k) do ;

end;

Function Ctrl_ Shift_Down(i: byte): boolean;

{Нажата ли одна из этих клавиш Ctrl – 1, Shift – 2}

var

ShiftState: TShiftState;

j: LongInt;

begin

Result:=false;

Case i of

1: j:= VK_CONTROL;

2: j:= VK_SHIFT;

end;

ShiftState := KeyDataToShiftState(j);

Case i of

1: Result:= (ssCtrl in ShiftState);

2: Result:= (ssShift in ShiftState);

end;

end;

Function CtrlDown: boolean;

{нажата ли клавиша Ctrl}

begin

Result:=Ctrl_ Shift_Down(1);

end;

Function ShiftDown: boolean;

{нажата ли клавиша Shift}

begin

Result:=Ctrl_Shift_Down(2);

end;

Function Profit(Expend, Price: Real): Real;

{рентабельность=(цена — затраты)/затраты*100}

begin

if (Expend<>0) then Result:=(Price/Expend-1.0)*100.0

else Result:= 1.e5;

end;

Procedure Warn1(S: Variant);

{Окно с Variant-значением, например Warn1('Процессзакончен')}

begin

MessageDlg(S, mtInformation, [mbOk], 0);

Screen.ActiveForm.Refresh;

End;

Procedure Warn4(s1,s2,s3,s4: String);

{то же, что Warn1, но в 4 строки}

var i,j: byte;

begin

i:=Length(s1); j:=i;

i:=Length(s2);

if (i>j) then j:=i;

i:=Length(s3);

if (i>j) then j:=i;

i:=Length(s4);

if (i>j) then j:=i;

Warn1(Center(s1,j)+''#13#10+''+Center(s2,j)

+''#13#10''+Center(s3,j)+''#13#10+''+Center(s4,j));

end;

Function DaNet(S: String): boolean;

{Окно. Предназначено для вопроса, на который можноответить, щелкнув по одной из кнопок «Да» или «Нет»}

begin

DaNet:=MessageDlg(S, mtConfirmation,[mbYes, mbNo], 0)=mrYes;

Screen.ActiveForm.Refresh;

end;

Function DaNet4(s1,s2,s3,s4: String): boolean;

{Окно. То же, что DaNet, только в 4 строки}

begin

DaNet4:=MessageDlg(Trim(s1)+''#13#10+''+Trim(s2)+''#13#10''+Trim(s3)

+''#13#10+''+Trim(s4),mtConfirmation,[mbYes,mbNo], 0)=mrYes;

Screen.ActiveForm.Refresh;

end;

Function InOtrReal(i,a,b: real): boolean;

{Если i в орезке [a, b], то возвращает True}

begin

Result:=(i>=a) and (i<=b);

end;

Function ExitK: boolean;

{стандартный вопрос о выходе}

begin

ExitK:=DaNet('Выход ?');

end;

Function Pos2(SubS, S: String; StartPos: byte): boolean;

{входит ли SubS в S начиная с StartPos}

begin

Lon:=Pos(SubS,S);

Result:= (Lon > 0) and (StartPos =Lon);

end;

Function ChStr(Ch: Char; d: Word): String;

{создает строку из символа Ch, повторенного d раз}

begin

if d>0 then

begin

SetLength(Result,d);

FillChar(Result[1],d,Ch);

end;

end;

Function Prop(d: Word): String;

{создает строку из d пробелов}

begin

Result:=ChStr(' ',d);

end;

Function Pad(s: String; d: Word): String;

{вставляет справа от строки пробелы, добирая ее до длины d}

begin

Serv:=s;

Lon:=Length(s);

If (d>Lon) then Serv:=s+Prop(d-Lon);

Result:=Serv;

end;

Function PadCopy(s: String; n,d: Word): String;

{копирует из s начиная с позиции n строку длины d. В случаеменьшей строки добирает ее до длины d}

begin

Serv:=Copy(s,n,d);

if Length(Serv) < d thenServ:=Pad(Serv,d);

Result:=Serv;

end;

Function LeftPad(s: String; d: Word): String;

{вставляет слева от строки пробелы, добирая ее до длины d}

begin

Serv:=s;

Lon:=Length(s);

if (d>Lon) then Serv:=Prop(d-Lon)+s;

Result:=Serv;

end;

Function Center(s: String; d: Word): String;

{вставляет слева и справа от строки поровну пробелы,добирая ее до длины d}

begin

Serv:=s;

Lon:=Length(s);

Lon2:=Round(0.5*(d-Lon));

if (d>Lon) thenServ:=Prop(Lon2)+s+Prop(d-Lon2);

Result:=Serv;

end;

Function CompStrings(s1,s2: String): CompareType;

{сравнение строк:s1<s2 — Less, s1=s2 — Equal, s1>s2 — Greater}

begin

if (s1<s2) then CompStrings:=Less

else

if (s1>s2) then CompStrings:=Greater

else

CompStrings:=Equal;

end;

Function CompReal(r1,r2: Real): CompareType;

{сравнение вещественных чисел}

begin

if (r1<r2) then Result:=Less

else

if (r1>r2) then Result:=Greater

else

Result:=Equal;

end;

Procedure IncRe(Var r: Real; h: real);

begin

r:=r+h;

end;

Function LongToStr(L: LongInt; d: byte): String;

{конвертирует целое в строку длины d}

begin

Str(L,Serv);

Result:=LPad(Serv,d);

end;

Function Long2Str(L: LongInt): String;

{конвертирует целое в строку}

begin

Str(L,Serv);

Result:=Serv;

end;

Function StrLong(st: String): LongInt;

{конвертирует строку в целое }

begin

Val(Trim(st),Lon,Code);

Result:=Lon; end;

Function Str2Long(st: String; Var L: LongInt): boolean;

{конвертирует строку в целое. Возвращает True в случаеуспеха}

begin

Val(Trim(st),L,Code);

Result:=(Code=0);

end;

Function RealToStr(R: Real; Posle: byte): String;

{Конвертирует Real в строку, Posle – количество символов вдробной части R}

begin

Str(R:20:Posle,Serv);

RealToStr:=Trim(Serv);

end;

 

Function Slash(Dir: String): String;

{ставит в конец пути символ '\'}

begin

Serv:=Trim(Dir);

if (Serv[Length(Serv)]<>'\') thenResult:=Serv+'\'

else Result:=Serv;

end;

Function ChWinDos(Ch: Char): Char;

{преобразует русский Windows-символ в русский DOS-символ}

Var i,j: byte;

begin

i:=Ord(Ch);

Case i of

168: {Ё} j:=240;

184: {ё} j:=241;

192..255: if (i>239) then j:=i-16 elsej:=i-64

else j:=i;

end;

Result:=Char(j);

end;

Function ChDosWin(Ch: Char): Char;

{преобразует русский DOS-символ в русский Windows-символ}

Var i,j: byte;

begin

i:=Ord(Ch);

Case i of

240: {Ё} j:=168;

241: {ё} j:=184;

128..175: j:=i+64;

224..239: j:=i+16

else j:=i;

end;

Result:=Char(j);

end;

Function StrWinDos(st: String): String;

{преобразует русскую Windows-строку в русскую DOS-строку}

Var

n, i: byte;

s: ^String;

begin

New(s);

n:=Length(st);

s^:= '';

if (n>0) then

for i:=1 to n do

s^:= s^+ChWinDos(st[i]);

Result:=s^;

Dispose(s);

end;

Function StrDosWin(s: String): String;

{преобразует русскую DOS-строку в русскую Windows-строку}

Var

n,i: byte;

s: ^String;

begin

New(s);

n:=Length(st);

s^:= '';

if (n>0) then

for i:=1 to n do

s^:= s^+ChDosWin(st[i]);

Result:=s^;

end;

Function InputStr(const Prompt: String; Var s: String; IsParol: byte):boolean;

{ввод строки. Prompt – пояснение, s – вводимая строка,

isParol=1, если засекреченный ввод, иначе видимый}

begin

Result:=

KdnInputQuery('Ввод строки', Prompt, s, clBlack, (IsParol=1));

end;

Function ParolControl(RealParol: String): boolean;

{возвращает True, если введенная строка совпадает сRealParol}

var

b,h: boolean;

i: byte;

begin

St:='';

i:=0;

b:=false;

Repeat

Inc(i);

h:=InputStr('Введите пароль ...',St,1);

if h then b:= (St=RealParol);

if not b and h then Warn1('Ошибка');

Until b or (i=3) or (not h);

Result:=b;

end;

Function ExistSubDir(SubDir:String; Dir: tPathStr):boolean;

{устанавливает наличие субдиректории SubDir внутридиректории Dir. Например, в D:\DIR0001 субдиректории BAR }

begin

Result:=DirectoryExists(Slash(SubDir)+Dir);

end;

Function GetFileSize(const FileName: string): LongInt;

{размер файла}

var Sr: TSearchRec;

begin

if FindFirst(ExpandFileName(FileName),faAnyFile, Sr) = 0 then

Result := Sr.Size

else Result := -1;

end;

Function FileDateTime(const FileName: string): System.TDateTime;

{время создания файла FileName, например:

s:=DateTimeToStr(FileDateTime('c:\KdnBread\Bread.exe'))}

begin

Result :=FileDateToDateTime(FileAge(FileName));

end;

Function HasAttr(const FileName: string; Attr: Word): Boolean;

{имеет ли файл FileName атрибут Attr}

begin

Result := (FileGetAttr(FileName) and Attr)= Attr;

end;

Procedure AppendText(Var f: Text; nF: String);

{открывает текстовой файл для добавления строк}

begin

Assign(f,nF);

if KdnFS(nF,1)>0 then Append(f) elseRewrite(f);

end;

Procedure AppendToText(nF,s: String);

{добавляет строку в конец текстового файла}

Var f: TextFile;

begin

AppendText(f, nF);

Writeln(f,s);

CloseFile(f);

end;

Procedure KdnExec(Command: String);

{запуск другого приложения, например'c:\KdnBreadDir\KdnBread.exe'}

begin

Serv:=Command+#0;

If WinExec(@Serv[1], SW_SHOWNORMAL)<32

then Warn2('Ошибочное завершение WinExec');

end;

еще рефераты
Еще работы по информатике, программированию