среда, 27 апреля 2011 г.

Простые типы данных

Перевод из справочной системы Delphi

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

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

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

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

Подмножественные типы сохраняют порядковые номера своих базовых типов.

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

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

ФункцияПараметрыВозвращаемое значениеКомментарий
OrdПорядковое выражениеПорядковый номер значения выраженияНе принимает аргументы типа Int64.
PredПорядковое выражениеПредыдущее значение в порядковом типе
SuccПорядковое выражениеПоследующее значение в порядковом типе
HighИдентификатор или переменная порядкового типаНаибольшее значение в типе Так же работает с ShortString и массивами.
LowИдентификатор или переменная порядкового типаНаименьшее значение в типе Так же работает с ShortString и массивами.
Например:
High(Byte) //возвращает 255; 
//максимальное значение типа Byte – 255
Succ(2)//возвращает 3; 
//3 – значение, следующее за 2. 

Стандартные процедуры Inc и Dec увеличивают и уменьшают значение переменной порядкового типа. Например:

Inc(I)//эквивалентно I := Succ(I) 
Inc(I)//если I – целочисленная переменная, 
//то эквивалентно I := I + 1. 

Целочисленные типы

Целочисленные типы представляют подмножество целых чисел. Обобщенные типы данных – это Integer и Cardinal; ими следует пользоваться, если это возможно, поскольку они обеспечивают лучшую производительность с базовыми ЦПУ и ОС. Далее в таблице приведены диапазоны значений и форматы хранения для компилятора Delphi.

Обобщенные целочисленные типы
ТипДиапазонФормат
Integer-2147483648..214748364732 бит со знаком
Cardinal0..429496729532 бит без знака

Фундаментальные целочисленные типы:
ТипДиапазонФормат
Shortint-128..1278 бит со знаком
Smallint-32768..3276716 бит со знаком
Longint-2147483648..214748364732 бит со знаком
Int64-2^63..2^63-164 бит со знаком
Byte0..2558 бит без знака
Word0..6553516 бит без знака
Longword0..429496729532 бит без знака
UInt640..2^64-164 бит без знака

В общем случае арифметические операции над целыми числами возвращают значения типа Integer, которые эквивалентны 32 битному типу Longint. Операции возвращают тип Int64 только когда выполняются над одним или более операнде типа Int64. То есть, следующий код будет выдавать некорректный результат:

var I: Integer; J: Int64; ... I := High(Integer); J := I + 1;

Чтобы в этом примере получить возвращаемое значение с типом Int64 следует преобразовать тип I в Int64:

... J := Int64(I) + 1;

Замечание: некоторые стандартные подпрограммы, принимающие целочисленные аргументы обрезают значения типа Int64 до 32 бит. Тем не менее, подпрограммы High, Low, Succ, Pred, Inc, Dec, IntToStr, and IntToHex полностью поддерживают аргументы типа Int64. Кроме того, функции Round, Trunc, StrToInt64 и StrToInt64Def возвращают значения типа Int64. Некоторые подпрограммы вообще не могут принимать значения типа Int64.

Когда вы увеличиваете последнее или уменьшаете первое значение в целочисленном типе, результат "оборачивается" вокруг начала или конца диапазона. Например, тип Shortint имеет диапазон -128..127, после выполнения кода:

var I: Shortint; ... I := High(Shortint); I := I + 1;

значение I будет -128. В том случае, если опция проверки диапазонов у компилятора включена, этот код вызовет ошибку при выполнении программы.


Символьные типы

Фундаментальные символьные типы – это AnsiChar и WideChar. Значения типа AnsiChar имеют размер 1 байт (8 бит). Символы упорядочены в соответствии с локальным набором символов (который может быть мультибайтовым).

Символы WideChar используют более чем один байт для представления символов. В текущей реализации WideChar имеет размер слова (16 бит), символы упорядочены в соответствии с набором символов Unicode (следует учесть, что он может стать длиннее в последующих реализациях). Первые 256 символов 256 Unicode соответствуют символам ANSI.

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

Строковая константа с длиной 1, например, 'A' может определять символьное значение. Предопределенная функция Chr возвращает символьное значение для любого числа в диапазоне AnsiChar; например, Chr(65) возвращает букву 'A'.

Значения AnsiChar и WideChar, как и целые числа, при уменьшении или увеличении значения в начале или конце диапазона значений (в том случае, если контроль диапазона значений включен) "оборачиваются" вокруг конца диапазона. Например:

var Letter: AnsiChar;
I: Integer;
begin
  Letter := High(Letter);
  for I := 1 to 66 do Inc(Letter);
// Letter имеет значение A (ASCII 65)
end;

Булевские типы

Есть четыре предопределенных булевских типа данных: Boolean, ByteBool, WordBool и LongBool. Boolean – это предпочтительный тип. Остальные существуют для обеспечения совместимости с другими языками и библиотеками операционных систем.

