3506

Язык программирования Delphi (Object Pascal)

Конспект

Информатика, кибернетика и программирование

Язык программирования Delphi (Object Pascal) Основным инструментом Delphi, его ядром, является язык Object Pascal. По сравнению с первоначальной версией Pascal и Borland Pascal 7.0 версия Object Pascal, используемая в Delphi, подверглась серьезным д...

Русский

2012-11-02

408.81 KB

192 чел.

Язык программирования Delphi (Object Pascal)

Основным инструментом Delphi, его ядром, является язык Object Pascal. По сравнению с первоначальной версией Pascal и Borland Pascal 7.0 версия Object Pascal, используемая в Delphi, подверглась серьезным дополнениям и изменениям.

1. Алфавит Object Pascal

Алфавит языка Object Pascal включает буквы, цифры, шестнадцатеричные цифры, специальные символы, пробелы и зарезервированные слова.

Буквы - это буквы латинского алфавита от а до z и от А до Z , а также знак подчеркивания “_”. В языке нет различия между заглавными и строчными буквами алфавита, если только они не входят в символьные и строковые выражения.

Цифры - арабские цифры от 0 до 9.

Каждая шестнадцатеричная цифра имеет значение от 0 до 15. Первые 10 значений обозначаются арабскими цифрами 0... 9, остальные шесть - латинскими буквами а ... f или а... f.

Специальные символы Object Pascal - это следующие символы:

+ - * / = , ' . : ; < > [ ] ( ) { } " @ $ #

К специальным символам относятся также следующие пары символов:

< > , < = , > = , : = , ( * , * ) , ( . , . ) , / / .

В программе эти пары символов нельзя разделять пробелами, если они используются как знаки операций отношения или ограничители комментария. Символы (. и .) могут употребляться соответственно вместо [ и ].

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

1.1. Зарезервированные слова и стандартные директивы

В Object Pascal имеются следующие зарезервированные слова:

and

exports

mod

Shr

array

file

nil

String

as

finalization

not

then

asm

finally

object

threadvar

begin

for

of

to

case

function

or

try

class

goto

out

type

const

if

packed

unit

constructor

implementation

procedure

until

destructor

in

program

uses

dispinterface

inherited

property

var

div

initialization

raise

while

do

inline

record

with

downto

interface

repeat

xor

else

is

resourcestring

 

end

label

set

 

except

library

shi

 

 

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

Стандартные директивы первоначально связаны с некоторыми стандартными объявлениями в программе. К ним относятся:

absolute

dynamic

name

public

safecall

abstract

export

near

published

stdcall

assembler

external

nodefault

read

stored

automated

far

override

readonly

virtual

cdecl

forward

package

register

write

contains

implements

pascal

re introduce

writeonly

default

index

private

requires

 

dispid

message

protected

resident

 

Как и зарезервированные слова, стандартные директивы в окне кода Delphi выделяются жирным шрифтом, тем не менее, можно переопределить любую стандартную директиву, т.е. объявить одноименный идентификатор.

Слова private, protected, public, published и automated считаются зарезервированными внутри объявления класса и стандартными директивами вне объявления.

1.2. Комментарии

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

{Это комментарий}

(*Это тоже комментарий*)

//Все символы до конца этой строки составляют комментарий

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

{$R *.RES}

2. Структура программы

Среда программирования Delphi прежде всего предназначена для программирования в операционной системе Windows. Данная часть курса направлена на изучение общих вопросов программирования языка Object Pascal. Поэтому здесь будут рассмотрены так называемые консольные приложения.

Консоль – это монитор и клавиатура, рассматриваемые как единое устройство. Консольное приложение – программа, предназначенная для работы в операционной системе MS-DOS (или в окне DOS ОС Windows), для которой устройством ввода является клавиатура, а устройством вывода – монитор, работающий в режиме отображения символьной информации (буквы, цифры и специальные знаки). Для удобства усвоения материала под программой будем понимать консольное приложение.

Любая программа на языке Object Pascal состоит из одного или нескольких модулей (файлы с расширениями dpr, pas).

Текст программы или так называемый листинг состоит из предложений, которые разделяются точкой с запятой (;). Последнее предложение оканчивается точкой. В одной строке можно указывать несколько описаний или операторов.

В общем случае, программа на языке Object Pascal состоит из заголовка, раздела описаний и раздела выполнения (раздела операторов).

заголовок

 

 

Program <имя>;

раздел описаний

 

Uses - раздел описания внешних модулей

 Label - раздел описания меток

 Const - раздел описания констант

 Type - раздел описания типов

 Var - раздел объявления переменных

 Procedure, Function - раздел подпрограмм

begin

раздел выполнения

оператор 1

оператор n

end.

2.1. Заголовок

Слово Program со следующим за ним именем программы и точкой с запятой образуют заголовок программы. За заголовком следует раздел описаний, в котором программист (или Delphi) описывает используемые в программе идентификаторы. Идентификаторы обозначают элементы программы, такие как типы, переменные, процедуры, функции.

Для указания того, что создается консольное приложение, после заголовка следует разместить специальную директиву:

{$APPTYPE CONSOLE}

2.2. Раздел описания внешних модулей

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

Uses

mylib in 'mylib.pas';

2. Раздел описания меток

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

–Например,

label

MyBlock;

begin

Goto MyBlock;

 {Программист требует передать управление оператору, помеченному

меткой MyBlock. 

Операторы, размещенные здесь будут пропущены.}

MyBlock:

 {Оператору, идущему за этой меткой, будет передано управление.}

end;

2.4. Раздел описания констант

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

2.5. Раздел описания типов

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

2.6. Раздел объявления переменных

Переменные связаны с изменяемыми областями памяти, т.е. с такими ее участками, содержимое которых будет меняться в ходе работы программы. Для объявления переменной после ее идентификатора ставится двоеточие и имя типа, по образу которого должна строиться переменная. Разделу объявления переменной (переменных) должно предшествовать зарезервированное слово var. Переменные рассмотрены в разделе 6.

2.7. Раздел подпрограмм (процедур и функций)

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

В Object Pascal есть два сорта подпрограмм: процедуры и функции. Функция отличается от процедуры только тем, что функция имеет выходной результат определенного типа.

Создание и работа с процедурами и функциями в Object Pascal рассмотрены в разделах 15.

–2.8. Раздел выполнения

Собственно тело программы начинается со слова begin (начать) и ограничивается терминатором end с точкой. Тело состоит из нескольких операторов языка Object Pascal. В каждом операторе реализуется некоторое действие - изменение значения переменной, анализ результата вычисления, обращение к подпрограмме и т. п. Основные операторы и операции Object Pascal рассмотрены в разделах 7.-11.

Идентификаторы

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

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

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

i

MyVariable

external

ANNA

_alpha

Func_Counter

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

4. Стандартные типы данных

Любые данные (константы, переменные, свойства, значения функций или выражения), в Object Pascal характеризуются своими типами. Тип определяет множество допустимых значений, которые может иметь тот или иной объект, а также множество допустимых операций, которые применимы к нему. Кроме того, тип определяет также и формат внутреннего представления данных в памяти ЭВМ.

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

Типы

Простые

Структурированные

Порядковые

Дата-Время

Вещественные

Целые

Логические

Символьные

Перечисляемые

Тип-диапазон

Строки

Указатели

Объекты

Классы

Процедурные

Варианты

Массивы

Записи

Множества

Файлы

Настраиваемые варианты

Рис. 1. Структура типов языка Object Pascal

К простым типам относятся порядковые, вещественные типы и тип дата-время.

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

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

Тип дата-время предназначен для хранения даты и времени. Фактически для этих целей он использует вещественный формат.

4.1. Порядковые типы.

К порядковым типам относятся (см. рис. 1) целые, логические, символьный, перечисляемый и тип-диапазон.

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

Таблица 1. Характеристики целых типов

Название типа

Длина,байт

Диапазон значений

Cardinal

4

0. .. 2 147 483 647

Byte

1

0...255

Shortint

1

-128...+127

Smallint

2

-32 768...+32 767

Word

2

0...65 535

Integer

4

-2 147 483 648...+2 147 483 647

Longint

4

-2 147 483 648...+2 147 483 647

Int64

8

-9*10 18 . ..+9*10 18

LongWord

4

0. . .4 294 967 295

 

–Примечание

Типы LongWord и Int64 впервые введены в версии 4, а типы Smallint и Cardinal отсутствуют в Delphi 1. Тип integer для этой версии занимает 2 байта и имеет диапазон значений от -32768 до +32767, т.е. совпадает с Smallint.

При использовании процедур и функций с целочисленными параметрами следует руководствоваться “вложенностью” типов, т.е. везде, где может использоваться Word, допускается использование Byte (но не наоборот), в Longint “входит” Smallint, который, в свою очередь, включает в себя Shortint.

Перечень процедур и функций, применимых к целочисленным типам, приведен в табл. 2. Буквами b, s, w, i, l обозначены выражения соответственно типа Byte, Shortint, Word, Integer и Longint, х - выражение любого из этих типов; буквы vb, vs, vw, vi, vl, vx обозначают переменные соответствующих типов. В квадратных скобках указывается необязательный параметр.

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

Логические типы. К логическим относятся типы Boolean, ByteBool, Bool, wordBool и LongBool. В стандартном Pascal определен только тип Boolean, остальные логические типы введены в Object Pascal для совместимости с Windows: типы Boolean и ByteBool занимают по одному байту каждый, Bool и WordBool - по 2 байта, LongBool - 4 байта.

Значениями логического типа может быть одна из предварительно объявленных констант False (ложь) или True (истина).

Таблица 2. Стандартные процедуры и функции,

применимые к целым типам

Обращение

Тип результата

Действие

abs (x)

x

Возвращает модуль x

chr(b)

Char

Возвращает символ по его коду

dec(vx[,i])

-

Уменьшает значение vx на i, а при отсутствии i - на 1

inc(vx[,i])

-

Увеличивает значение vx на i, а при отсутствии i -на 1

Hi(w)

Byte

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

Hi(I)

То же

Возвращает третий по счету байт

Lo(i)

То же

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

Lo(w)

То же

То же

odd(l)

Boolean

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

Random(w)

 

 

Как у параметра

Возвращает псевдослучайное число, равномерно распределенное в диапазоне 0...(w-l)

sqr(x)

X

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

swap(i)

Integer

Меняет местами байты в слове

swap (w)

Word

Тоже

Символьный тип. Значениями символьного типа является множество всех символов ЭВМ. Каждому символу приписывается целое число в диапазоне О...255. Это число служит кодом внутреннего представления символа, его возвращает функция ord().

Для кодировки в Windows используется код ANSI (назван по имени American National Standard Institute - американского института стандартизации, предложившего этот код). Первая половина символов ПК с кодами 0... 127 соответствует табл.  Вторая половина символов с кодами 128...255 меняется для различных шрифтов. Стандартные Windows-шрифты Arial Cyr, Courier New Cyr и Times New Roman для представления символов кириллицы (без букв “ё” и “Ё”) используют последние 64 кода (от 192 до 256): “А”... “Я” кодируются значениями 192..223, “а”... “я” - 224...255. Символы “Ё” и “ё” имеют соответственно коды 168 и 184.

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

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

type

colors = (red, white, blue);

Соответствие между значениями перечисляемого типа и порядковыми номерами этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер О, второе - 1 и т. д. Максимальная мощность перечисляемого типа составляет 65536 значений, поэтому фактически перечисляемый тип задает некоторое подмножество целого типа Word и может рассматриваться как компактное объявление сразу группы целочисленных констант со значениями 0, 1 и т.д.

Таблица  Кодировка символов в соответствии со стандартом ANSI

Код

Символ

Код.

Символ

Код.

Символ

Код

Символ

0

NUL

32

BL

64

@

96

'

1

ЗОН

33

!

65

А

97

а

2

STX

34

66

В

98

b

3

ЕТХ

35

#

67

С

99

с

4

EOT

36

$

68

D

100

d

5

ENQ

37

%

69

Е

101

е

6

ACK

38

&

70

F

102

f

7

BEL

39

'

71

G

103

д

8'

BS

40

(

72

Н

104

h

9

HT

41

)

73

I

105

i

10

LF

42

*

74

J

106

j

11

VT

43

+

75

К

107

k

12

FF

44

f

76

L

108

1

