10. Типы данных

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

Простые типы

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

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

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

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

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

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

Для целых типов функция Ord(x) возвращает само значение х, т. е. Ord(X) = х для х, принадлежащего любому целому типу. Применение Ord(x) к логическому, символьному и перечисляемому типам дает положительное целое число в диапазоне от 0 до 1 (логический тип), от 0 до 255 (символьный), от 0 до 65535 (перечисляемый).

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

  • pred(x) - возвращает предыдущее значение порядкового типа, которое соответствует порядковому номеру ord (X) -1, т. е. Ord(Pred(X)) = Ord(X) - 1;
  • succ(X) - возвращает следующее значение порядкового типа, которое соответствует порядковому номеру ord (X) +1, т. е. Ord(Succ(X)) = Ord(X) + 1.

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

var
  с : Char;
begin
  с := '5';
end;

то функция Pred(C) вернет символ '4', а функция Succ(C) - символ '6'.

Если представить себе любой порядковый тип как упорядоченное множество значений, возрастающих слева направо и занимающих на числовой оси некоторый отрезок, то функция pred(x) не определена для левого, a succ(X) – для правого конца этого отрезка.

Целые

Ниже приведена таблица поддерживаемых целых типов и диапазон допустимых значений:


Тип данных

Длина, байт

Диапазон допустимых значений

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

Cardinal

4

0 .. 4 294 967 295

LongWord

4

0 .. 4 294 967 295

Int64

8

-9*1018 .. +9*1018

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

Логические

Логический тип может принимать 2 значения: True или False. К логическим типам относят: Boolean, ByteBool, WordBool, LongBool. Первый тип является стандартным типом Паскаля, а последние три введены для совместимости с Windows. В данной реализации языка функция Ord вернет для True – 1, для False – 0.

В Pascal Script функция Ord для переменной типа Boolean также возвращает результат типа Boolean.

var
  b: Boolean;
  bb: ByteBool;
begin
  b := True;
  bb := True;
  Debug(Ord(b));		// True
  Debug(Integer(Ord(b)));	// 1
  Debug(Ord(bb));		// 1
end;

Символьный

К символьному типу относится тип Char, вмещает в себя 1 байт, символы с кодом 0..255. Переменной этого типа можно присвоить один символ:

var
  c: Char;
  n: Byte;
begin
  c := ‘A’;
  c := #65;       // Код символа A
  n := Ord(‘E’);  // Возвращает код символа
end;

Для кодировки символов используется таблица ASCII. Первая половина символов от 0 до 127 является стандартной и приведена в таблице. Вторая половина от 128 до 255 зависит от шрифта.


Код

Символ

Код.

Символ

Код.

Символ

Код

Символ

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

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

Перечисляемый тип

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

type
  TColors = (Red, White, Blue);
var
  Color: TColors;
begin
  Color := Red;
end;

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

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

Например:

type
  TColors = (Red, Green, Blue);
  TNum = (One, Two, Three, Four);
 
procedure Form_Create;
var
  n: TNum;
  c: TColors;
begin
  n := One;
  c := Four;	    // Несовместимость типов
  Debug(Ord(c));    // Значение 3
end;

Однако компилятор Pascal Script не увидит ошибку и код будет работать. Будьте внимательны.

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

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


Тип данных

Длина, байт

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

Диапазон допустимых значений

Single

4

7-8

1.5*10e-45 .. 3.4*10e38

Double

8

15-16

5.0*10e324 .. 1.7*10e308

Extended

10

19-20

3.4*10-4951 .. 1.1*10e4932

Currency

8

19-20

+/-922 337 203 685 477,5807

В DataExpress числовые поля имеют тип Double.

Сравнение вещественных чисел

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

var
  d: Double;
begin
  d := 0.2;
  // Это условие не выполнится
  if d = 0.2 then// Можно так. Это условие выполнится
  if d – 0.2 < 0.001 then// Так правильно сравнивать вещественные числа
  if SameValue(d, 0.2, 0) thenend;

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

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

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

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

IbOutput.Caption := DateToStr(Date + 21);

поместит в метку IbOutput дату, соответствующую текущей дате плюс 3 недели. Чуть сложнее с исчислением времени. Например, чтобы добавить к текущему времени полтора часа, следует использовать выражение:

Time + StrToTime('1:30')

или

Time+1.5/24

Составные типы

К составным типам относятся: массивы, записи и множества. Они характеризуется множественностью образующих этот тип элементов. Каждый элемент, в свою очередь, может принадлежать структурированному типу, что позволяет говорить о возможной вложенности типов. В Object Pascal допускается произвольная глубина вложенности типов, однако суммарная длина любого из них во внутреннем представлении не должна превышать 2 Гбайт (в реализации от RemObjects не проверялось).

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

