Содержание
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) then … end;
Тип дата-время
Тип дата-время определяется стандартным идентификатором 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-объектами. С помощью специальных функций можно узнать значение какого типа хранится в варианте или преобразовать значение к нужному типу.