13

CR

45

-

77

М

109

m

14

SO

46

 

78

N

110

n

15

SI

47

/

79

0

111

о

16

DEL

48

0

80

Р

112

P

17

DC1

49

1

81

Q

113

q

18

DC2

50

2

82

R

114

r

19

DC3

51

3

83

S

115

s

20

DC 4

52

4

84

Т

116

t

21

NAK

53

5

85

U

117

u

22

SYN

54

6

86

V

118

v

23

ETB

55

7

87

W

119

W

24

CAN

56

8

88

х

120

x

25

EM

57

9

89

Y

121

У

26

SUB

58

:

90

Z

.122

z

27

ESC

59

;

91

t

123

{

28

FS

60

<

92

\

124

1

29

GS

61

=

93

]

125

}

30

RS

62

>

94

Л

126

~

31

US

63

f

95

 

127

r

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

Переменные любого перечисляемого типа можно объявлять без предварительного описания этого типа, например:

var

col: (black, white, green);

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

<мин.знач.>..<макс.знач.>

где <мин. знач.> – минимальное значение типа-диапазона;

<макс. знач.> – максимальное значение типа-диапазона.

Например,

type

digit = '0'..'9';

dig2 = 48 .. 57;

Тип-диапазон не обязательно описывать в разделе type, а можно указывать непосредственно при объявлении переменной, например:

var

date : 1. .31;

month: 1..12;

Ichr : 'А'..'Z';

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

  1.  два символа “..” рассматриваются как один символ, поэтому жду ними недопустимы пробелы;
  2.  левая граница диапазона не должна превышать его правую границу.

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

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

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

Таблица 4. Вещественные типы

Длина, байт

Название

Количество значащих цифр

Диапазон значений

8

4

8

10

8

8 

Real

Single

Double

Extended

Comp

Currency

15…16

7…8

15…16

19…20

19…20

19…20

5.0*10e-324…1.7*10e308

1.5*10e-45…4*10e38

5.0*10e324…1.7*10e308

4*10-4951…1.1*10e4932

-2e63…+2e63-1

+/-922 337 203 685477,5807

Примечание

В предыдущих версиях Delphi 1...3 тип Real занимал 6 байт и имел диапазон значений от2, 9*10 -39 до 1,7*10 38 . В версиях 4 и 5 этот тип эквивалентен типу Double. Если требуется (в целях совместимости) использовать 6-байтньш Real, нужно указать директиву компилятора {SREALCOMPATIBILITY ON}.

Как видно из табл. 4, вещественное число в Object Pascal занимает от 4 до 10 смежных байт и имеет следующую структуру в памяти ПК:

S

е

m

где s - знаковый разряд числа;

е - экспоненциальная часть; содержит двоичный порядок;

m - мантисса числа.

Мантисса m имеет длину от 23 (для single) до 63 (для Extended) двоичных разрядов, что и обеспечивает точность 7...8 для single и 19...20 для Extended десятичных цифр. Десятичная точка (запятая) подразумевается перед левым (старшим) разрядом мантиссы, но при действиях с числом ее положение сдвигается влево или вправо в соответствии с двоичным порядком числа, хранящимся в экспоненциальной части, поэтому действия над вещественными числами называют арифметикой с плавающей точкой (запятой).

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

Особое положение в Object Pascal занимают типы comp и Currency, которые трактуются как вещественные числа с дробными частями фиксированной длины: в comp дробная часть имеет длину 0 разрядов, т.е. просто отсутствует, в currency длина дробной части -4 десятичных разряда. Фактически оба типа определяют большое целое число со знаком, сохраняющее 19...20 значащих десятичных цифр (во внутреннем представлении они занимают 8 смежных байт). В то же время в выражениях comp и currency полностью совместимы с любыми другими вещественными типами: над ними определены все вещественные операции, они могут использоваться как аргументы математических функций и т. д. Наиболее подходящей областью применения этих типов являются бухгалтерские расчеты.

Для работы с вещественными данными могут использоваться встроенные математические функции, представленные в табл. 5.

Таблица 5. Стандартные математические функции Object Pascal

Обращение

Тип параметра

Тип результата

Примечание

abs (x)

вещественный, целый

тип аргумента

вещественный

Модуль аргумента

Pi

-

вещественный

=14159265..

ArcTan(x)

вещественный, целый

то же

Арктангенс (значение в радианах)

cos (x)

то же

то же

Косинус, угол в радианах

exp(x)

то же

то же

Экспонента eX

frac(x)

то же

то же

Дробная часть числа

int(x)

то же

то же

Целая часть числа

ln(x)

то же

то же

Логарифм натуральный

Random

-

то же

Псевдослучайное число, равномерно распределенное в диапазоне 0...[1]

Random(x)

 

целый

 

целый

 

Псевдослучайное целое число, равномерно распределенное в диапазоне 0...(х-1)

Randomize

 

-

 

-

Инициация генератора псевдослучайных чисел

sin(x)

вещественный, целый

вещественный

Синус, угол в радианах

sqr(x)

тo же

то же

Квадрат аргумента

sqrt(x)

то же

то же

Корень квадратный

–4. Тип дата-время

Тип дата-время определяется стандартным идентификатором TDateTime и предназначен для одновременного хранения и даты, и времени. Во внутреннем представлении он занимает 8 байт и подобно Currency представляет собой вещественное число с фиксированной дробной частью: в целой части числа хранится дата, в дробной - время.

Дата определяется как количество суток, прошедших с 30 декабря 1899 года, а время - как часть суток, прошедших с 0 часов, так что значение 36444,837 соответствует дате 11.10.1999 и времени 20:05. Количество суток может быть и отрицательным, однако значения меньшие -693594 (соответствует дате 00.00.0000 н.э.) игнорируются функциями преобразования даты к строковому типу.

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

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

NewDate:= DateToStr(Date + 21);

поместит в переменную NewDate дату, соответствующую текущей дате плюс 3 недели.

–4.4. Строки

Для обработки текстов в Object Pascal используются следующие типы:

  1.  короткая строка shortString или string[n] , где n <= 255;
  2.  длинная строка string;
  3.  широкая строка WideString;
  4.  нуль-терминальная строка pchar.

Общим для этих типов является то, что каждая строка трактуется как одномерный массив символов, количество символов в котором может меняться в работающей программе: для string [n] длина строки меняется от 0 до n, для string и pchar - от 0 до 2 Гбайт.

В стандартном Pascal используются только короткие строки String[n]. В памяти такой строке выделяется n+i байт, первый байт содержит текущую длину строки, а сами символы располагаются начиная со 2-го по счету байта. Поскольку для длины строки в этом случае отводится один байт, максимальная длина короткой строки не может превышать 255 символов. Для объявления короткой строки максимальной длины предназначен стандартный тип ShortString (эквивалент String[255]).

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

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

Операции отношения =, <>, >, <, >=, <= выполняются над двумя строками посимвольно, слева направо с учетом внутренней кодировки символов. Если одна строка меньше другой по длине, недостающие символы короткой строки заменяются значением #о.

Например, следующие операции отношения дадут значение True:

'А' > '1'

'Object' < ' Object Pascal'

'Пас' > 'Pas cal'

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

Таблица 6. Процедуры и функции для работы со строками

Function AnsiLowerCase(const S: String ): String;

Возвращает исходную строку S, в которой все заглавные буквы заменены на строчные в соответствии с национальной кодировкой Windows (т.е. с учетом кириллицы)

Function AnsiUpperCase(const S: String ): String;

Возвращает исходную строку s, в которой все строчные буквы заменены на заглавные в соответствии с национальной кодировкой Windows

Function Concat(Sl [, S2, ..., SN]: String ): String;

Возвращает строку, представляющую собой сцепление строк-параметров S1, S2, ... , SN

Function Copy(St: String; Index, Count: Integer): String;

Копирует из строки St count символов, начиная с символа с номером Index

Procedure Delete(St: String;

Index, Count:" Integers-

Удаляет count символов из строки St, начиная с символа с номером index

Procedure Insert

(SubSt:String; St, Index:

Integer);

Вставляет подстроку SubSt в строку St, начиная с символа с номером Index

Function Length

St: String): Integer;

Возвращает текущую длину строки St

Function LowerCase

(const S:String): String;

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

Procedure OleStrToStrVar(Source: PWideChar; var Dest:String);

Копирует “широкую” (двухбайтную) строку в обычную строку Object Pascal

Function Pos(SubSt, St:String): Integer;

 

 

Отыскивает в строке St первое вхождение подстроки SubSt и возвращает номер позиции, с которой она начинается. Если подстрока не найдена, возвращается ноль

Procedure SetLength(St:String; NewLength: Integer);

 

 

Устанавливает новую (меньшую) длину NewLength строки St. если NewLength больше текущей длины строки, обращение к SetLength игнорируется

Function StringOfChar(Ch:Char; Count: Integer):String;

Создает строку, состоящую из Count раз повторенного символа ch

Function StringToOleStr(const Source: String):PWideChar;

Копирует обычную строку в двухбайтную

function StringToWideChar(const Source: String; Dest:PWideChar; DestSize: Integer) : PWideChar;

Преобразует обычную строку в строку с символами UNICODE

Function Uppercase(const S:String): String;

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

Подпрограммы преобразования строк в другие типы

Function StrToCurr

St: String): Currency;

Преобразует символы строки St в целое число типа Currency. Строка не должна содержать ведущих или ведомых пробелов

Function StrToDate

St: String): TDateTime;

Преобразует символы строки St в дату. Строка должна содержать два или три числа, разделенных правильным для Windows разделителем даты (в русифицированной версии таким разделителем является “.”). Первое число - правильный день, второе - правильный месяц. Если указано третье число, оно должно задавать год в формате XX или ХХХХ. Если символы года отсутствуют, дата дополняется текущим годом. Например: DateToStr(StrToDate('28.06')) даст строку '28.06.99'

Function StrToDateTime

St:String): TDateTime;

 

Преобразует символы строки St в дату и время. Строка должна содержать правильную дату (см. StrToDate) и правильное время (см. StrToTime), разделенные пробелом, например: StrToDateTime('28.06 18:23')

Function StrToFloat

St:String): Extended

 

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

Function StrToInt

St:String): Integer;

Преобразует символы строки St в целое число. Строка не должна содержать ведущих или ведомых пробелов

Function StrToIntDef

St:String; Default:Integer):

Integer;

 

Преобразует символы строки St в целое число.

Если строка не содержит правильного представления целого числа, возвращается значение Default

Function StrToIntRange

(St:String; Min, Max: Longint):Lomgint;

Преобразует символы строки St в целое число и возбуждает исключение ERangeError, если число выходит из заданного диапазона Min..Mах

Function StrToTime

(St:String): TDateTime;

 

 

 

Преобразует символы строки St во время.

Строка должна содержать два или три числа, разделенных правильным для Windows раздели телем времени (для русифицированной версии таким разделителем является “:”). Числа задают часы, минуты и, возможно, секунды. За послед ним числом через пробел могут следовать символы “am” или “рm”, указывающие на 12- часовой формат времени

Procedure Val

(St: String; var

X; Code: Integer);

 

 

Преобразует строку символов St во внутреннее представление целой или вещественной переменной х, которое определяется типом этой переменной. Параметр Code содержит ноль, если преобразование прошло успешно, и тогда в х помещается результат преобразования, в противном случае он содержит номер позиции в строке St, где обнаружен ошибочный символ, и в этом случае содержимое х не меняется. В строке St могут быть ведущие и/или ведомые пробелы. Если St содержит символьное представление вещественного числа, разделителем целой и дробной частей должна быть точка независимо от того, каким символом этот разделитель указан в Windows

Подпрограммы обратного преобразования

Function DateTimeToStr

(Value: TDateTime): String; Procedure DateTime-ToString(var St: String; Format: String;- Value: TData-Time);

Преобразует дату и время из параметра в строку символов Преобразует дату и время из параметра value в строку St в соответствии со спецификаторами параметра Format

Function DateToStr

(Value: TDateTime): String;

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

Function FloatToStr

(Value: Extended): String;

Преобразует вещественное значение value в строку символов.

Function IntToHex

(Value: Integer; Digits: Integer):Strings;

Преобразует целое число Value в строку символьного представления шестнадцатеричного формата: Digits - минимальное количество символов в строке

Function IntToStr