Переменные типов Boolean и ByteBool занимают один байт памяти, переменная типа WordBool занимает два байта (одно слово), а переменная типа LongBool – четыре байта (два слова).

Булевские значения определяются предопределнными константами True и False. Имеют место следующие взаимосвязи:

BooleanByteBool, WordBool, LongBool
False < TrueFalse <> True
Ord(False) = 0Ord(False) = 0
Ord(True) = 1Ord(True) <> 0
Succ(False) = TrueSucc(False) = True
Pred(True) = FalsePred(False) = True

Значения типа ByteBool, LongBool или WordBool рассматриваются как как True, когда их порядковый номер не равен нулю. Если такое значение оказывается в контексте, где ожидается значение типа Boolean, компилятор автоматически преобразует значения с ненулевыми порядковыми номерами в True.

Это замечание относится к порядковым номерам булевских значений, но не к значениям как таковым. В Delphi, выражения типа Boolean не могут быть приравняны к целым или дробным числам. То есть, если X – это целочисленная переменная, то инструкция:

if X then ...;

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

if X <> 0 then ...; 
    { use longer expression that returns Boolean value }
   var OK: Boolean;
   ... 
 if X <> 0 then
   OK := True;
 if OK then ...;

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

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

type typeName = (val1, ...,valn)

где typeName и каждый эелемент val – допустимые идентификаторы. Например, объявление:

type Suit = (Club, Diamond, Heart, Spade);

определяет перечисляемый тип с именем Suit и с возможными значениями Club, Diamond, Heart и Spade, где Ord(Club) возвращает 0, Ord(Diamond) возвращает 1 и так далее.

Когда вы объявляете перечисляемый тип, вы заявляете, что каждый элемент val является константой типа typeName. Если идентификаторы val в этой же области видимости будут использованы по другому, - возникнет конфликт имен. Например, предположим, вы объявили тип:

type TSound = (Click, Clack, Clock);

К сожалению, Click является методом, определенным для TControl и всех объектов VCL, которые являются его наследниками. Если вы пишете приложение и создаете обработчик события следующего вида:

procedure TForm1.DBGridEnter(Sender: TObject);
  var
     Thing: TSound;
     begin 
       ... 
       Thing := Click;
     end;

вы получите ошибку компиляции, поскольку в этой области видимости компилятор интерпретирует Click как обращение к методу TForm Click. Вы можете исправить это, специфицирую идентификатор. Так, если TSound объявлен в MyUnit, вы можете написать:

Thing := MyUnit.Click;

Однако лучшим решением будет выбрать имена констант, которые не будут конфликтовать с остальными идентификаторами. Например:

type
  TSound = (tsClick, tsClack, tsClock);
  TMyColor = (mcRed, mcBlue, mcGreen, mcYellow, mcOrange);
  Answer = (ansYes, ansNo, ansMaybe);

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

var MyCard: (Club, Diamond, Heart, Spade);

Но, если вы таким способом объявите MyCard, вы не сможете объявить в этой же области видимости другую переменную, использующую эти же идентификаторы:

var Card1: (Club, Diamond, Heart, Spade);
 var Card2: (Club, Diamond, Heart, Spade);

вызовет ошибку компиляции, но:

var Card1, Card2: (Club, Diamond, Heart, Spade);

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

type 
  Suit = (Club, Diamond, Heart, Spade); 
  var 
    Card1: Suit;
    Card2: Suit;
Перечисляемые типы с явно обозначенным порядком

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

type Size = (Small = 5, Medium = 10, Large = Small + Medium);

определяет тип с именем Size, чьи возможные значения включают Small, Medium и Large, для которых Ord(Small) возвращает 5, Ord(Medium) возвращает 10, а Ord(Large) возвращает 15.

Перечисляемый тип фактически является поддиапазоном, наименьшие и наибольшие значения которого соответствуют наименьшим и наибольшим порядковым номерам констант в объявлении. В предыдущем примере тип Size имеет 11 возможных значений, порядковые номера которых находятся в диапазоне от 5 до 15 (так, тип array[Size] of Char будет представлять массив из 11 символов.) Только три из этих значений имеют имена, а остальные элементы доступны через преобразование типво и такие подпрограммы как Pred, Succ, Inc и Dec. В следующем примере "анонимные" значения в диапазоне Size назначаются переменной X.

var 
  X: Size; 
  X := Small;   // Ord(X) = 5 
  X := Size(6); // Ord(X) = 6 
  Inc(X);       // Ord(X) = 7

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

type SomeEnum = (e1, e2, e3 = 1);

SomeEnum имеет только два возможных значения: Ord(e1) возвращает 0, Ord(e2) возвращает 1, Ord(e3) также возвращает 1. Поскольку e2 и e3 имеют одинаковый порядок они представляют одинаковое значение.

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

type SomeEnum = (e1, e2, e3);

А перечислямые константы с определенными значениями не имеют RTTI:

type SomeEnum = (e1 = 1, e2 = 2, e3 = 3);

Специфицируемые перечисления

Вы можете пользоваться специфицируемыми перечислениями в программном коде Delphi при включении директивы компилятора $SCOPEDENUMS.

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

unit Unit1;

interface

type
  TMyEnum = (first, second, third);


implementation

end.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Unit1 in 'Unit1.pas';

var
  First: Integer;
  Value: TMyEnum;

begin
  try
    Value := TMyEnum.First;

  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

Поддиапазонные типы

Поддиапазонные типы представляют собой подмножества значений из некоторого порядкового типа (который называется базовым). Любая конструкция вида Low..High, где Low and High – это константные выражения одного и того же порядкового типа, и Low меньше High, определяет поддиапазонный тип, который включает все значения между Low и High. Например, если вы объявляете перечисляемый тип:

type 
  TColors = 
    (Red, Blue, Green, Yellow, Orange, Purple, White, Black);

Вы можете определить поддиапазонный тип:

type 
  TMyColors = Green..White;

Здесь TMyColors включает значения Green,Yellow, Orange, Purple и White.

Для определения поддиапазонных типов вы можете использовать числовые константы и символы (строковые константы с длиной 1):

type 
 SomeNumbers = -128..127;
 Caps = 'A'..'Z';

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

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

var SomeNum: 1..500;

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

Порядковый номер каждого значения в поддиапазоне устанавливается в соответствии с порядком базового типа (в первом примере: если Color – это переменная, которая содержит значение Green, Ord(Color) возвращает 2 безотносительно от того, к какому типу относится Color, к TColors или TMyColors). Значения не "оборачиваются" вокруг начала или конца диапазона, даже в тех случаях, когда базовым типом является integer или символьный тип. Увеличение или уменьшение значения с переходом за границу поддиапазона просто преобразовывает значение в базовый тип. То есть:

type Percentile = 0..99;
 var I: Percentile;
   ... 
   I := 100;

Вызовет ошибку. Следующий код:

...
 I := 99;
 Inc(I);

Присваивает I значение 100 (хотя опция контроля диапазона компилятора включена).

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

const X = 50; Y = 10;
 type Scale = (X - Y) * 2..(X + Y) * 2;

вызовет ошибку. Для решения этой проблемы перепишите объявление типа таким образом, чтобы оно не начиналось с открывающей скобки:

type Scale = 2 * (X - Y)..(X + Y) * 2;

Вещественные типы данных

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

ТипДиапазон значенийЗначимые цифрыРазмер в байтах
Real482.9 x 10^-39 .. 1.7 x 10^3811-126
Single1.5 x 10^-45 .. 3.4 x 10^387-84
Double5.0 x 10^-324 .. 1.7 x 10^30815-168
Extended3.6 x 10^-4951 .. 1.1 x 10^493210-2010
Comp-2^63+1 .. 2^63-110-208
Currency-922337203685477.5808.. 922337203685477.580710-208

Обобщенный тип Real в текущей реализации эквивалентен Double.

ТипДиапазон значенийЗначимые цифрыРазмер в байтах
Real-5.0 x 10^-324 .. 1.7 x 10^30815-168


Замечание: Шестибайтовый тип Real48 назывался Real в ранних версиях Object Pascal. Если вы перекомпилируете в Delphi код, который использует старый шестибайтовый тип Real, вы возможно захотите изменить его на Real48. Вы также можете воспользоваться директивой компилятора {$REALCOMPATIBILITY ON} для переключения типа Real назад, в шестибайтовый формат.

Следующие замечания относятся к фундаментальным вещественным типам:

  • Real48 поддерживается для обратной совместимости. Поскольку формат хранения этого типа данных не является естественным для архитектуры процессоров Intel, применение этого типа данных вызовет снижение производительности в сравнении с остальными вещественными типами.
  • Extended позволяет вести вычисления с высокой точностью (в сравнении с остальными вещественными типами), но он в меньшей степени переносим. Будьте внимательны при использовании типа Extended при создании файлов с данными, которые вы планируете использовать на других платформах.
  • Тип Comp (computational - вычислительный) является естественным для процессоров с архитектурой Intel и представляет 64-битное целое число. Тип классифицируется как вещественный, поскольку его поведение отличается от порядковых типов (например, вы не можете увеличить или уменьшить значение типа Comp(см. примечание)). Comp поддерживается только для обратной совместимости. Для улучшения производительности используйте тип Int64.
  • Currency - это тип данных с фиксированной точкой, который минимизирует ошибки округления при финансовых вычислениях. На платформе Win32 он хранится как масштабируемое целое число, содержащее четыре минимально значимые цифры, представляющие десятичные знаки. При смешивании с прочими вещественными типами в выражениях и при присваивании, значения типа Currency автоматически делятся и умножаются на10000.

Комментариев нет:

Отправить комментарий