Строки представлены типами String, AnyString, AnsiString, WideString и PChar. Для всех типов строк, кроме PChar, память выделяется динамически, по мере необходимости. Типы String, AnyString, AnsiString являются синонимами. Тип String можно представить, как массив символов. Подобно массиву можно обращаться к отдельным символам строки по их индексу. Первый символ строки имеет индекс 1. Примеры работы со строками:

var
  S: String;
begin
  S := ‘hello, World’;	  // Строка в кодировке UTF-8
  S[1] := ‘H’;		  // Меняем h на H. Индекс первого символа равен 1.
  S := S + ‘!!!’;	  // Сложение строк.
  S := ‘Привет, МИР!!!’;  // Строка в кодировке UTF-8
  Debug(S[1]);		  // Будет выведен знак «?», потому что для кодирования символов 				
                          // кириллицы требуется более одного байта.
end;

Строка может хранить данные в кодировке ANSI или UTF-8. В кодировке ANSI символ занимает 1 байт. Символ в кодировке UTF-8 может занимать от 1 до 4 байт. Если строка хранит данные в этой кодировке, то обращение к элементам массива строки уместно только, если обрабатываются символы первой половины таблицы ASCII с кодами от 1 до 127. В ином случае нужно применять функции для работы со строками UTF-8. Для символов, кодируемых более чем одним байтом, в UTF-8 принята следующая система кодировки: в первом байте от двух до четырёх старших битов заняты единицами - их количество соответствует количеству байт в символе, во втором и последующих байтах первые два бита заняты соответственно единицей и нулём. В символах кодируемых одним байтом старший бит всегда занят нулём. Таким образом, при побайтном рассмотрении строки, однобайтный символ не может быть случайно принят за часть многобайтного.

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

К строкам применимы операции сравнения. Сравнение происходит посимвольно.

S := ‘Hello World’;
S2 := ‘Hello world’;
// Условие ложно, потому что W <> w
if S = S2 then// Условие истинно, потому что W < w
if S < S2 then

Тип WideString хранит строки в кодировке UTF-16. Каждый символ кодируется двумя байтами (за исключением символов с номерами больше FFFF - для обозначения таких символов используется четыре байта). Несмотря на то, что каждый символ занимает 2 байта, вы можете обратиться к символу по индексу, как если бы для кодирования использовался 1 байт. Строки этого типа используются OLE-объектами и функциями Windows API.

var
  S: WideString;
begin
  S := ‘Hello, МИР!!!’;
  Debug(S[8]);	// Символ «М»
end;

Символы, кодируемые четырьмя байтами всегда состоят из пары двухбайтовых слов (т.наз. «суррогатной пары»), первое из которых принадлежит диапазону D800..DBFF, а второе - диапазону DC00..DFFF. Символы, кодируемые двумя байтами не могут входить ни в один из этих диапазонов. Что позволяет при посимвольном обходе строки легко отличать двухбайтовые символы от частей четырёхбайтовых

Тип PChar представляет строки с завершающим нулем. Переменная типа PChar содержит указатель на массив строк. Концом строки считается символ с кодом #0. В чистом виде тип PChar не используется, т. к. Pascal Script не работает с указателями. Предположительно, он введен только для совместимости с функциями Windows API.

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

function MessageBoxA(hWnd: Integer; lpText, lpCaption: String; uType: Cardinal): Integer; external 'MessageBoxA@user32.dll stdcall';
function MessageBoxW(hWnd: Integer; lpText, lpCaption: WideString; uType: Cardinal): Integer; external 'MessageBoxW@user32.dll stdcall';
 
var
  S: String;
  WS: WideString;
begin
  S := ‘Строка’;
  WS := ‘Привет, МИР!!!’;
  WS := S;
  S := WS;
  MessageBoxA(0, S, ‘Сообщение’, 0);
  MessageBoxW(0, WS, ‘Сообщение’, 0);
end;

Варианты

Переменная вариантного типа может принимать хранить значение следующих типов: целый, вещественный, логический, строка, дата-время, OLE-объект. Вариант представляет собой структуру, в которой хранятся сведения о типе и значении переменной. В выражениях и при передаче параметров в процедуры происходит попытка преобразования варианта к нужному типу. Если это не удается, будет ошибка “invalid variant type cast”. Вариант может хранить специальное значение Null (пусто, неизвестно).

Любая арифметическая операция с null даст в результате null. Попытка автоматического преобразования null к какому-либо типу (кроме Variant) приведет к ошибке 'could not convert variant of type (Null) into type (…)'.

var
  V1, V2, V3, V4, V5: Variant;
begin
  V1 := 1;
  V2 :=4;
  V3 := V + V2;		   // Результат 5 или ‘5’
  V4 := V3 + Null;	   // V4 = Null
  V5 := V1 + ‘sdfsdf’;	   // invalid variant type cast
  MsgBox(‘Сообщение’, V4); // Could not convert variant of type (Null) into type (String) 
end;

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