(Value: Integer) : String;

Преобразует целое значение Value в строку символов

Procedure Str

(X [:Width[:Decimals]];

var St:String);

 

 

 

Преобразует число х любого вещественного илицелого типов в строку символов St; параметры width и Decimals, если они присутствуют, задают формат преобразования: width определяет общую ширину поля, выделенного под соответ ствующее символьное представление вещественного или целого числа х, a Decimals - количество символов в дробной части (этот параметр имеет смысл только в том случае, когда х -вещественное число)

Function TimeToStr

(Value: TDateTime): String;

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

 

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

Примечание

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

5. Константы

В качестве констант в Object Pascal могут использоваться целые, вещественные и шестнадцатеричные числа, логические константы, символы, строки символов, конструкторы множеств и признак неопределенного указателя NIL.

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

<идентификатор> = <значение>;

где <идентификатор> - идентификатор константы;

<значение> - значение константы.

Например,

const

MaxVal = 10;

Name = 'Noname';

pi = 1415926;

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

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

Например,

const

Ln10 = 2.302585092994;

Ln10Rev = 1 / Ln10;

6. Переменные

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

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

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

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

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

В общем, виде инструкция объявления переменной выглядит так:

<Имя переменной> : <тип данных>;

Пример:

var

а : Real;

b : Real;

i : Integer;

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

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

var

а,b,с : Real;

Переменные должны быть объявлены в разделе объявления переменных программы.

Области видимости переменных, глобальные и локальные переменные рассмотрены в разделе 15. 

7. Операторы в Object Pascal

 Операторы располагаются в разделе выполнения (begin ... end) и описывают действия, выполняемые во время работы программы.

Операторы бывают простыми и структурированными.

Простой оператор – это такой оператор, который не содержит в себе других операторов.

Структурированный оператор соответственно может содержать в себе другие операторы.

К простым операторам относятся:

  1.  оператор присваивания: := ;
  2.  вызов процедуры;
  3.  безусловный переход на метку: goto <метка>.

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

В общем виде, оператор присваивания выглядит так:

<имя переменной> := <выражение>;

где:

  1.  <имя переменной> – переменная, значение которой изменяется в результате выполнения оператора присваивания;
  2.  := – символ оператора присваивания.
  3.  <выражение> – выражение, значение которого присваивается переменной, имя которой указано слева от символа оператора присваивания.

Например,

 

Summa:=value*pos;

Found:=False;

Оператор присваивания выполняется следующим образом:

  1.  Сначала вычисляется значение выражения, которое находится справа от символа оператора присваивания.
  2.  Затем вычисленное значение записывается в переменную, имя которой стоит слева от символа оператора присваивания.

Например, в результате выполнения следующих операторов:

i:=0; – значение переменной i становится равным нулю;

a:=3; а:=a+2; – значением переменной а будет 5.

Структурированными операторами являются:

  1.  составной;
  2.  условный;
  3.  оператор выбора;
  4.  циклический;
  5.  оператор присоединения.

Составным оператором является группа операторов, расположенных в так называемых операторных скобках:

begin

<операторы>

end;

Операторные скобки begin ... end используются для объединения группы операторов в один.

Весь раздел выполнения является составным оператором.

Условный оператор и оператор выбора рассмотрены в разделе 10. Операторы цикла рассмотрены в разделе 11. Оператор присоединения рассмотрен в разделе 14.

8. Выражения и операции в Object Pascal

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

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

8.1. Тип выражения

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

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

При вычислении значения выражения в первую очередь выполняются операции с более высоким приоритетом. Порядок выполнения нескольких операций равного приоритета устанавливается компилятором из условия оптимизации кода программы и не обязательно слева направо. При исчислении логических выражений операции равного приоритета всегда вычисляются слева направо, причем будут вычисляться все или только достаточные операции в зависимости от установленного в среде Delphi переключателя Project | Options | Compiler | Complete Boolean eval . При установленном переключателе вычисляются все операции отношения, при неустановленном - только те, что необходимы для однозначного определения результата исчисления.

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

(r1+r2+r3)/(r1*r2*r3)

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

8.2. Операции

В Object Pascal определены следующие операции:

  1.  унарные not, @ ;
  2.  мультипликативные *, /, div, mod, and, shi, shr; 
  3.  аддитивные +, -, or, xor;
  4.  отношения =, <>, <, >, <=, >=, in.

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

Таблица 7. Правила использования операций с операндами различного типа

Операция

Действие

Тип операндов

Тип результата

not

Отрицание

Логический

Логический

not

To же

Любой целый

Тип операнда

@

Адрес

Любой

Указатель

*

Умножение

Любой целый

Наименьший целый

*

Тоже

Любой вещественный

Extended

*

Пересечение множеств

Множественный

Множественный

/

Деление

Любой вещественный

Extended

div

Целочисленное деление

Любой целый

Наименьший целый

mod

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

То же

То же

and

Логическое И

Логический

Логический

and

То же

Любой целый

Наименьший целый

shl

Левый сдвиг

То же

То же

shr

Правый сдвиг

То же

То же

+

Сложение

То же

То же

+

Тоже

Любой вещественный

Extended

+

Объединение множеств

Множественный

Множественный

+

Сцепление строк

Строковый

Строковый

-

Вычитание

Любой целый

Наименьший целый

-

Тоже

Любой вещественный

Extended

or

Логическое или

Логический

Логический

or

Тоже

Любой целый

Наименьший целый

=

Равно

Любой простой или строковый

Логический

< >

Не равно

Тоже

Тоже

<

Меньше

Логический

Логический

<=

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

Тоже

Тоже

>

Больше

То же

Тоже

>=

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

Тоже

Тоже

Унарная операция @ применяется к операнду любого типа и возвращает результат типа pointer (указатель).

В Object Pascal определены следующие логические операции:

  1.  not - логическое НЕ;
  2.  and - логическое И;
  3.  or - логическое ИЛИ;
  4.  xor - исключительное ИЛИ.

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

–Таблица 8. Логичеcкие операции над данными целого типа (поразрядно)

Операнд 1

Операнд 2

not

and

or

xor

1

-

0

-

-

-

0

-

1

-

-

-

0

0

-

0

0

0

0

1

-

0

1

1

1

0

-

0

1

1

1

1

-

1

1

0

 

К логическим в Object Pascal обычно относятся и две сдвиговые операции над целыми числами:

i shl j – сдвиг содержимого i на j разрядов влево; освободившиеся младшие разряды заполняются нулями;

i shr j – сдвиг содержимого i на j разрядов вправо; освободившиеся старшие разряды заполняются нулями.

В этих операциях i и j – выражения любого целого типа.

Логические операции над логическими данными дают результат логического типа по правилам, указанным в табл. 9.

Операция отношения in применяется к двум операндам. Первым (левым) операндом должно быть выражение любого порядкового типа, вторым – множество, состоящее из элементов того же типа, или идентификатор множественного типа. Результат операции будет True, если левый операнд принадлежит множеству, и False в противном случае.

–Таблица 9. Логические операции над данными логического типа

Операнд 1

Операнд 2

not

and

or

xor

True

-

False

-

-

-

False

-

True

-

-

-

False

False

-

False

False

False

False

True

-

False

True

True

True

False

-

False

True

True

True

True

-

True

True

False

9. Консольный ввод / вывод

9.1. Ввод: read, readln

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

read (Переменная1, Переменная2, ... ПеременнаяN);

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

Например,

read(a);

read(NewValue, MyKol);

При выполнении оператора read происходит следующее:

  1.  Программа приостанавливает свою работу и ждет, пока на клавиатуре будут набраны нужные данные и нажата клавиша <Enter>.
  2.  После нажатия клавиши <Enter> введенное значение присваивается переменной, имя которой указано в операторе.

Например, в результате выполнения оператора

read( temperature ) ;

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

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

Например, если тип переменных а, b и с – real, то выполнение оператора read(a,b,c); и ввод с клавиатуры строки: 4.5 23 0.17

будут иметь тот же результат, что и следующие операторы:

а := 4.5; b := 20; с := 0.17.

Если в строке набрано больше чисел, чем задано переменных в функции read, то оставшаяся часть строки будет обработана следующим оператором read.

Например, в результате выполнения:

read(a, b); read(c);

и ввода с клавиатуры строки

10 25 18

переменные получат следующие значения: a:=10, b:=25. Оператор read(c); присвоит переменной c значение 18.

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

В результате выполнения примера аналогичного предыдущему:

readln(А,В); read(С);

и ввода с клавиатуры строки

10 25 18

переменные получат следующие значения: a:=10, b:=25. После чего программа будет ожидать ввода нового числа, чтобы присвоить его переменной c.

Перед каждым оператором read или readln желательно располагать подсказку пользователю: какие данные ожидает от него программа. Это можно осуществить с помощью вывода соответствующего текста на экран функциями write или writeln, рассмотренными в следующем разделе.

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

9.2. Вывод: write, writeln

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

Например:

write( Res );

write( 'Результат вычислений' );

write('Площадь поверхности x=', x);

После имени переменной через двоеточие можно поместить описание (формат) поля вывода значения переменной.

Для переменной типа Integer формат – это целое число, которое задает ширину поля вывода (количество позиций на экране).

Например, оператор write(x:5); показывает, что для вывода значения переменной x используется 5 позиций.

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

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

Например, пусть переменные x1 и х2 типа real имеют значения 125 и -0.3401, тогда в результате выполнения оператора

write('xl=',x1:5:2,' х2=',х2:12);

на экран будет выведено:

x1=125 х2=-40100Е-01

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

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

Например, в результате выполнения следующих операторов

х:=-2.73;

write('Значение перем');

write('енной:');

write('х=');

write(x:8:5);

на экран будет выведено:

Значение переменной: х=-2.73000

Функция writeln отличается от write только тем, что после вывода сообщения или значений переменных курсор переводится в начало следующей строки. Например, если значением переменной x1 является число -561, а значением переменной х2 – число 10.345, то результатом выполнения операторов

writeln('Значения переменных:');

writeln('x1=',x:7:3);

writeln('х2=',х:7:3);

на экран будет выведено:

Значения корней уравнения:

xl=-5610

х2= 10.345

10. Управляющие конструкции языка

10.1. Оператор перехода goto

На Object Pascal вполне можно создать программу любой сложности без использования операторов перехода. Поэтому наличие в языке операторов перехода кажется излишним. Более того, современная технология структурного программирования основана на принципе «программировать без GOTO», так как считается, что злоупотребление операторами перехода затрудняет понимание программы, делает ее запутанной и сложной в отладке.

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

Оператор перехода имеет вид:

goto <метка>;

где goto - зарезервированное слово (перейти на [метку]);

<метка> - имя метки.

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

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

Действие оператора goto состоит в передаче управления соответствующему меченному оператору.

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

  1.  метка, на которую ссылается оператор goto, должна быть описана в разделе описаний, и она обязательно должна встретиться где-нибудь в теле программы;
  2.  метки, описанные в подпрограмме, локализуются в ней, поэтому передача управления извне подпрограммы на метку внутри нее невозможна.

–Пример:

label

MyBlock;

var

 a:integer;

begin

a:=3;

Goto MyBlock;

a:=a+7;

 

MyBlock:

write(’значение a=’,a:5);

end.

На экран будет выведено

значение a=3

10.2. Условный оператор if

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

В общем виде оператор if записывается так:

if условие then

{оператор, который надо выполнить, если условие истинно

при необходимости выполнить ряд операторов используют составной оператор begin … end }

else

{оператор, который надо выполнить, если условие ложно

при необходимости выполнить ряд операторов используют составной оператор begin … end }

Выполняется оператор if следующим образом:

  1.  Вычисляется значение условия (условие – выражение логического типа, значение которого может быть равно True или False).
  2.  Если условие истинно (значение выражения условие равно True), то выполняется оператор, следующий за словом then. На этом выполнение операции if заканчивается, то есть оператор, следующий за else, не будет выполнен.

Если условие ложно (значение выражения условие равно False), то выполняется оператор, следующий за словом else.

На рис. 2 представлен алгоритм, соответствующий оператору if-tnen-else.

условие

выполняется?

оператор при истинном условии

оператор при ложном условии

да

нет

Рис. 2. Алгоритм условного оператора

Пример:

var

 a:integer;

 s:string;

begin

a:=2; {задание начального значения a}

if (a<5) then

begin {выполняется, если a меньше 5}

a:=a*a*a;

s:=’a^3=’;

 end

else

 begin {выполняется, если a больше или равно 5}

a:=a*a;

s:=’a^2=’;

 end

 

writeln(s, a:4); {вывести на экран}

end.

На экран будет выведено: a^2=8

Если же в начале программы в строке {задание начального значения a} указать a:=6;, то на экран будет выведено

a^3=216

 

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

if условие then

{оператор, который надо выполнить, если условие истинно

при необходимости выполнить ряд операторов используют составной оператор begin … end }

На рис. 3 представлен алгоритм, соответствующий оператору if-then.

Пример:

var

 a:integer;

begin

read(a); {ввести значение a}

if (a<0) then {если a меньше нуля,}

a:=a*a; {то возвести a в квадрат}

writeln(a:4); {вывести на экран значение a}

end.

условие

выполняется?

оператор при истинном условии

да

нет

Рис.  Алгоритм неполного условного оператора

Логические выражения чаще всего получаются при сравнении переменных с помощью операций отношения =, <>, >, >=, <, <=. Сложные логические выражения составляются с использованием логических операций and (логическое И), or (логическое ИЛИ) и not (логическое НЕ). Например:

if > b) and (b <> 0) then ...

Примечание

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

Например, такая запись предыдущего оператора будет неверной:

if a>b and b <> 0 then ...// Ошибка так как фактически (с учетом приоритета операции) компилятор будет рассматривать эту строку как эту:

if a>(b and b)<>0 then...

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

10. Оператор выбора case

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

Специально для целей эффективного осуществления множественного выбора в языке Object Pascal предусмотрен оператор выбора case.

–В общем виде оператор case записывается следующим образом:

case <ключ выбора> of 

<список выбора 1>: оператор 1;

<список выбора 2>: оператор 2;

...

<список выбора N>: оператор N;

else

оператор для else;

end;

где <ключ выбора> – выражение, значение которого определяет дальнейший ход выполнения программы;

<список выбора X> – список значений <ключа выбора>, если <ключ выбора> будет равен одному из значений <списка выбора X>, то будет выполнен оператор X.

Если значения в списке выбора представляют собой диапазон чисел, то вместо списка можно указать первую и последнюю константу диапазона, разделив их двумя точками. Например, список 1, 2, 3, 4, 5, 6 может быть заменен диапазоном 1..6.

Выполняется оператор case следующим образом:

  1.  Сначала вычисляется значение выражения <ключа выбора>.
  2.  Значение выражения <ключа выбора> последовательно сравнивается со значениями из списков выбора.
  3.  Если значение выражения <ключа выбора> совпадает хотя бы с одним значением из списка выбора, то выполняется соответствующий этому списку оператор (может быть составной). На этом выполнение оператора саsе завершается.
  4.  Если значение выражения <ключа выбора> не совпадает ни с одной константой из всех списков, то выполняется оператор для else.

На рис. 4 приведен алгоритм, реализуемый оператором case.

Оператор выбора case также как и условный оператор if может быть неполным. Т.е. синтаксис оператора case позволяет не использовать else и соответствующий оператор для else. В этом случае, если значение выражения <ключа выбора> не совпадает ни с одним значением из всех списков, то выполняется следующий за case ... end; оператор программы.

выражение

оператор

оператор

оператор

список выбора

список выбора

список выбора

список выбора

Рис. 4. Алгоритм оператора выбора

Рассмотрим пример использования оператора выбора case для вывода на экран категории дня недели.

Вариант 1:

case nDayOfWeek of

1,2,3,4,5: sDay := ’Рабочий день. ’ ;

6: sDay := ’Cyббoтa! ’;

7: sDay := ’Воскресенье! ’;

end;

Вариант 2:

case nDayOfWeek of

1..5: sDay := ’Рабочий день.’ ;

6: sDay := ’Cyббoтa! ’;

7: sDay := ’Воскресенье! ’;

end;

Вариант 3:

case nDayOfWeek of

6: sDay := ’Cyббoтa!’;

7: sDay := ’Воскресенье!’;

 else sDay := ’Рабочий день.’ ;

end;

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

11. Циклические конструкции языка

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

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

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

–В Object Pascal цикл может быть реализован при помощи любого из следующих операторов:

  1.  for – цикл с параметром или цикл со счетчиком;
  2.  while – цикл с предварительным условием или цикл предусловием;
  3.  repeat – цикл с последующим условием или цикл с постусловием.

11.1. Оператор цикла с параметром for

Оператор цикла с параметром или цикла со счетчиком for имеет такую структуру:

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

где <параметр_цикла> – переменная типа Integer (точнее, любого по-, рядкового типа);

<нач_знач> – начальное значение - выражение того же типа;

<кон_знач> – конечное значение - выражение того же типа;

<оператор> – произвольный оператор Object Pascal, в том числе и составной. Исполняемый в цикле оператор часто называют телом цикла.

При выполнении оператора for вначале вычисляется выражение <нач_знач> и осуществляется присваивание <параметр цикла>:= <нач_знач>.

После этого циклически повторяется:

  1.  проверка условия <параметр_цикла> <= <кон_знач>; если условие не выполнено, оператор for завершает свою работу;
  2.  выполнение оператора <оператор>;
  3.  наращивание переменной <параметр_цикла> на единицу;
  4.  переход к шагу 1 .

Следует заметить, что условие, управляющее работой оператора for, проверяется перед выполнением оператора <оператор>. Это значит, что если условие не выполняется в самом начале работы оператора for, исполняемый оператор не будет выполнен ни разу.

Шаг наращивания параметра цикла строго постоянен и равен +1 (это цикл с возрастающим параметром или счетчиком).

счетчик:=счетчик + 1

оператор

тела цикла

да

нет

счетчик:=начальное_значение

счетчик <=

кон_знач

счетчик:=счетчик - 1

оператор

тела цикла

да

нет

счетчик:=начальное_значение

счетчик >=

кон_знач

а) цикл с возрастающим параметром б) цикл с убывающим параметром

Рис. 5. Алгоритм оператора цикла for

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

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

Замена зарезервированного слова to на downto означает, что шаг наращивания параметра цикла равен (-1), а управляющее условие приобретает вид <параметр_цикла> >= <кон_знач>.

Алгоритм, соответствующий оператору цикла с параметром for, представлен на рис. 5.

Переменную-счетчик (<параметр_цикла>) можно использовать внутри тела цикла (но ни в коем случае не изменять).

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

Как видно, листинги программ отличаются только организацией циклов: в первом случае от 1 до n с шагом +1, во втором варианте от n до 1 с шагом -1.

Предложенные программы можно оптимизировать, уменьшив количество повторений циклов на 1 раз: циклы сделать соответственно от 2 до n и от n до 2.

Пример программы расчета факториала вводимого числа с использованием цикла с возрастающим параметром:

var

n, f, i :integer;

{n – число, факториал которого следует найти; f- найденное значение факториала , i – счетчик цикла}

begin

write(’Введите n (больше 1):’);

readln(n);

if (n>1) then

begin {введено правильное число}

 f:=1;

 for i:=1 to n do {цикл расчета факториала}

f:=f*i;

writeln(’Факториал числа ’,n,’ равен:’,f);

end

else

writeln(’Введено неверное n! Факториал не рассчитан!’);

end.

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

var

n, f, i :integer;

{n – число, факториал которого следует найти; f- найденное значение факториала , i – счетчик цикла}

begin

write(’Введите n (больше 1):’);

readln(n);

if (n>1) then

begin {введено правильное число}

 f:=1;

 for i:=n downto 1 do {цикл расчета факториала}

f:=f*i;

writeln(’Факториал числа ’,n,’ равен:’,f);

end

else

writeln(’Введено неверное n! Факториал не рассчитан!’);

end.

 

11.2. Оператор цикла с предварительным условием while

Оператор цикла с предварительным условием или с предусловием while имеет такую структуру:

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

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

<оператор> – произвольный оператор Object Pascal, в том числе и составной. Исполняемый в цикле оператор также как в цикле for называют телом цикла.

Оператор while выполняется следующим образом:

  1.  Вычисляется значение выражения <условие>.
  2.  Если значение выражения <условие> равно False (условие не выполняется), то на этом выполнение цикла while завершается.
  3.  Если значение выражения <условие> равно True (условие выполняется), то выполняется оператор тела цикла <оператор>.
  4.  Переход на шаг 1.

Цикл продолжает выполняться до тех пор, пока условие не станет ложным (False).

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

Типичными примерами использования цикла while являются поиск в массиве или в файле.

Алгоритм, соответствующий оператору while, представлен на рис. 6.

оператор

тела цикла

да

нет

условие = True?

кон_знач

Рис. 6. Алгоритм оператора цикла while

Для того чтобы <оператор> в тела цикла выполнился хотя бы один раз, необходимо, чтобы перед выполнением оператора while значение выражения <условие> было истинно (True).

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

Пример программы, которая запрашивает число и выводит его квадрат, до тех пор, пока не будет введено число 0:

–var

n:integer;

{n – число, квадрат которого рассчитывает программа}

begin

n:=1; {инициализация n, чтобы попасть в цикл while}

 while (n<>0) do

begin

 write(’Введите n (выход - 0):’);

readln(n);

writeln(’Квадрат числа’,n,’ равен:’,n*n);

end;

end.

11. Оператор цикла с последующим условием repeat..until

Оператор цикла с последующим условием или с постусловием repeat имеет такую структуру:

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

где <оператор> – произвольный оператор Object Pascal, в том числе и составной. Исполняемый в цикле оператор также как и в других циклах называют телом цикла.

<условие> – условие выхода из цикла – выражение логического типа, при значении которого равном False выполняется <оператор>.

Оператор repeat выполняется следующим образом:

  1.  Выполняется находящийся между repeat и until <оператор> тела цикла.
  2.  Вычисляется значение выражения <условие>.
  3.  Если значение выражения <условие> равно True (условие выполняется), то на этом выполнение цикла repeat..until завершается.
  4.  Если значение выражения <условие> равно False (условие не выполняется), то выполняется переход на шаг 1.

Оператор тела цикла, находящийся между repeat и until, выполняется до тех пор, пока условие ложно.

Таким образом, в цикле repeat..until <оператор> тела цикла выполняется хотя бы один раз.

Как и в цикле while, для правильного выхода из цикла repeat..until необходимо, чтобы операторы в теле цикла влияли на значение выражения <условие> (изменяли значения переменных, входящих в выражение <условие>).

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

Алгоритм, соответствующий оператору цикла repeat..until, представлен на рис. 7.

оператор

тела цикла

да

нет

условие = True?

кон_знач

Рис. 7. Алгоритм оператора цикла repeat

Рассмотрим такой же пример, как и для цикла while – программу, которая запрашивает число и выводит его квадрат, до тех пор, пока не будет введено число 0:

var

n:integer;

{n – число, квадрат которого рассчитывает программа}

begin

 repeat

 write(’Введите n (выход - 0):’);

readln(n);

writeln(’Квадрат числа’,n,’ равен:’,n*n);

until (n=0);

end.

11.4. Процедуры управления циклом

Для гибкого управления циклическими операторами for, while и repeat в состав Object Pascal включены две процедуры без параметров:

  1.  break - реализует немедленный выход из цикла; действие процедуры заключается в передаче управления оператору, стоящему сразу за концом циклического оператора;
  2.  continue - обеспечивает досрочное завершение очередного прохода цикла; эквивалент передачи управления в самый конец циклического оператора.

12. Массивы

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

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

<имя_массива>: array [нижний_индекс. .верхний_индекс] of <тип>;

где <имя_массива> – имя объявляемого массива;

нижний_индекс и верхний_индекс – целые константы, определяющие диапазон изменения индекса элементов массива и, неявно, количество элементов (размер) массива. В качестве индексных типов в Object Pascal можно использовать любые порядковые типы, имеющие мощность не более 2 Гбайт (т.е. кроме LongWord И Int64) .

<тип> – тип элементов массива.

Примеры объявления массивов:

var

tmp : array[1..31] of real;

коef : array[0. .2] of integer;

name : array[1..30] of string[25];

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

const

NG = 18; // число студентов

SN = 25; // предельная длина имени студента

 var

group: array[1..NG] of string[SN];

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

group [3] := 'Иванов';

d := koef[l]*koef[1]-4*koef[i]*koef[j];

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

Инструкция объявления массива с одновременной его инициализацией в общем виде выглядит так:

<имя_массива>: array [нижний_индекс. .верхний_индекс] of <тип> =( список );

где список – разделенные запятыми значения элементов массива.

Например:

a:array [1..10] of integer = (1,2,3,4,6,8,2,0,3,7);

group:array [1..3] of String=('Иванюк','Петрук','Сидорук');

Следует заметить, что количество элементов списка инициализации должно соответствовать размерности массива. Если это будет не так, то компилятор выведет сообщения об ошибке: Number of elements differs from declaration (количество элементов не соответствует указанному в объявлении).

12.1. Многомерные массивы

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

Например, таблица из пяти строк и двенадцати колонок может быть представлена как совокупность одномерных массивов:

Stroka1: array [1..12] of integer;

Stroka2: array [1..12] of integer;

Stroka3: array [1..12] of integer;

Stroka4: array [1..12] of integer;

Stroka5: array [1..12] of integer;

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

Kolonka1: array [1..5] of integer;

Kolonka2: array [1..5] of integer;

Kolonka3: array [1..5] of integer;

...

Kolonka10: array [1..5] of integer;

Kolonka11: array [1..5] of integer;

Kolonka12: array [1..5] of integer;

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

В общем виде инструкция объявления двумерного массива выглядит так:

<имя_массива>: array [нижний_индекс1..верхний_индекс1,

нижний_индекс2..верхний_индекс2] of <тип>;

Рассматриваемая нами таблица может представлена двумерным массивом:

OurTab: array [1..5, 1..12] of integer;

Количество элементов двумерного массива можно вычислить по формуле:

(верхн_индекс1 – нижн_индекс1 + 1)*(верхн_индекс2 – нижн_индекс2 + 1)

Таким образом, массив OurTab состоит из 60 элементов типа integer.

Для того чтобы использовать элемент массива, нужно указать имя массива и индексы элемента. Например, OurTab[2,3] или OurTab[2][3].

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

var

a : array [1..5, 1..12] of integer;

i,j,s:integer;

begin

{блок наполнения массива значениями опущен}

s:=0;

 for i:=1 to 5 do

 for j:=1 to 12 do

s:=s+a[i,j];

writeln(s);

readln; {ожидание нажатия клавиши <Enter>}

end.

Рассматриваемая нами таблица также может быть описана как одномерный массив одномерных массивов:

OurTab: array [1..5] of array [1..12] of integer;

В этом случае обращение к элементам массива осуществляется также. Например, OurTab[2,3] или OurTab[2][3].

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

<имя_массива>: array [нижний_индекс1..верхний_индекс1,

нижний_индекс2..верхний_индекс2, ...

... нижний_индексN..верхний_индексN] of <тип>;

12.2. Динамические массивы

В Object Pascal начиная с версии Delphi 4 введены так называемые динамические массивы. При объявлении таких массивов в программе границы индексов не указываются:

<имя_массива>: array of <тип>;

Например, объявление одномерного, двумерного и трехмерного динамических массивов:

var

A: array of Integer;

В: array of array of Char;

C: array of array of array of Real;

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

Например, в ходе выполнения такого оператора:

SetLength(А,3);

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

Фактически идентификатор динамического массива ссылается на указатель, содержащий адрес первого байта памяти, выделенной для размещения массива. Поэтому для освобождения этой памяти достаточно присвоить идентификатору значение nil (другим способом является использование процедуры Finalize).

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

В многомерных массивах сначала устанавливается длина его первого измерения, затем второго, третьего и т. д. Например:

var

A: array of array of Integer;

{Двумерный динамический массив}

begin

SetLength(A,3);{Устанавливаем длину первого измерения}

{далее задаем длину каждого измерения}

SetLength(A[0],3);

SetLength(A[l],3);

SetLength(A[2],3);

end.

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

SetLength(A,3) ; {далее задаем длину каждого измерения}

SetLength(A[0],3) ;

SetLength(A[l],4) ;

SetLength(A[2],5) ;

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

var

A: array of array of array of Real;

i, j: Integer;

begin

SetLength(A,3) ;

for i := 0 to 2 do

begin

SetLength(A[i],3);

for j := 0 to 2 do SetLength{A[i,j],3);

end;

end.

12. Операции с массивами

Типичными операциями при работе с массивами являются:

  1.  ввод массива;
  2.  вывод массива;
  3.  поиск максимального или минимального элемента массива;
  4.  поиск заданного элемента массива;
  5.  сортировка массива.

Как правило, работа с массивами осуществляется с помощью операторов цикла.

В Object Pascal можно одним оператором присваивания передать все элементы одного массива другому массиву того же типа (только для обычных, не динамических массивов!), например:

var

a,b : array [1..5] of Single;

begin

а := b;

end.

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

var

a: array [1..5] of Single;

b: array [1..5] of Single;

создаст разные типы массивов, поэтому оператор а := b; в этом случае вызовет сообщение об ошибке.

Чтобы избежать таких проблем, можно создать свой тип массивов определенного вида, а затем уже объявлять массивы этого типа, например:

–type

 mySingleArray = array [1..5] of Single;

var

a: mySingleArray; 

b: mySingleArray;

Над массивами не определены операции отношения. Сравнить два массива можно только поэлементно.

Основными используемыми функциями при работе с массивами являются:

Low(x) – возвращает нижний (минимальный) индекс массива x;

High(x) – возвращает верхний (максимальный) индекс массива x;

Length(x) – возвращает количество элементов массива x.

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

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

DayOfWeek : array[0..6] of string,

то во время компиляции программы оператор

day [7] := 'Воскресенье';

будет помечен как ошибочный.

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

OurTab: array [1..N] of integer;

то оператор

for i:=0 to N do tab1[i] := 5;

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

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

1 Множества

Множества - это наборы однотипных логически связанных друг с другом объектов. Характер связей между объектами лишь подразумевается программистом и никак не контролируется Object Pascal. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256 (множество, не содержащее элементов, называется пустым). Именно непостоянством количества своих элементов множества отличаются от массивов и записей.

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

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

<имя типа> = set of <базовый тип>;

где <имя типа> - имя создаваемого типа;

<базовый тип> - базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, кроме Word, Integer, Longint, Int64.

Пример определения и задания множеств:

type

digitChar = set of '0'..'9';

digit = set of 0. .9;

var

sl,s2,s3 : digitChar;

s4,s5,s6 : digit;

begin

s1 = ['1', '2', '3'];

s2 = ['3', '2', '1'];

s3 = ['2', '3'];

s4 = [0..3, 6];

s5 = [4, 5];

s6 = [.9];

end.

В этом примере множества s1 и s2 эквивалентны, а множество s3 включено в s2 , но не эквивалентно ему.

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

Спецификациями элементов могут быть константы или выражения базового типа, а также тип-диапазон того же базового типа.

Операции, определенные над множествами представлены в табл. 10.

–Таблица 10. Операции над множествами

Знак

Операция

Результат

*

пересечение множеств

результат содержит элементы, общие для обоих множеств

+

объединение множеств

результат содержит элементы первого множества, дополненные недостающими элементами из второго множества

-

разность множеств

результат содержит элементы из первого множества, которые не принадлежат второму

=

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

возвращает True, если оба множества эквивалентны

< >

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

возвращает True, если оба множества неэквивалентны

<=

проверка вхождения

возвращает True, если первое множество включено во второе

>=

проверка вхождения

возвращает True, если второе множество включено в первое

in

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

в этой бинарной операции первый элемент - выражение, а второй - множество одного и того же типа; возвращает True, если выражение имеет значение, принадлежащее множеству

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

  1.  include(s, i) – включает новый элемент i в множество s.
  2.  exclude(s, i) – исключает элемент i из множества s.

14. Записи

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

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

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

В общем виде объявление типа «запись» выглядит так:

<имя_типа_записи> = record

<имя_поля_1> : тип_1;

<имя_поля_2> : тип_2;

<имя_поля_N> : тип_N;

end;

где <имя_типа_записи> - имя создаваемого типа «запись»;

<имя_поля_x> и тип_x – имя и тип компонента (поля) записи.

Примеры объявлений:

type

TStudent = record

SurName: string[20];

Name: string[20];

Day: integer;

Month: integer;

Year: integer;

Address: string[50];

end;

После объявления типа записи можно объявить переменную-запись, например:

var

student : TStudent;

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

Например, команда

writeln ('Имя: ', student.Name, #10#13 + 'Адрес: ',

student.Address);

выводит на экран содержимое полей Name (имя) и в следующей строке Address (адрес) переменной-записи student.

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

var

student:record

SurName: string[20];

Name: string[20];

Day: integer;

Month: integer;

Year: integer;

Address: string[50];

end;

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

with <имя_записи> do <оператор>;

где <имя_записи> – имя переменной-записи;

<оператор> – произвольный оператор (в том числе составной), в котором при обращении к полям записи <имя_записи>, имя записи можно не указывать.

Например, рассмотренный выше пример вывода имени и адреса студента может быть представлен так:

with student do 

writeln ('Имя: ', Name, #10#13 + 'Адрес: ', Address);

15. Подпрограммы (процедуры и функции)

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

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

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

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

Как правило, подпрограмма имеет параметры. Различают формальные и фактические параметры.

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

Параметры используются:

  1.  для передачи данных в подпрограмму;
  2.  для получения из результата подпрограммы.

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

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

15.1. Описание процедур и функций

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

Описание процедуры в общем виде выглядит так:

Procedure <имя_процедуры> (<список форм. параметров>);

{раздел описаний процедуры}

begin {раздел операторов - тело процедуры}

end;

Описание функции в общем виде выглядит так:

Function <имя_функции> (<список форм. параметров>):<тип>;

{раздел описаний функции}

begin {раздел операторов - тело процедуры}

<имя_функции> := <выражение_результат>;

end;

где <имя_функции> – имя описываемой функции;

<имя_ процедуры> – имя описываемой процедуры;

<список форм. параметров> – список формальных параметров процедуры или функции;

<тип> – тип значения, которое функция возвращает в вызвавшую ее программу.

Сразу за заголовком подпрограммы может следовать одна из стандартных директив assembler, external, far, forward, inline, interrupt, near. Эти директивы уточняют действия компилятора и распространяются только на данную подпрограмму.

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

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

Список формальных параметров <список форм. параметров> при описании процедуры или функции необязателен и может отсутствовать. Если же он есть, то в нем должны быть перечислены имена формальных параметров и их типы, например:

(пар1:тип1, ..., парK:типK; var парL:типL, ..., парM:типM;

 const парP:типP, ..., парN:типN;)

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

Любой из формальных параметров подпрограммы может быть либо параметром-значением, либо параметром-переменной (после слова var), либо, параметром-константой (после слова const).

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

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

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

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

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

Умалчиваемые параметры. В Object Pascal (начиная с версии Delphi 4) можно использовать умалчиваемые параметры. В списке формальных параметров <список форм. параметров> умалчиваемые параметры размещаются в конце в форме:

<имя_параметра>:<тип> = <значение_по_умолчанию>

где <имя_параметра>:<тип> – имя умалчиваемого параметра и его тип;

<значение_по_умолчанию> – значение параметра, которое он получает, если этот параметр не указывается (умалчивается) при вызове подпрограммы.

Например, для процедуры

Procedure StudentEx (a:integer; s:string = ’Ivanov’);

два следующих ее вызова из программы идентичны:

StudentEx(7);

StudentEx(7, ’Ivanov’);

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

Например, для процедуры

Procedure StudentEx (a:integer; b: real=2.3;

s:string = ’Ivanov’; k:integer=1);

следующие ее вызовы из программы являются правильными:

StudentEx(7, 6, ’Petrov’);

StudentEx(7, 2.3, ’Ivanov’, 2);

а следующие вызовы ошибочны:

StudentEx(7, , ’Petrov’);

StudentEx(7, 2.3, , 2);

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

Procedure StudentEx (a: array [1..10] of real);

Такое объявление следует осуществлять следующим образом:

type

аТуре = array [1..10] of real;

Procedure StudentEx (var a: аТуре);

Для передачи в подпрограмму одномерных массивов переменной длины в Object Pascal поддерживает так называемые открытые массивы. 

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

Procedure StudentEx (aOpen : array of Integer);

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

15. Локализация имен

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

На рис. 8. схематично представлена структура программы с вложенными подпрограммами.

Далее представлен пример описания процедур для программы с такой структурой.

Программа

Подпрограмма A

Подпрограмма B

Подпрограмма B1

Подпрограмма B2

Подпрограмма B21

Подпрограмма B22

Рис. 8. Структура программы с вложенностью подпрограмм

Program StructureExample;

Procedure A;

begin

end; {A}

Procedure B;

Procedure Bl;

begin

end; {Bl}

 Procedure B2;

Procedure B21;

begin

end; {B2l}

Procedure B22

begin

end; {B22}

 begin

end; {B2}

 begin

 end; {B}

begin

 end. {Program}

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

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

Например, в рассмотренном выше примере из основной программы можно обратиться к процедурам A и B, но нельзя вызвать ни одну из вложенных в подпрограмму B процедур: B1, B2, B21, B22.

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

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

Так, в рассмотренном примере, из подпрограммы B21 можно вызвать подпрограмму A, использовать имена, объявленные в основной программе, в подпрограммах B и B2, и даже обратиться к ним.

Более того, любая подпрограмма может вызывать саму себя – такой

способ вызова называется рекурсией. Рекурсия рассмотрена в разделе 15.6.

При взаимодействии подпрограмм одного уровня иерархии вступает в силу основное правило Object Pascal: любая подпрограмма перед ее использованием должна быть описана.

Таким образом, в рассмотренном примере, из подпрограммы B можно вызвать подпрограмму A, но из A вызвать B невозможно. Для такой возможности необходимо использовать опережающее описание подпрограмм (см. раздел 15.7.)

Объекты (переменные, константы, типы, подпрограммы), описанные

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

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

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

var

i : Integer;

Procedure StudentEx;

var

i : Integer; {описание локальной переменной}

begin

i:=3;

writeln(i);

end { StudentEx };

begin

 i:=1;

StudentEx;

end;

На экран будет выведено значение  Глобальной переменной i в теле программы присвоено значение 1, однако, в процедуре StudentEx объявлена локальная переменная с таким же именем i. Поэтому в процедуре StudentEx используется именно локальная переменная i со значением

Следует заметить, что если из процедуры StudentEx убрать описание переменной i:

var

i : Integer; {описание локальной переменной}

то на экран будет выведено значение 1.

15.4. Результат функции

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

Например, функция NumbSquare, возводящая число в квадрат:

var

a:integer;

Function NumbSquare(x:integer):integer;

 begin

NumbSquare:=x*x; {возвращаемое значение из функции}

 end;

begin

 write(’Введите целое число:’);

readln(a);

 writeln(’Квадрат равен:’, NumbSquare(a));

 end.

 

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

В рассмотренном примере возведения в квадрат целого числа в таком случае тело функции будет следующее:

 Result:=x*x; {возвращаемое значение из функции}

15.5. Пример функции возведения числа в степень

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

Тогда положительное a число в степени n может быть представлено так:

.

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

var 

x,y,pow:real;

Function Power(a,n:real):real;

begin

 Result:=exp(n*ln(a));

end;

begin

write(’Введите число a:’); readln(x);

write(’Введите показатель степени n:’); readln(y);

write(’a в степени n равно:’);

pow:=Power(x, y);

write(’a в степени n равно:’, pow);

end.

Для вызова функции Power ее имя указывается в операторе присваивания значения переменной pow, в которой и хранится значение возведенного в степень y числа x. Параметры x и y в момент обращения к функции Power - это фактические параметры. Они подставляются вместо формальных параметров a и n в заголовке функции, и затем над ними осуществляются нужные действия. Полученный результат присваивается специальной зарезервированной переменной с именем Result, которая в теле любой функции интерпретируется как значение, которое вернет функция после окончания своей работы.

15.6. Рекурсивный вызов подпрограмм

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

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

Рассмотрим пример расчета факториала с помощью рекурсивного вызова функции. Основная идея здесь заключается в том, что факториал некоторого числа n может быть представлен как произведение этого числа n на факториал числа n-1:

n!=n*(n-1)!,

а факториал числа 1 равен 1.

var 

x,fact:integer;

Function Factorial(n:integer):integer;

begin

 if (n=1) then

 Result:=1

 else

 Result:=n*Factorial(n-1);

end;

begin

write(’Введите число n:’); readln(x);

fact:=Factorial(x);

write(’n!=’, fact);

end.

Расчет факториала в рассмотренном примере схематически представлен на рис. 9.

n!= n * (n-1)!

 

(n-1)*(n-2)!

(n-2)*(n-3)!

 

......

(n-(n-3))*2!

2*1!

=1

Рис. 9. Схема расчета факториала с помощью рекурсии

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

15.7. Опережающее описание подпрограмм

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

Procedure A (i:integer);

begin

B(i); {!!! вызов еще не описанной процедуры}

end;

Procedure B (i:integer);

begin

A(i);

end;

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

Procedure B (i:integer); forward;

Procedure A (i:integer);

begin

B(i); {вызов процедуры с опережающим описанием}

end;

Procedure B;

begin

A(i);

end;

15.8. Процедурный тип

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

В Object Pascal существует два процедурных типа: тип-процедура и тип-функция.

Для объявления процедурного типа используется заголовок процедуры (функции), в котором опускается ее имя, например:

type

ProcType1 = Procedure (a, b, с: Real; var d: Real);

ProcType2 = Procedure (var a, b);

РгосTypeЗ = Procedure;

FuncType1 = Function: String;

FuncType2 = Function (var s: String): Real;

В программе могут быть объявлены переменные процедурных типов, например:

var

p1 : ProcType1;

fl, f2 : FuncType2;

ар : array [1..N] of ProcType2;

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

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

Процедурные типы совместимы друг с другом при выполнении следующих условий:

  1.  оба процедурных типа имеют одинаковое количество параметров;
  2.  параметры занимают одинаковые позиции и имеют один тип – для совместимости имена параметров не играют никакой роли;
  3.  типы результатов, возвращаемые функциями, идентичны;
  4.  любой процедурный тип совместим со значением nil.

16. Тип вариант: variant

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

16.1. Свойства варианта

В переменную-вариант можно поместить:

  1.  целое или вещественное число;
  2.  логическое значение;
  3.  строку;
  4.  время и/или дату;
  5.  OLE-объект;
  6.  массив произвольной размерности и длины, содержащий элементы одного из перечисленных выше типов.

Варианты могут участвовать в целочисленных, вещественных, логических и время-дата выражениях при условии корректности соответствующих преобразований. Например, если варианту v присвоена строка '1.0', то выражение 1+v будет правильным вещественным значением 2,0. Однако если v := 'текст', выражение 1+v вызовет ошибку (исключение) EVariantError (см. раздел 18.).

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

В Object Pascal начиная с версии Delphi 6 появились так называемые пользовательские варианты, которые фактически снимают ограничения на характер значений варианта.

16.2. Подпрограммы для работы с вариантами

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

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

Function VarAsType(const V: Variant; VarType: Integer): Variant;

Преобразует данные варианта V к типу, определяемому параметром VarType

Procedure VarCast(var Dest: Variant; const Source: Variant; VarType: Integer) ;

Преобразует данные варианта Source к типу,определяемому параметром VarType, и помещает результат в переменную Dest

Procedure VarClear(var V:Variant);

 

Освобождает динамическую память, если она была связана с вариантом, и дает ему тип varEmpty

Procedure VarCopy(var Dest: Variant; const Source:Variants);

Копирует параметр Source в вариант Dest

Function VarFromDateTime

(DateTime: TDateTime):Variant;

Возвращает вариант, содержащий данные DateTime типа дата-время

Function VarIsEmpty(const V:

Variant): Boolean;

Возвращает True, если вариант V не содержит данных

Function VarIsNull

(const V: Variant) : Boolean;

Возвращает True, если вариант V содержит данные неопределенного типа (varNull),

Function VarToDateTime

(const V:Variant ): TDateTime);

Преобразует данные варианта V к типу дата-время

Function VarToStr

(const V: Variant) : String;

Преобразует данные варианта V к строке

Function VarType

(const V: Variant) : Integer;

Возвращает тип хранящихся в варианте данных

16. Вариантные массивы

Значением варианта может быть массив данных, такие варианты называются вариантными массивами. (Не путать с обычным или динамическим массивом, элементами которого являются варианты!) Значениями элементов вариантного массива могут быть любые допустимые для варианта значения, кроме строк varstring. Значениями элементов вариантного массива могут быть и варианты, а это значит, что в таком массиве могут одновременно храниться данные разных типов (в том числе и строки). Например:

uses

variants;

var

V: Variant;

begin

{Создаем одномерный вариантный массив с 5 элементами и

наполняем его}

V := VarArrayCreate([0, 4], varVariant);

V[0] := 1; {целый тип}

V[1] := 1234.5678; {вещественный тип }

V[2] := ’Hello world’; {строковый тип}

V[3] := True; {логический тип}

{Пятым элементом исходного массива сделаем еще один

массив}

V[4] := VarArrayOf([1, 10, 100, 1000]);

writeln(V[2]); {Hello world}

writeln(3*V[4][2]); {300} 

end.

Все действия с вариантными массивами осуществляются с помощью процедур и функций, приведенный в таблице 12.

Таблица 11. Процедуры и функции для операций над

вариантными массивами

Function VarArrayCreate

(const Bounds: array of Integer; VarType: Integer): Variant;

Создает вариантный массив из элементов типа VarType с количеством и границами измерений, указываемых параметром Bounds

Function VarArrayDimCount

(const A: Variant): Integers;

Возвращает количество измерений вариантного массива А или 0, если А не массив

Function VarArrayHighBound

(const A: Variant; Dim: Integer): Integer;

Возвращает верхнюю границу индекса вариантного массива А по измерению Dim

Function VarArrayLock

(var A: Variant): Pointer;

 

Блокирует массив (предотвращает его возможные изменения размеров) и возвращает указатель на связанные с ним данные

Function VarArrayLowBound

(const A: Variant; Dim: Integer): Integers;

Возвращает нижнюю границу индекса вариантного массива А по измерению Dim

Function VarArrayOf

(const Values: array of Variant): Variants;

 

 

Создает одномерный вариантный массив по перечню значений, содержащихся в открытом массиве Values. Нижняя граница индексов вариантного массива в этом случае равна 0

Procedure VarArrayRedim

(var A:Variant;

HighBound: Integer);

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

Function VarArrayRef(const A:

Variant): Variants;

Возвращает ссылку на вариантный массив. Используется при обращении к API-функциям

Procedure VarArrayUnlock

(var A: Variant)

Отменяет действие функции VarArrayLock

 

17. Указатели и динамическая память

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

17.1. Указатели

Указатель (pointer)это переменная, которая в качестве своего значения содержит адрес первого байта памяти, занимаемой некоторой переменной.

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

var

p1 : ^Integer; {указатель на переменную целого типа}

р2 : ^Real; {указатель на переменную целого типа}

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

var

р: pointer; {нетипизированный указатель}

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

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

var

pI1, pI2: ^integer;

pR: ^Real;

p: Pointer;

begin

pI1 := pI2; {верно}

pl1 :=pR; {неверное присваивание}

end;

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

p :=pR;

pI1 := p;

17.2. Работа с динамической памятью

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

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

var pI,pJ: ^Integer;

pR: ^Real;

begin

New (pI) ;

New (pR) ;

end;

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

pJ^:=2; {В область памяти pJ помещено значение 2}

pR^:=2*14; {В область памяти pR помещено значение 6.28}

Итак, если за указателем нет значка ^, то имеется в виду сам указатель, т.е. адрес, по которому размещены данные. Если за указателем стоит ^, то имеется в виду значение переменной, размещенной по адресу в указателе.

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

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

Dispose(pJ);

Dispose(pR);

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

Освободившийся указатель, как правило, приравнивают зарезервированному слову nil (как бы «указатель в никуда»).

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

GetMem(p, size); // резервирование памяти;

FreeMem(p, size); // освобождение памяти.

где р - нетипизированный указатель;

size - размер в байтах выделяемой или освобождаемой части кучи.

Примечание

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

В таблице 12 приведены процедуры и функции для работы с динамической памятью в Object Pascal.

Таблица 12. Процедуры и функции для работы с динамической памятью

Function Addr(X):

Pointer;

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

Procedure Dispose (var P: Pointer);

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

Procedure FreeMem (var P: Pointer; Size: Integer);

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

Procedure GetMem var P: Pointer; Size: Integer) ;

Выделяет для нетипизированного указателя Р фрагмент динамической памяти требуемого размера Size

Procedure New(var P: Pointer) ;

Выделяет фрагмент кучи для размещения переменной и помещает в типизированный указатель Р адрес первого байта

Function SizeOf(X): Integer;

Возвращает длину в байтах внутреннего представления указанного объекта Х

Операционная система Windows имеет собственные средства работы с памятью. За информацией обращайтесь к справочной службе в файлах WIN32.hlp или WIN32S.hlp.

18. Работа с файлами

Файл – это именованная область внешней памяти компьютера. Любой файл имеет три характерные особенности. Во-первых, у него есть имя, что дает возможность программе работать одновременно с несколькими файлами. Во-вторых, он содержит компоненты одного типа. Типом компонентов может быть любой тип Object Pascal, кроме файлов. Иными словами, нельзя создать “файл файлов”. В-третьих, длина вновь создаваемого файла никак не оговаривается при его объявлении и ограничивается только емкостью устройств внешней памяти.

18.1. Объявление и связывание файла

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

  1.  текстовые файлы:

<имя> = TextFile;

  1.  типизированные файлы:

<имя> = File of <тип>;

  1.  нетипизированные файлы:

<имя> = File;

где <имя> – имя объявляемой файловой переменной.

Например:

F1: TextFile; {текстовый файл}

F2: File of integer; {типизированный файл целых чисел}

F3: File; {нетипизированный файл}

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

AssignFile(var <имя>, <ИмяФайла>: string)

где <имя> – имя файловой переменной;

<ИмяФайла> – имя конкретного файла, с которым связывается файловая переменная <имя>.

<ИмяФайла> задается согласно принятым в Windows правилам. Оно может быть полным, т.е. состоять не только непосредственно из имени файла, но и включать путь к файлу (имя диска, каталогов и подкаталогов), например:

AssignFile(f, '\students\ivanov\korni.txt');

18.2. Доступ и работа с файлом

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

В Object Pascal можно открыть файл для чтения, для записи информации, а также для чтения и записи одновременно.

Для чтения файл открывается с помощью стандартной процедуры Reset:

Reset (<имя>);

где <имя> – имя файловой переменной, связанной ранее процедурой AssignFile с уже существующим файлом.

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

Если делается попытка инициировать чтение из несуществующего файла, возбуждается исключительная ситуация. Чтобы проверить, существует ли дисковый файл, можно использовать стандартную функцию FileExists, которая возвращает Truе, если указанный при обращении этой функции файл существует, и False – если нет. Например,

if FileExists(FileName) then

..... {Файл существует}

else

..... {Файл не существует}

end;

Открытие файла для записи осуществляется с помощью процедуры Rewrite:

Rewrite (<имя>);

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

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

Append (<имя>);

которая инициирует запись в ранее существовавший текстовый файл для его расширения, при этом указатель файла устанавливается в его конец. Процедура Append применима только к текстовым файлам, т.е. их файловая переменная должна иметь тип TextFile (см. выше). Процедурой Append нельзя инициировать запись в типизированный или нетипизированный файл. Если текстовый файл ранее уже был открыт с помощью Reset или Rewrite, использование процедуры Арpend приведет к закрытию этого файла и открытию его вновь, но уже для добавления записей.

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

CloseFile (<имя>);

где <имя> – имя файловой переменной.

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

Таблица 1 Процедуры и функции для работы с файлами

Procedure AssignFile(var F; FileName: String) ;

Связывает файловую переменную F с именем файла FileName

Function ChangeFileExt 

(const FileName, Extension: String):String;

Изменяет существующее расширение файла на расширение, заданное параметром Extension

Procedure ChDir(Path: String);

Изменяет текущий каталог: Path - строковое выражение, содержащее путь к устанавливаемому по умолчанию каталогу

Procedure CloseFile(var F);

 

 

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

Function DateTimeToFileDate (DateTime: TDateTime): Integer;

Преобразует значение DateTime в системный формат времени создания (обновления) файла

Function DiskFree(D:Byte): Longint;

 

Возвращает объем в байтах свободного пространства на указанном диске: D - номер диска (0 - устройство по умолчанию, 1 - диск А ,2- диск В и т, д.). Функция возвращает значение -1, если указан номер несуществующего диска

Function DeleteFile(const FileName:String): Boolean;

Уничтожает файл с именем (допускается указание пути) FileName. Возвращает True, если операция прошла успешно

Function DiskSize(D:Byte): Longing;

 

Возвращает объем в байтах полного пространства на указанном диске: d - номер диска (0 - устройство по умолчанию, 1 - диск А ,2- диск Д и т. д.). Функция возвращает значение -1, если указан номер несуществующего диска

Function EOF (var F):Boolean;

Тестирует конец файла и возвращает True, если файловый указатель стоит в конце файла. При записи это означает, что очередной компонент будет добавлен в конец файла, при чтении - что файл исчерпан

Procedure Erase(var F);

Уничтожает файл F. Перед выполнением процедуры необходимо закрыть файл

Function FileAge(const

FileName: String): Integer;

Для файла FileName возвращает время его последнего обновления (в системном формате) или -1, если такого файла не существует

Function ExcludeTrailingBackslash (const S: String): String;

Исключает из строки S замыкающий символ “\” (если этот символ не замыкает строку, возвращает S без изменения)

Function ExpandUNCFileName (const FileName: String): String;

Дополняет имя файла текущим сетевым каталогом (и диском)

Function ExtractFileDir(const FileName:String): String;

Извлекает из полного имени файла маршрут доступа к нему (без последнего символа “\”)

Function ExtractFileExt(const FileName: String): String;

Извлекает из полного имени файла его расширение (с ведущей точкой)

Function ExtractFileName(const FileName: String): String;

Извлекает из полного имени файла его имя (с расширением)

Function ExtractFilePath(const FileName: String): String;

Извлекает из полного имени файла маршрут доступа к нему (с последним символом “\”)

Function ExtractRelativePath (const BaseName,

DestName:String): String;

Извлекает из полного имени файла имя маршрута относительно DestName (промежуточные каталоги заменяются символами “..\”)

Function ExtractShortPathName (const FileName : String): String;

Преобразует имя файла к короткому формату 8.3 для MSDOS и Windows 3-х

Function FileDateToDateTime (FileDate: Integer): TDateTime;

Преобразует системный формат FileDate времени создания файла в формат дата-время

Function FileExists(const FileName: String): Boolean;

Возвращает True, если файл с именем (и, возможно, маршрутом доступа) FileName существует

Function FindFirst 

(const Path: String;

Attr: Integer; var F:

TSearchRec): Integer;

 

Возвращает атрибуты первого из файлов, зарегистрированных в указанном каталоге: Path - маршрут поиска и маска выбора файлов; Attr - атрибуты выбираемых файлов; F - переменная типа TSesrchRec, в которой будет возвращено имя первого выбранного файла. При успешном поиске возвращает значение 0

Procedure FindClose(var F: TSearchRec);

Освобождает память, выделенную для поиска файлов функциями FindFirst/FindNext

Function FindNext(var F: TSearchRec): Integer;

Возвращает в переменой F имя следую-щего файла в каталоге. Переменная F должна предварительно инициироваться обращением к функции FindFirst. При успешном поиске возвращает значение 0

Procedure GetDir(D:Byte;

var S: String);

 

 

Возвращает имя текущего каталога (каталога по умолчанию): D - номер устройства (0 - устройство по умолчанию, 1 - диск А, 2- диск В и т. д.); S - переменная типа String, в которой возвращается путь к текущему каталогу на указанном диске

Function IncludeTrailingBackslash

(const S:String): String;

Возвращает полный маршрут доступа к файлу с ведомым символом “\”

Function lOResult: Integer;

Возвращает условный признак последней операции ввода-вывода

Function IsPathDelimiter(const S: String; Index: Integer):

Boolean;

Возвращает True, если в строке S символ Index есть “\”.

 

Function MatchesMask 

(const Filename, Mask:String): Boolean;

Возвращает True, если имя FileName соответствует групповому имени Mask

Procedure MkDir(Dir:String);

Создает новый каталог Dir.

Procedure Rename(var F; NewName: String);

Переименовывает файл F; NewName - строковое выражение, содержащее новое имя файла. Перед выполнением процедуры необходимо закрыть файл

Procedure Reset(var F: File; [RecSize: Word]);

Открывает существующий файл. RecSize имеет смысл только для не типизированных файлов и указывает размер блока данных

Procedure Rewrite(var F: File; [Recsize: Word]) ;

Создает новый файл. Recsize имеет смысл только для не типизированных файлов и указывает размер блока данных

Procedure RmDir(Dir:String);

 

 

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

18. Текстовые файлы

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

Записи текстового файла могут иметь переменную длину, что существенно влияет на характер работы с ними. Текстовый файл трактуется в Object Pascal как совокупность строк переменной длины (записей). Доступ к каждой строке возможен лишь последовательно, начиная с первой. При создании текстового файла в конце каждой строки ставится специальный признак eoln (End Of LiNe – конец строки) последовательность кодов #13 и #10, а в конце всего файла – признак eof (End Of File – конец файла) код #26. Эти признаки можно проверить одноименными логическими функциями (см. ниже).

Для чтения, т.е. ввода из файла, строк из текстового файла используют процедуры read и readln, а для записи, т.е. вывода в файл применяются процедуры write, writeln, рассмотренные в разделе 9. Следует заметить, что первым параметром в них обязательно должна стоять файловая переменная того файла, с которым происходит работа.

Процедура readln идентична процедуре read за исключением того, что после считывания последней переменной оставшаяся часть строки до маркера eoln пропускается, поэтому следующее обращение к readln начинается с первого символа новой строки. Если readln вызвать без параметров vi, то это приведет к пропуску всех символов текущей строки вплоть до eoln.

Следует заметить, что запись в текстовый файл всегда осуществляется через буфер. Сначала выводимые записи помещаются в буфер, а затем данные из буфера собственно и записываются в файл (по мере накопления). Для принудительного «сброса» буфера в файл пользуются процедурой flush. При вызове writeln без параметров vi в файл выводится пустая строка.

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

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

Procedure Append(var

F: TextFile);

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

Function Eoln(var

F: TextFile): Boolean;

Тестирует маркер конца строки и возвращает True, если конец строки достигнут

Procedure Flush(varF);

Очищает внутренний буфер файла и, таким образом, гарантирует сохранность всех последних изменений файла на диске

Procedure Read

(var F: TextFile;

V1 [, V2,...,Vn ]);

Читает из текстового файла последовательность символьных представлений переменных Vi типа char, String, а также любого целого или вещественного типа, игнорируя признаки EOLN

Procedure ReadLn 

(var F: TextFile;

[V1 [, V2, ...,Vn]]);

Читает из текстового файла последовательность символьных представлений переменных Vi типа char, String, а также любого целого или вещественного типа с учетом границ строк

Function SeekEof

(var F: Text): Boolean;

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

Function SeekEoln 

(var F: TextFile):

Boolean;

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

Procedure Write

(var F: Text;

P1 [, P2,..., Pn] ) ;

Записывает символьные представления параметров Pi в текстовый файл

Procedure WriteLn 

(var F: Text;

[P1 [, P2, ..., Pn]]);

Записывает символьные представления параметров Pi и знак конца строки eoln в текстовый файл

Пример открытия и записи в текстовый файл:

var

f: TextFile; {объявление файловой переменной}

begin

AssignFile(f, ’studex.txt’); {связывание файловой

переменной с файлом}

Rewrite(f); {открытие файла для записи;

если его нет, то будет создан}

Writeln(f, ’Выводим текст в файл!’);

Flush(f); {физическая запись буфера вывода в файл}

CloseFile(f); {закрытие файла }

end.

Процедуры ввода read и readln удобно использовать также и для чтения числовых данных из текстовых файлов.

18.4. Типизированные файлы

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

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

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

Таблица 15. Подпрограммы для работы с типизированными файлами

Function FilePos 

(var F): Longint;

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

Function FileSize 

(var F): Longint;

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

Procedure Seek(var F;

N: Longint) ;

Смещает указатель файла F к требуемому компоненту: n - номер компонента файла (первый компонент файла имеет номер 0)

Procedure Read

(var F, V1,..., Vn);

Читает данные из типизированного файла F.

Vi - переменные такого же типа, что и компоненты файла

Procedure Write

(var F,P1, ...,Pn);

Записывает данные в типизированный файл F.

Pi - выражения такого же типа, что и компоненты файла

Примечание

Чтобы переместить указатель в конец типизированного файла, можно написать: Seek (FileVar, FileSize(FileVar)).

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

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

var

f: file of integer; {объявление файловой переменной

файла целых}

i,n: integer;

begin

AssignFile(f, ’studex.dtf’); {связывание файловой

переменной с файлом}

Rewrite(f); {открытие файла для записи;

если его нет, то будет создан}

 for i:=1 to 3 do

 begin

write(’Введите число:’);

readln(n);

write(f, n); {вывод числа файл}

 end; {for i} 

CloseFile(f); {закрытие файла}

end.

18.5. Нетипизированные файлы

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

При открытии нетипизированного файла процедурами reset или rewrite можно указать длину записи нетипизированного файла в байтах.

Например,

var

f: file;

begin

AssignFile(f,'myfile.dat');

Reset(f,512);

...

end.

Длина записи нетипизированного файла указывается вторым параметром при обращении к процедурам reset или rewrite, в качестве которого может использоваться выражение типа Longint. Если длина записи не указана, она принимается равной 128 байтам.

Object Pascal не накладывает каких-либо ограничений на длину записи нетипизированного файла. Для обеспечения максимальной скорости обмена данными рекомендуется задавать длину, которая была бы кратна длине физического сектора дискового носителя информации (512 байт). Однако операции обмена данными с дисковыми устройствами в среде Windows кэшируются, т.е. осуществляются через промежуточный буфер памяти, поэтому можно задавать Recsize=1, что позволяет обмениваться с файлом блоками любой длины, начиная с одного байта.

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

Procedure BlockRead(var F: File; var Buf; Count: Integer [;

var AmtTransferred: Integer]) ;

Procedure BlockWrite(var F: File; var Buf; Count: Integer [;

var AmtTransferred: Integer]);

где Buf – буфер: переменная, которая будет участвовать в обмене данными с дисками;

Count – количество записей, которые должны быть прочитаны или записаны за одно обращение к диску;

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

За одно обращение к процедурам может быть передано до Count*RecSize байт, где RecSize - длина записи нетипизированного файла. Передача происходит, начиная с первого байта переменной Buf. Необходимо позаботиться о том, чтобы длина внутреннего представления переменной Buf была достаточной для размещения всех Count*RecSize байт при чтении информации с диска. Если при чтении указана переменная недостаточной длины или если в процессе записи на диск не окажется нужного свободного пространства, возникнет ошибка ввода-вывода, которую можно заблокировать, указав необязательный параметр AmtTransferred.

После завершения процедуры чтения/записи в нетипизированный файл указатель смещается на Count записей. Процедурами Seek, FilePos и FileSize можно обеспечить доступ к любой записи нетипизированного файла.

Пример работы с нетипизированным файлом:

var

f: file; {объявление файловой переменной}

i,n: integer;

begin

AssignFile(f, 'studex.dat'); {связывание файловой

переменной с файлом}

Rewrite(f, 4); {открытие файла для записи по 4 байта;

если его нет, то будет создан}

for i:=1 to 3 do

 begin

write('Введите число:');

readln(n);

BlockWrite(f, n, 1); {вывод числа файл}

 end; {for i}

CloseFile(f); {закрытие файла}

end.

19. Обработка ошибок 

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

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

19.1. Классификация ошибок

Ошибки в программах Delphi разделяют на три основные группы:

  1.  синтаксические (ошибки в тексте);
  2.  алгоритмические;
  3.  исключения.

Синтаксические ошибки, их также называют ошибками времени компиляции (Compile-time error), наиболее легко устранимы. Их обнаруживает компилятор Delphi еще до запуска программы – на этапе компиляции, а программисту остается только внести изменения в текст программы и выполнить повторную компиляцию. Если программа откомпилирована и ее удается запустить на выполнение, то значит в программе нет синтаксических ошибок.

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

Нарушения в работе программы или ошибки времени выполнения (Run-time error) называются в Delphi исключениями (exception). Их причиной, как правило, являются действия пользователя, хотя фактически исключения можно классифицировать как алгоритмические ошибки.

Например, следующая программа, которая выводит на экран частное двух чисел a и b будет работать правильно сколь угодно раз до тех пор, пока пользователь не введет значение b=0. В этом случае произойдет так называемая исключительная ситуация: деление на ноль.

var

a,b:integer;

c:real;

begin

write(’Введите число a:’); readln(a);

write(’Введите число b:’); readln(b);

c:=a/b;

 write(’Результат a/b=’,c:7:4);

end.

19.2. Обработка исключений: try–конструкции

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

В Delphi для контроля и обработки исключительных ситуаций предусмотрены две конструкции защищенного блока: try..except и try..finally.

try {защищенный блок}

 <выполняемые_операторы>

except {блок обработки исключений}

on ТипИсключения1 do оператор_обработки1;

on ТипИсключения2 do оператор_обработки2;

...

on ТипИсключенияN do оператор_обработкиN;

else

<операторы_обработки_остальных_исключений>

end;

try {защищенный блок}

 <выполняемые_операторы>

finally {заключительный блок операторов}

<заключительные_операторы>

end;

где <выполняемые_операторы> – операторы, размещенные в секции try..except, выполнение которых может вызвать исключительную ситуацию;

except – ключевое слово, обозначающее начало секции обработки исключений;

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

Защищенный блок начинается зарезервированным словом try (попытаться) и завершается словом end. Конструкции защищенного блока: try..except и try..finally отличаются способом обработки исключения.

В блоке except выполнение происходит следующим образом:

  1.  Выполняются <выполняемые_операторы> в защищенном блоке – секции try..except.
  2.  Если <выполняемые_операторы> выполнены без возникновения исключительной ситуации, работа защищенного блока на этом прекращается, и управление получает оператор, стоящий за end.
  3.  Если при выполнении <выполняемых_операторов> возникло исключение, то управление получает соответствующий обработчик в блоке обработки исключений except, а если для возникшего типа исключений в блоке except обработчик не предусмотрен, то выполняются <операторы_обработки_осталь-ных_исключений>, стоящие за словом else. Если для возникшего типа исключений в блоке except обработчик не предусмотрен, и нет блока else, то выполняется стандартная процедура обработки данного типа исключений.

В блоке try..finally операторы в заключительном блоке операторов – секции finally..end получают управление всегда, независимо от того, возникло ли исключение при выполнении <выполняемых_операторов> в защищенном блоке try..finally или нет. Если исключение возникло, то все операторы в секции try..finally, стоящие за оператором, в котором произошло исключение, пропускаются, и выполняются <заключительные_операторы> в заключительном блоке операторов. Если исключения не было, то после выполнения всех <выполняемых_операторов> будут выполнены <заключительные_опера-торы>.

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

var

a,b:integer;

c:real;

begin

write(’Введите число a:’); readln(a);

write(’Введите число b:’); readln(b);

 try

c:=a/b;

except

on EZeroDivide do

begin

 writeln(’Ошибка! Деление на ноль!’);

c:=0;

end;

end;

 write(’Результат a/b=’,c:7:4);

end.

Примечание

При каждом исключении среда Delphi перехватывает управление программой. В этом случае бывает полезно отменить такое поведение среды. Для этого в меню Tools | Debugger Options на странице Languaqe Exception необходимо убрать флажок в переключателе Stop on Delphi Exceptions (в ранних версиях Delphi опция меню Tools | Environent Options и на странице Preference убрать флажок в переключателе Break on exception).

Если в программе важен лишь сам факт возникновения исключения и несущественен его тип, то в try-конструкции вместо обработчиков той или иной ошибки в секции except..end можно разместить общие операторы реакции на любую ошибку. Например,

var

a,b:integer;

c:real;

begin

write(’Введите число a:’); readln(a);

write(’Введите число b:’); readln(b);

 try

c:=a/b;

except

 writeln(’Ошибка! Деление на ноль!’);

c:=0;

end;

 write(’Результат a/b=’,c:7:4);

end.

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

В некоторых ситуациях бывает необходимо инициировать собственное исключение. Для этого пользуются зарезервированным словом raise (возбудить). Если raise встретилось в секции try..except или try..finally, то немедленно начинают свою работу секции соответственно except..end и finally..end. Если оно встретилось в except..end или finally..end, то считается, что данный защищенный блок на текущем уровне вложенности завершил свою работу и управление передается вышестоящему уровню.

За информацией о типах исключений обращайтесь к справочной службе Delphi.


 

А также другие работы, которые могут Вас заинтересовать

75966. Шоковая терапия и ее исторические последствия 53 KB
  Шоковая терапия - пропагандистское (газетное) название, с легкой руки некоторых публицистов приклеившееся к политике, которую начало проводить, придя к власти, реформаторское правительство Ельцина-Гайдара – политике стабилизации экономики. (попытка России перейти к Рыночной Экономике)
75967. Норманская и антинорманская теории: дискуссия закончена 15.84 KB
  Норманская теория происхождения древнерусского государства: это направление в историографии которое склоняется к тому что варяги и скандинавы норманы стали основателями Киевской Руси то есть первого восточнославянского государства основоположники Бейер и Миллер. Ломоносов подверг уничтожающей критике все основные положения этой антинаучной концепции генезиса Древней Руси. Оно могло происходить не только вследствие тесного общения скандинавов и славян но просто по тому что все первые князья на Руси а значит законная власть...
75968. Русь и Византия. Русь и Степь: система взаимовлияний и неоднозначность трактовок 17.05 KB
  Русь и Степь: система взаимовлияний и неоднозначность трактовок Направления византийского влияния: Византия помогала Руси стать православным государством. Принятие на Руси христианства резко усилило влияние византийской культуры. Крещение Руси по православному обряду ввело киевского князя в круг европейских монархов и позволило использовать типичный для Средневековья способ закрепления дип. Крещение Руси также дало нам славянскую азбуку и приобщило к европейской культурной традиции.
75969. Евразийская идея в российской истории: представители, концепция, критика 18.4 KB
  Евразийское движение родилось в 20-е годы в Европе в среде российской эмигрантской интеллигенции. Само имя движения говорит за себя. Евразийство – это культурно-историческая концепция, в которой Россия рассматривается как Евразия
75970. И.А. Ильин о сущности русской идеи 16.89 KB
  В этих фразах и заключается мысль о том что идея должна быть основана на народных особенностях и только это сможет обеспечить достойную жизнь народа на земле сделает возможным исполнение его исторического призвания. Если же говорить о самой сущности то Ильин утверждает: Русская идея есть идея сердца. Эта идея конечно же корнями уходит в православное христианство которое русский человек принял не от меча не по расчету не страхом и не умственностью а чувством добротою совестью и сердечным созерцанием. Таким образом русская...
75971. СВЯТО ПОХІДНОЇ 94.5 KB
  Мета уроку: навчальна: повторити і систематизувати знання учнів 11-го класу по темі «Похідна та її застосування», формувати навички практичного застосування отриманих знань; розвивальна: розвивати знання учнів про похідну, формувати навички контролю....
75972. СВЯТО ПОКРОВИ 517 KB
  Мета: ознайомити учнів зі святом Покрови, пробуджувати пізнавальні інтереси до історії української культури; сприяти примноженню родинних і національних традицій. Виховувати любов та пошану до традицій українського народу.
75973. Інтегрований урок в 3 класі «Поле чудес» 57.5 KB
  Формування ключових компетентностей: вміння вчитися – самоорганізовуватися до навчальної діяльності у взаємодії; загальнокультурної – дотримуватися норм мовленнєвої культури, звязно висловлюватися в контексті змісту; здоровязбережувальної...
75974. Ескіз розпису косовської кераміки 312 KB
  Мета уроку: розвивати вміння дітей складати декоративну композицію для розпису косовської кераміки. Матеріали до уроку: керамічні вироби косовських майстрів: вази глечики свічники миски кашпо; навчальні таблиці з зображенням виробів з Косова...