    Object Pascal: основные понятия 

    ГЛАВА     3 

    Д.ж. Райдер 


        Введение в Object Pascal 

    Первым делом, ознакомьтесь с языком Object Pascal. Именно благодаря ему 
Delphi стала системой быстрой разработки приложений. 

         Работа с типами данных 

    Изучите способы организации данных в программах, написанных на Object 
Pascal, и научитесь создавать собственные типы данных. 

         Работа с операторами и выражениями 

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

         Применение операторов для управления выполнением программы 

    Изучите элементарные операторы модификации или вывода информации. 

         Создание процедур и функций 

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

         Построение модулей 

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


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

    Введение в Object Pascal 

    В Delphi заглянуть под капот не составляет труда: создайте новый проект, 
дважды щелкните мышью на форме, и перед вами появится Object Pascal — 
локомотив, мощь которого заставляет работать Delphi. 

    procedure TForml.FormCreate(Sender: TObject);
	begin                     
        ...
      end;

    Ну и что же дальше? Да все что угодно! Но только в рамках допустимого. 
Обязательно применение официальной системы Pascal. Обязательно соблюдение 
"синтаксиса" языка при задании тех или иных действий. Обязательно соблюдение 
ограничений на обращение к данным и 


53

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

    Target := Expression 

    Данный оператор присваивает переменной Target (месту в программе, 
предназначенному для хранения данных конкретного типа) значения, возвращаемые 
выражением Expression. Например, между словами begin и end процедуры 
TForml.FormCreate можно было бы вставить следующий оператор Object Pascal: 

    Caption := 'Здравствуй, мир!' 

    Здесь указывается, что переменная, которой присваивается новое значение, 
имеет имя Caption, а выражение, результат которого присваивается,— строковая 
константа Здравствуй, мир! Вот, собственно, и все, что нужно для создания 
программы Delphi. Затем можно скомпилировать, запустить программу и 
посмотреть, что она делает. В данном случае она только выводит на экран 
пустую форму с заголовком Здравствуй, мир!. Изучив Delphi основательнее, вы 
сможете назначить для этой программы и более сложные действия. 
    Данный пример выбран не случайно. Программисты уже многие годы опробуют 
таким образом новый язык программирования или среду разработчика. Как правило, 
это элементарная задача, однако в некоторых языках программирования она 
неоправданно усложнена. В Delphi же для вывода на экран приветствия 
Здравствуй, мир! достаточно простого оператора присваивания. 
    Эта простота возникла не сама собой, а благодаря эффективной структуре 
языка программирования Object Pascal. Конечно, приходится и под капот 
заглядывать, но Delphi подсказывает, на чем следует сосредоточить внимание. И 
сколько бы ни было там кабелей и шлангов, в Object Pascal программа 
разбивается на множество небольших частей, работающих почти не зависимо друг 
от друга. И эта "независимость" очень важна. Она предотвращает конфликты между 
любой парой частей. И еще она важна для задания взаимодействия между 
отдельными частями. Из мельчайших создаются части покрупнее; из последних, в 
свою очередь, еще более крупные; наконец, из самых крупных частей (возможно, с 
некоторыми подстановками на сам ом нижнем уровне) составляется программа. 
    Delphi сама задает правильный курс. Указанная выше процедура 
TForml.FormCreate представляет собой одну из наиболее мелких частей. Точка между 
TForml и FormCreate означает, что FormCreate — составляющая более крупной 
части с именем TForml. Для каждой программы Delphi автоматически создает новую 
форму, помещая ее в более крупную часть, известную как модуль (unit). 
Пролистав окно с исходным текстом, содержащим TForml.FormCreate, нетрудно 
обнаружить, что указанный оператор присвоения приветствия "Здравствуй, мир!" 
является составляющей более крупной части. 

    unit Unitl; 
    interface 
    uses 
      Windows, Messages, SysUtils, Classes, Graphics, Controls, 
      Forms, Dialogs; 
    type 
      TForml = class(TForm) 
      procedure FormCreate(Sender: TObject); 
      private 
        { Закрытые объявления. } 
      public 
        { Открытые объявления. } 
      end; 
    var 
      Forml:   TForml; 
    implementation 
    {$R  *.DFM} 
    procedure  TForml.FormCreate(Sender:   Tobject); 
    begin 
      Caption   :=   'Здравствуй,   мир!1 
    end; 
    end. 

    Из данного примера видно, что часть программы, именуемая TForml, — это 
объявление особого типа, известного как класс. После объявления TForml в 
примере следует объявление переменной Forml типа TForml. Однако, несмотря на 
реальность операторов данного примера, весь модуль не более чем "потенциален". 
Все объявления пока просто ждут своего

    54	Часть I. Основные положения 

часа, который наступит с возникновением проекта. Для каждой программы 
Delphi автоматически задает новый проект, и, чтобы заглянуть под капот еще 
глубже и узнать, что же заставляет программу "крутиться", следует рассмотреть 
исходный текст, содержащийся в файле PROJECT1.DPR. 

    program Projectl; 
    uses 
      SysUtils, Forms, 
      Unitl in  'Unitl.pas' { Forml}; 
    {$R  *.RES} 
    begin 
       Application.Initialize; 
       Application.CreateForm(TForml,   Forml); 
       Application.Run; 
    end. 

    Иногда все получается наудивление просто. Каждое слово, фраза, 
последовательность символов в предыдущем примере имеет свое назначение, и 
каждая строка — это оператор, написанный в соответствии с правилами Object 
Pascal, так что программа вполне готова к компиляции. Однако все, что Delphi 
предоставляет автоматически, является лишь малой частью того , что можно 
запрограммировать. Текст указанного выше проекта — простейший пример. Более 
подробная (но далеко не полная) картина того, что может содержаться в 
программе, дана в следующем примере. 

    program programid; 
    uses 
      unitid, unitid2, unitidS; 
    labels 
      labell, Iabel2, Iabel3; 
    const 
      constid =  constant_expression; 
    type 
      typeid  =  type_definition; 
    var 
      varl: type_definition; 
    procedure prod; 
      определение_процедуры; 
    function func1 : typeid; 
       определение_функции; 
    begin 
       операторы; 
    end. 

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

    Заголовок программы 
    Блок программы 

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

   команда  uses 
   объявления 
   выполняемый оператор 

    Команда uses необязательна и должна размещаться перед любыми объявлениями 
и выполняемым оператором. Объявления содержат определения меток, констант, 
типов, переменных, процедур и функций, которые могут следовать в любом 
порядке. Однако объявление любого идентификатора (последовательности букв и 
цифр, которая может включать также символ подчеркивания и обязательно должна 
начинаться с буквы) должно предшествовать его использованию. Выполняемый 
оператор — это всегда составной оператор, состоящий из некоторого числа 
выполняемых операторов различных видов, заключенных в скобки begin и end и 
заканчивающихся точкой. 

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

    Глава 3. Object Pascal: основные понятия 55 

    Например, программисты, работающие на С и C++, привыкли разбивать 
программы на множество функций. Все программы C/C++ должны начинаться с функции 
main() или Winmain(). В Delphi подобная процедура не вызывается. Выполнение 
программы Delphi происходит в расположенном в конце блока программы составном 
операторе begin ... end. В программе Delphi может быть только один 
программный модуль, точно так же, как в программе С или C++ может быть только 
одно обращение к функции main(). 

    И такой подход прослеживается во всем определении языка Object Pascal. 
Всем программным задачам присваиваются имена, и затем эти задачи 
рассматриваются как способы внутренней записи информации и последующего 
манипулирования ею. Затем все это объединяется в один выполняемый оператор. 
    В оставшейся части данной главы будут рассмотрены способы, которыми в 
Object Pascal выполняются следующие действия. 
    •	Запись информации в переменные различных типов. 
    •	Объединение информации с помощью операторов, образующих выражения. 
    •	Управление вычислением выражений во время выполнения программы с помощью операторов. 
    •	Объединение операторов в процедуры и функции. 
    •	Сопоставление логически связанных компонентов и обеспечение доступа к 
ним из нескольких программ. 
    Это, конечно же, далеко не полная информация о Delphi и Object Pascal. 
Более подробные сведения о среде разработчика и языке программирования можно 
будет почерпнуть из следующих глав. 

    Типы данных 

    С помощью типов данных программист указывает компилятору, как хранить 
информацию в программе. При объявлении переменной необходимо указать ее тип. 
Одни типы уже определены в языке, другие программисту приходится задавать 
самому. В ранних языках программирования допускалось ограниченное число типов 
данных, и Pascal оказался одним из первых языков, допускающих определение в 
программе новых типов. 
    Типы данных, определяемые пользователем, обычно задаются в разделе 
определения типов программы или модуля (unit), однако это можно делать и 
внутри процедур или функций. Объявления типов действуют в пределах того блока, 
в котором они размещены. Внё этого блока ссылаться на такие типы нельзя. 
Внутри же они заменяют все внешние типы с тем же именем. Объявленные типы 
данных можно применять в любом месте области их видимости; запрещена только 
ссылка определяемого типа на самого себя (тут, однако, есть одно исключение, 
касающееся указателей). 
   Объявления типов в Pascal являются для компилятора чем-то вроде схем, 
которые он должен запомнить на случай, если вдруг встретит в программе 
ссылки на тот или иной тип. Само по себе объявление типа не вносит в программу 
никаких изменений. 
    Что же касается объявлений var, то они задают компилятору некоторые 
действия, связанные с ранее объявленными типами. Тип переменной ограничивает 
как ее значения, так и операции, которые можно выполнять с этими значениями. 
    Определения типов и переменных могут размещаться в нескольких местах 
компонентов программы. Выглядят же они следующим образом. 

    type 
      typel = type_definitionl; // Новые типы данных 
      // определяются в разделе "type". 
      // Каждому новому типу 
      // присваивается имя, затем он 
      // определяется через уже 
      // существующие типы. 
      type2 = type_definition2; // В одном разделе "type" можно 
      // объявить несколько типов. Самое 
      // простое определение типа 
      // состоит из имени типа, 
      type3 = typel; // определенного ранее. 

      // Новые переменые объявляются в var 
      // разделе "var". Каждой новой 
      varl : type_definition3;  // переменной сначала присваивается 
      // имя, а затем — тип (на основе 
      // ранее определенных типов). 
      var2, var3 : type_definition4; // В одном разделе "var" 


    56 Часть I. Основные положения 

      // можно объявить  несколько 
      // переменных.   Нескольким 
      //  переменным можно 
      // присваивать  один и тот же 
      //  тип. 
      var4   :   typel;      //  Программу легче 
      // читать,   если 
      // переменным присвоены 
      //  существующие  типы. 

    Синтаксис Object Pascal позволяет одновременно конструировать 
исключительно сложные типы и определение переменных. Однако определение типов 
в разделах type тех или иных блоков дает возможность использовать эти типы в 
разных частях программы. Новые типы определяются из типов следующих категорий. 
    •	Простые типы для хранения информации в форме чисел и других 
"упорядоченных" значений. 
    •	Строковые типы для хранения последовательностей символов. 
    •	Структурные типы для одновременного хранения информации разных типов. 
    •	Указательные типы для косвенного обращения к переменным заданных типов. 
    •	Процедурные типы для обращения к процедурам и фущщиям, рассматриваемым как
переменные.  
    •	Вариантные типы для хранения в одной переменной данных различных типов. 
    Обычно идентификаторы типов используются только при определении новых 
типов или объявлении переменных. Есть, однако, несколько функций, в 
которых дмя типа мюжет использоваться как часть выполняемого оператора. 
Например, функция SizeOf(Т) возвращает количество байтов, занимаемых 
переменной Т. 
    Функция SizeOf очень важна для написания эффективных программ. Многие из 
определенных в Object Pascal типов имеют очень сложную структуру и могут 
занимать в памяти довольно много места. При этом элементы таких типов созданы 
скорее для представления значений в некотором логическом порядке, а не для 
того, чтобы занимать место в памяти. Функция SizeOf избавляет программиста от 
необходимости вычислять объем данных в подобных случаях. 

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

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

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

    Из простых типов данных порядковые — самые простые. В этих типах 
информация представляется в виде отдельных элементов. Связь между отдельными 
элементами и их представлением в памяти определяет естественные отношения 
порядка между этими элементами. Отсюда и название порядковые. 
    В Object Pascal определены три группы порядковых типов и два типа, 
определяемых пользователем. Группы — это целые, символьные и булевы типы. 
Порядковые типы, задаваемые пользователем, — это перечисления и поддиапазоны. 
    Все значения любого порядкового типа образуют упорядоченную 
последовательность, и значение переменной порядкового типа определяется его 
местом в этой последовательности. За исключением переменных целых типов, 
значения которых могут быть как положительными, так и отрицательными, первый 
элемент любого порядкового типа имеет номер 0, второй элемент — номер 1 и т.д. 
Порядковый номер целого значения равен самому значению. Отношение порядка 
определяет общие для данных всех порядковых типов операции. Некоторые 
стандартные функции такого вида встроены в Object Pascal. Они представлены в 
табл. 3.1.
    Для всех порядковых типов в Object Pascal существует операция задания типа 
для преобразования целых значений в значения соответствующих порядковых типов. 
Если Т — имя порядкового типа, а X — целое выражение, то Т (X) возвращает 
значение Т с порядковым номером X. 

    На заметку     
    Программисты, работающие на С и C++, для приращения или уменьшения 
значений переменных привыкли использовать операторы "++" и "--", 
возвращающие следующее и предыдущее значения. Программисты Delphi всегда 
разбивают эти операции на более простые составляющие с помощью функций 
Pred, Succ,Dec и Inc. 

    Глава 3. Object Pascal: основные понятия	57 


    Таблица 3.1. Операции над порядковыми типами 

    Операция	Описание 

    Low (Т)		Минимальное значение порядкового типа Т 
    High (Т)	Максимальное значение порядкового типа Т 
    Ord(X)		Порядковый номер значения выражения порядкового типа. Для целого 
выражения — просто его значение. Для остальных порядковых типов Ord возвращает 
физическое представление результата выражения, трактуемое как целое число. 
Возвращаемое значение всегда принадлежит одному из целых типов 
    Pred(X)		Предыдущее по порядку значение. Для целых выражений эквивалентно Х-1 
    Succ(X)		Следующее по порядку значение. Для целых выражений эквивалентно Х+1 
    Dec(V)		Уменьшает значение переменной на I. Эквивалентно V := Pred(V) 
    Inc(V)		Увеличивает значение переменной на 1. Эквивалентно V := Succ(V) 


    Целые типы 

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

    Integer 
    Shortlnt 
    Smalllnt 
    Longlnt 
    Byte 
    Word 
    Cardinal 

    Обратите внимание, что один из этих целых типов назван именно целым 
(integer). Это может иногда приводить к путанице, но мы легко сможем ее 
избежать, применяя термин целый к группе типов, a integer — к конкретному 
типу, определяемому в программе этим ключевым словом. Переменные физических 
целых типов имеют разные диапазоны значений в зависимости от того, сколько 
байтов памяти они занимают (что равно значению, возвращаемому функцией SizeOf 
для данного типа). Диапазоны значений для всех физических типов перечислены в 
табл. 3.2. 

    Таблица 3.2.	Физические целые типы

    Тип		Диапазон значений	Физический формат 

    Shortlnt	-128-127				8 бит, со знаком 
    Smalllnt	-32 768-32 767			16 бит, со знаком 
    Longlnt		-2 147 483 648-2 147 483 647	32 бит, со знаком 
    Byte		0-255					8 бит, без знака 
    Word		0-65 535				16 бит, без знака 

    Диапазоны значений и форматы физических целых типов не зависят от 
микропроцессора и операционной системы, в которых выполняется программа. Они 
не меняются (или, по крайней мере, не должны меняться) с изменением реализации 
или версии Object Pascal. 
    Диапазоны значений логических целых типов (Integer и Cardinal) 
определяются совершенно иным образом. Как видно из табл. 3.3, они никак не 
связаны с диапазонами соответствующих физических типов. Обратите внимание, что 
в Delphi по умолчанию задано 32-разрядное представление. 

    58	Часть I. Основные положения 


    Таблица 3.3. Логические целые типы 

	Тип		Диапазон значений			Физический формат 

	Integer	-32 768-32 767			16 бит, со знаком (Smalllnt)
	Integer	-2 147 483 648-2 147 483 647	32 бит, со знаком (Longlnt)
	Cardinal	0-65 535				16 бит, без знака (Word)
	Cardinal	0-2 147 483 647			32 бит, без знака (Longlnt)
    
    На заметку
    В С и C++ для целых значений определены типы int, short int (или просто 
short) и long int (или просто long). Тип int из C/C++ соответствует типу 
Integer из Delphi, a long из C/C++— Longlnt из Delphi. Однако Shortlnt из 
C/C++ соответствует в Delphi не Shortlnt, a Smalltlnt. Эквивалент 
ShortInt из Delphi в С/C++ — это signed char. Тип unsigned char в C/C++ 
соответствует типу Byte из Delphi. В C/C++ существует еще тип 
unsigned long, аналога которому в Delphi нет. 

    Над целыми данными выполняются все операции, определенные для порядковых 
типов, но с ними все же удобнее работать как с числами, а не с "нечисленными 
порядковыми типами". Как и "живые" числа, данные целых типов можно складывать 
(+), вычитать (-) и умножать (*). Однако некоторые операции и функции, 
применяемые к данным целых типов, имеют несколько иной смысл. 

    Операция	Результат 

    Abs (X)		Возвращает абсолютное целое значение X 
    х Div Y		Возвращает целую часть частного деления X на Y 
    X Mod Y		Возвращает остаток частного деления X на Y 
    Odd (X)		Возвращает булево True (истина), если X — нечетное целое, и False 
			(ложь) — в противном случае 
    Sqr (X)		Возвращает целый квадрат X (т.е. Х*Х). 


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

     На заметку
     В С/С++_целое деление обозначается косой чертой (/). В Delphi косая 
между двумя целыми даст действительный результат с плавающей запятой.


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

    Смысл символьных данных очевиден, когда они выводятся на экран или 
принтер. Тем не менее определение символьного типа может зависеть от того, что 
подразумевать под словом символ. Обычно символьные типы данных задают схему 
взаимодействия между участками памяти разного объема и некоторым стандартным 
методом кодирования/декодирования для обмена символьной информацией. В 
классическом языке Pascal не задано никакой схемы, и в конкретных реализациях 
применялось то, что на том же компьютере мог использовать каждый. 
    В реализациях языка Pascal для первых микропроцессоров была применена 
7-битовая схема, названная ASCII (American Standard Code for Information 
Interchange — Американский стандартный код для обмена информацией). Эта схема 
и поныне широко распространена, но информация хранится, как правило, в 
8-битовых участках памяти. Дополнительный бит удваивает число возможных 
представлений символов, но реализации расширенного набора символов ASCII часто 
бывают далеки от стандарта. В данной версии Delphi определен набор 8-битовых 
символов, известный как расширенный (extended) ANSI (American National 
Standards Institute — Американский национальный институт стандартов). Как бы 
то ни было, символьную схему приходится воспринимать так, как ее воспринимает 
операционная система. Для оконных операционных систем фирмы Microsoft это 
схема ANSI, включающая ограниченное число предназначенных для вывода 
международных знаков. В стремлении же применить более обширный набор 
международных знаков весь компьютерный мир переходит к 16-битовой схеме, 
именуемой UNICODE, в которой первые 256 знаков совпадают с символами, 
определенными в схеме ANSI. 

    Глава 3. Object Pascal: основные понятия	59 

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

    AnsiChar	Однобайтовые символы, упорядоченные в соответствии с расширенным 
			набором символов ANSI 
    WideChar	Символы объемом в слово, упорядоченные в соответствии с 
			международным набором символов UNICODE. Первые 256 символов 
			совпадают с символами ANSI 

    Символьные типы объемом в двойное слово (32 бит) отсутствуют. 
    Логический символьный тип именуется char. В классическом языке Pascal char 
— единственный символьный тип. В Delphi char всегда соответствует физическому 
типу данных AnsiChar. У американских программистов ассоциация символа с 
однобайтовой ячейкой памяти укоренилась за долгие годы настолько, что им 
зачастую просто не приходит в голову, что можно использовать другие схемы 
кодирования. Однако дискуссии по интернационализации программ в Internet и 
World Wide Web могут существенно изменить их отношение к проблеме объема 
символьных данных. Применяя логический тип char, следует делать реализации для 
других микропроцессоров и операционных систем, в которых char может 
определяться как WideChar. При написании программ, которые могут обрабатывать 
строки любого размера, для указания этого размера рекомендуется применять 
функцию SizeOf, не задавая ее жестко постоянной. Функция Ord(С), где С — 
любая переменная символьного типа, возвращает целое значение, которым символ С 
представлен в памяти. 

    Chr (X)		Преобразует целую переменную в переменную типа char с тем же 
			порядковым номером. В Delphi это эквивалентно заданию типа Char(X) 
    UpCase		Преобразует строчную букву в прописную 


    На заметку
    Процессор не различает типы char, определенные в C/C++ и Delphi. Однако 
функционально каждый из этих языков трактует данный тип совершенно по-разному. 
В С/C++ это целый тип, переменной которого можно присваивать целые значения. 
Переменной int можно присвоить символьное значение, а переменной char — целое. 
В Delphi символьные типы жестко отделены от численных. Для присвоения 
численному значению символьного здесь необходимо воспользоваться функцией Ord. 

    На зметку
    В языке Basic один символ представляется так же, как и строка символов. 
Функция Chr из Delphi эквивалентна функции CHR$ из Basic. Функция Ord из 
Delphi, возвращающая код ANSI символьной переменной, подобна функции ASC из 
Basic, аргумент которой представляет односимвольную строку. 

    Булевы типы 

    На ранней стадии обучения программисты осваивают понятие бита, два 
состояния которого можно использовать для записи информации о чем-либо, 
представляющем собой одно из двух. Бит может обозначать 0 или 1, ДА или НЕТ. 
ВКЛЮЧЕНО или ВЫКЛЮЧЕНО, ВЕРХ или НИЗ, СТОЯТЬ или ИДТИ. В Object Pascal 
информация о чем-либо, что можно представить как ИСТИНА (True) или ЛОЖЬ 
(False), хранится в переменных булевых типов. Всего таких типов четыре, и они 
представлены в табл. 3.4. 

	Таблица 3.4. Размеры переменных булевых типов

	Тип		Размер 

	Boolean	1 байт 
	ByteBool	1 байт
	WordBool	2 байт (объем Word)
	LongBool	4 байт (объем Longlnt)

    По аналогии с целыми и символьными типами, подразделяющимися на физические 
и логические, естественно предположить, что ByteBool, WordBool и LongBool — 
физические типы, Boolean — логический. Но в данном случае это не совсем так. 
Все четыре типа различны. Для Object Pascal предпочтителен тип Boolean, 
остальные определены для совместимости с другими языками программирования и 
операционными системами. 

    60	Часть I. Основные положения 

    Переменным типа Boolean можно присваивать только значения True (истина) и 
False (ложь). Переменные ByteBool, WordBool и LongBool могут принимать и 
другие порядковые значения, интерпретируемые обычно как Falsе в случае нуля 
и True — при любом ненулевом,значении. 

    На заметку
    Булевы типы в Delphi можно сравнить с типом LOGICAL языка FORTRAN. В 
Basic, С и C++ булевы типы как таковые отсутствуют. Булевы выражения в этих 
языках применяются точно так же, как во всёх остальных, однако результаты 
этих выражений интерпретируются не как значения отдельного типа, а как целые 
числа. Как в Basic, так и в C/C++ булевы выражения дают численные результаты, 
интерпретируемые как False в случае 0 и True — в случае любого ненулевого 
значения. Это совместимо с порядковыми значениями булевых выражений в Delphi. 

    На заметку
    В C/C++ простые сравнения дают результат 1 (True) или 0 (False). Это 
эквивалентно булевым значениям Delphi. Только результат сравнения в Delphi 
выводится как булевый, а не целый. В большинстве случаев типу Boolean из 
Delphi соответствует тип charg в С/С++. 

    На заметку
    В Basic зарезервированы слова TRUE (эквивалентно константе -1) и FALSE 
(эквивалентно константе 0). В Basic TRUE меньше FALSE, в Delphi, наоборот, 
False меньше True. 


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

    Type  enum_type = ( first_value, value2, valueS, last_value); 

    Обычно данные перечислимых типов содержат дискретные значения, 
представляемые не числами, а именами. Тип Boolean — простейший перечислимый тип 
в Object Pascal. Булевы переменные могут принимать два значения, выражаемые 
именами True и False, а сам тип определен в Object Pascal так, как будто он 
объявлен следующим образом: 

	Type Boolean =   (False, True); 

    С помощью типа Boolean в Object Pascal выполняются сравнения, большинство 
же перечислимых типов — это просто списки уникальных имен или идентификаторов, 
зарезервированных с конкретной целью. Например, можно создать тип MyColor (мой 
цвет) со значениями myRed, myGreen и myBlue (мой красный, мой зеленый, мой 
синий). Это делается совсем просто: 

	Type MyColor = (myRed, myGreen, myBlue); 

    В этой строке объявлены четыре новых идентификатора: MyColor, myRed, 
myGreen и myBlue. Идентификатором MyColor обозначен порядковый тип, 
следовательно, в синтаксисе Object Pascal можно применять этот идентификатор 
везде, где разрешены перечислимые типы. Остальные три идентификатора — это 
значения типа MyColor. Подобно символьным и булевым типам перечислимые не 
являются числами, и использовать их наподобие чисел не имеет смысла. Однако 
перечислимые типы относятся к порядковым, так что значения любого такого типа 
упорядочены. Идентификаторам в списке присваиваются в качестве порядковых 
номеров последовательные числа. Первому имени присваивается порядковый номер 
0, второму — 1 и т.д. 

    На заметку
    В С и C++ есть тип enum, аналогичный перечислимому типу Delphi. Но в 
этих языках можно произвольно присваивать идентификаторам постоянные значения. 
В Delphi же соответствие имен и их значений фиксировано: первому имени 
присваивается значение 0, каждому последующему — на единицу больше. В С тип 
enum применяется лишь как средство быстрого определения набора целых 
постоянных. В C++ объявленные в перечислимом типе идентификаторы можно 
присваивать только переменным того же типа. 


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

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

    Type subrange_type  =  low_value...high_value; 

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

    Глава 3. Object Pascal: основные понятия	61 


    Действительные типы 

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

    Таблица 3.5. Действительные типы 

    Тип		Порог		Максимальное 	Количество 		Объем (байт) 
					значение 		значащих цифр

    Real		2.9E-39	1.7Е38		11-12			6 
    Single		1.5E-45	3.4Е38		7-8			4 
    Double		5.0E-324	1.7Е308		15-16			8 
    Extended	3.4E-4932	1.1Е4932		19-20			10 
    Comp		1.0		9.2Е18		19-20			8 
    Currency	0.0001	9.2Е14		19-20			8 


    Ha заметку	
    Тип Real предназначен для совместимости с ранними версиями Delphi и Borland 
Pascal. Формат этого типа неудобен для семейства процессоров Intel, поэтому 
операции с типом Real выполняются несколько медленнее операций над остальными 
действительными типами. 

    Целые типы представляют целые числа, т.е. числа, дробная часть которых 
равна нулю. Разница между двумя неодинаковыми целыми числами не может быть 
меньше единицы. Именно благодаря этому целые числа применяются для обозначения 
дискретных величин, независимо от того, имеют ли реальные объекты какое-либо 
отношение к числам. Действительные типы предназначены для представления 
чисел, которые могут иметь дробную часть, поэтому они полезны для 
представления величин, которые могут быть довольно близкими, почти 
непрерывными. 
    Заметьте, именно почти. Несмотря на название действительные, переменные 
этих типов отличаются от математических действительных чисел. В Object Pascal 
действительный тип — это подмножество математических действительных чисел, 
которые можно представить в формате с плавающей запятой и фиксированным числом 
цифр. Для невнимательных программистов ситуация усугубляется тем, что в 
стандартных форматах IEEE (Institute of Electrical and Electronic Engineers -
Институт инженеров-электриков и электронщиков), применяемых в программах 
Delphi и вообще в большинстве программ для Windows, возможно точное 
представление только чисел с фиксированным числом бит в дробной части. 
Удивительно, но такое простое число, как 0.1, записывается в расширенном 
формате IEEE с некоторой погрешностью, пусть очень небольшой. Из-за этого 
представление с плавающей запятой оказывается несколько неудобным для 
программ, в которых сохраняется и выводится фиксированное число десятичных 
разрядов численных значений. Это относится и к программам, работающим с 
"живыми" деньгами. 
    Для частичного решения этой проблемы в Object Pascal определены два 
формата с фиксированной запятой. Тип Comp (computational — вычислительный) 
содержит только целые числа в диапазоне от -263+1 до 263-1, что примерно 
соответствует диапазону от —9,2х1018 до 9,2х1018. При программировании 
операций с американской валютой разработчикам обычно приходится искать 
естественный способ записи денежных сумм, в котором целая часть числа 
определяет количество долларов, дробная — центов. Если такие значения 
записывать в переменные типа Comp, придется представлять их в виде целого 
числа центов. В этом случае следует умножать значение на 100 для обращения 
центов в доллары, а затем делить на 100, чтобы снова получить центы. 
    Этих забот можно избежать, если воспользоваться типом Currency. В этом 
случае задачу выбора масштаба возьмет на себя компилятор. Физически значения 
Currency записываются в память того же объема, что и Comp, как целые числа, 
однако компилятор не забывает вовремя разделить значение на 10 000 (не на 
100!) для его приведения в соответствие с денежным знаком и умножить на 10 000 
перед записью в память. Это обеспечивает абсолютную точность в четыре 
десятичных знака после запятой.
    В Delphi есть модуль System, содержащий ряд процедур обработки данных 
действительных типов. Наиболее распространенные из них перечислены в табл. 
3.6. Много полезных процедур содержится также в модулях SysUtils и Math. 

    62 Часть I. Основные положения 


    Таблица 3.6. Функции действительных типов 

    Функция		Возвращаемое значение 

    Abs (x)		Абсолютная величина х 
    АгсТап(х)	Арктангенс х 
    Cos (х)		Косинус х (х выражается в радианах, а не в градусах) 
    Ехр(х)		Экспоненциальная функция от х 
    Frac(x)		Дробная часть х                                                    
    Int (х)		Целая часть х. Несмотря на название, возвращает действительное 
			значение (с плавающей запятой), т.е. просто устанавливает 
			нуль в дробной части
    Ln (х)		Натуральный логарифм от х
    Pi 		Число Пи (3,1416...)
    Round (х)	Ближайшее к х целое значение. Возвращает значение целого типа. 
			Условие "ближайшее к х" не работает, если верхнее и нижнее значения 
			оказываются равноудаленными (например, если дробная часть точно 
			равна 0,5). В этих случаях Delphi перекладывает решение на 
			операционную систему. Обычно процессоры Intel решают эту задачу в 
			соответствии с рекомендацией IEEE округлять в сторону ближайшего 
			четного целого числа. Иногда такой подход называют "банкирским 
			округлением" 
    Sin(x)		Синус x
    Sqr(х)		Квадрат х, т.е. х*х
    Sqrt(x)		Квадратный корень от х 
    Тrunc(х)	Целая часть х. В отличие от Int, возвращающей действительное 
			значение, Trunc возвращает целое

    На заметку
    Будьте внимательны при переносе численных выражений из одного языка 
в другой. В Basic функция SQR вычисляет квадратный корень, а функция Sqr из 
Delphi — квадрат числа. Для вычисления квадратного корня в Delphi применяется 
функция Sgrt. 


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

    В выражениях Delphi поддерживает три физических строковых формата: 
короткий (ShortString), длинный (LongString) и широкий (WideString). Их можно 
комбинировать в операторах присваивания и выражениях (все необходимые 
преобразования Delphi выполняет автоматически). 
    Переменные типов AnsiString и WideString — это динамически распределяемые 
массивы символов, максимальная длина которых ограничивается только наличием 
памяти. Разница между ними состоит в том, что в AnsiString знаки записываются 
в формате char, а в WideString — в формате WideChar. Обычно вполне достаточно 
одного типа AnsiString, однако при работе с международными наборами символов, 
такими как UNICODE, удобнее использовать WideString. 
    Тип ShortString — это, по существу, массив Array[0 . .255] of char. 
Первый его элемент задает динамическую длину строки, которая может 
принимать значения от 0 до 255 символов. Символы, составляющие строку, 
занимают мёстк от 1 до 255. Тип ShortString предназначен, в основном, для 
обеспечения совместимости с ранними версиями Delphi и Borland Pascal . 
    Логический строковый тип именуется просто String. Отнесение его к типу 
AnsiString или ShortString задается командой $Н. По умолчанию задается {$Н+}, 
и String совпадает с AnsiString. Если задать команду {$Н-}, то String будет 
совпадать с ShortString и иметь максимальную длину, равную 255 символам. 
    Для совместимости с другими языками программирования в Delphi 
поддерживается класс строк с конечным нулем. Зарезервированных слов или 
идентификаторов для этого класса не существует. 
    Строки с конечным нулем состоят из ненулевых символов и оканчиваются 
символом с порядковым номером 0 (#0). В отличие от типов AnsiString, 
ShortString и WideString, строки с нулевым окончанием не имеют указателя длины. 
Конец в этих строках обозначается нулем. 

    Глава 3. Object Pascal: основные понятия 63 

    Физически строки с нуль-окончанием подобны массивам символов с нумерацией 
элементов от нуля, наподобие array[ 0..X] of char, где X — некоторое 
положительное целое, большее нуля, хотя никаких объявлений подобного рода не 
происходит. Вместо этого определяется переменная-указатель PChar и 
распределяется необходимый объем памяти. При необходимости строке AnsiString 
можно присвоить тип PChar. 
    В табл. 3.7 перечислены некоторые процедуры и функции обработки данных 
строковых типов. 

    На заметку
    Программисты, работающие на С, привыкли записывать все строки в массивы с 
нуль-окончанием. Фактически они применяют в выражениях не строковые 
переменные, а указатели на них. Программисты, работающие на Basic, 
привыкли использовать строку как одно целое. Для типа AnsiString из Delphi 
годятся оба подхода. 

    Таблица 3.7. Строковые функции 

    Функция			Описание

    Concat(sl, s2, s3)	Возвращает последовательное соединение 
				строк. Эквивалентна оператору si+s2+s3 
    Copy(s, pos, len)	Возвращает подстроку длиной максимум len символов, 
				начинающуюся в позиции роз строки s 
    Delete(s, pos, len) Удаляет максимум len символов из строки s, начиная с позиции 
				pos 
    Insert (sourse, tar-      Вставляет строку source в строковую переменную 
    get,   pos)			target, начиная с позиции роз 
    Length (s)		Возвращает динамическую длину строки. Подобна функциям LEN в 
				Basic и strlen — в C/C++
    Pos(substring, s)	Возвращает место первого вхождения подстроки 
				substring в строку s. Подобна функциям SUBSTR в Basic и 
				strstr) — в C/C++ 
    SetLength (s,		Задает новую динамическую длину newlen строковой переменной s 
    newlen) 
    SetString		Задает содержимое и длину строки 
    Str (x, s)		Преобразует численное значение х в строковую переменную s 
    StringOfChars		Возвращает строку с конкретным числом символов 
    UniqueString		Делает данную строку уникальной со счетом обращений 1 
    Val(s, v, code)	Преобразует строку s в соответствующее численное 
				представление v 


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

    На элементарном уровне наиболее полезными типами данных являются те, в 
которых содержится численная и строковая (символьная) информация. Объединив 
несколько образцов этих элементарных типов, можно создавать более сложные типы 
данных. 
    Структурные типы данных предоставляют возможность создавать новые типы, 
расширяя определения уже существующих таким образом, чтобы данные новых типов 
могли содержать более одного значения. Элементами данных структурных типов 
можно манипулировать как поодиночке, так и вместе, и эти элементы сами могут 
быть структурными. Никаких ограничений на подобное вложение одной структуры в 
другую не существует. 
    Ниже перечислены структурные типы, определенные в Delphi. 
    •	Записи 
    •	Массивы 
    •	Множества 
    •	Файлы 
    •	Классы 
    •	Указатели на классы 
    Перечисленные типы сами по себе обычно являются не типами, а структурными 
методами дополнения существующих типов. Структурные типы классов и указателей 
на классы рассматриваются в главе 4, "Object Pascal: углубленное изложение". 

    64 Часть I. Основные положения 


    Записи 

    На заметку 
    С помощью зарезервированного слова record (запись) в одном типе можно 
объединять данные разных типов. Общий синтаксис объявления этого типа выглядит 
следующим образом: 

    record 
      fieldnamel : fieldtypel; 
      fieldname2, fieldname3 : fieldtype2; 
      case optional_tagfield : required_ordinal_type of 
      1: variantnamel : varianttype3; 
      2, 3:   variantname2 : varianttype4; 
    end; 

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

    Фиксированные записи 

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

    record 
      fieldnamel : fieldtypel; 
      fieldname2, fieldname3 : fieldtype2; 
    end; 

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

    MyReс.Fieldnamel 

    Для доступа ко всей записи просто укажите ее имя. 

    На заметку
    В языке С эквивалентом фиксированного типа record из Delphi является 
struct. В C++ также определен тип struct, синтаксис которого совместим с типом 
struct из С. Однако в C++ этот тип имеет дополнительные особенности, благодаря 
чему напоминает тип Class из Delphi. 

    Вариантные записи 

    Вариантная часть типа record дает возможность по-разному трактовать 
область памяти, совместно занимаемую вариантами поля: 

    record 
      case optional_tagfield :  required_ordinal_type of 
      1:   variantnamel : varianttypeS; 
      2,   3:   variantname2 : varianttype4; 
    end; 


    На заметку
    Термин вариантный в отношении записей не имеет ничего общего с типом 
Variant, который мы рассмотрим в следующем разделе данной главы. Вариантные 
поля, несмотря на свое название, никогда не имеют тип Variant. Объявление 
этого типа в любом месте вариантной части записи запрещено. 

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

    Глава 3. Object Pascal: основные понятия	65 
    3 Использование Delphi 3. Специальное издание 


    Нз заметку 
    В С и C++ эквивалентом вариантному типу записи из Delphi является тип union. 


    Массивы 

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

    array [ordinal_type] of type_definition; 
    array [ordinal_typel, ordinal_type2] of type_definition; 

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

    var MyArray: Array [1..10] of  Integer; 

    Тогда обращение к его третьему элементу будет выглядеть, как MyArray [ 3 ], 
и выполняться, как к переменной Integer. 

    На заметку
    Понятие массива существует в большинстве языков программирования, однако синтаксис, 
как правило, в каждом случае свой. В языках Basic и FORTRAN вместо квадратных скобок 
применяются круглые. В С и C++ элементы массива нумеруются с нуля, в FORTRAN — с единицы. 
В Basic нумерация начинается с нуля или единицы, в зависимости от результата последнего 
выполнения оператора OPTION BASE. В некоторых версиях Basic можно задавать верхнее и 
нижнее значения индексов, как это делается в Delphi. 

    На заметку
    В С и C++ обращение к массиву эквивалентно обращению к его первому (нулевому) элементу. 
В Delphi это будёт обращением ко всему массиву.


    Множества 

    Зарезервированное слово set (множество) определяет множество не более чем 
из 256 порядковых значений: 

    Set of ordinal_type 

    Минимальный и максимальный порядковые номера исходного типа (на основе 
которого определяется множественный тип) должны быть в пределах между 0 и 255. 
Переменная множественного типа содержит (или не содержит) любое значение 
исходного порядкового типа. Каждое значение из заданного диапазона может 
принадлежать или не принадлежать множеству. Рассмотри м следующий пример.

    Type  CharSet  =  set  of AnsiChar;  	//  Тип множества  символов ANSI. 
    var MyAlphaSet : CharSet;			//  Переменная  типа  CharSet. 

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

    MyAlphaSet := ['А', 'Е', 'I', 'О', 'U', 'Y']; //  Все прописные гласные. 

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

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

    66	Часть I. Основные положения 


    Файловый тип 

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

    file of Typel //  Файл  определенного  типа,   содержащий 
    			//  записи фиксированной  длины. 
    file		//  Файл  без  типа или  "блочный". 
    textfile     	//  Файл  с  записями  переменной длины,   разделенными 
    			//  символами  CR и  LF   ("возврат  каретки"  и  "новая 
    			//  строка") . 


    На заметку
    Механизм ввода-вывода информации как никакой другой аспект 
программирования зависит от языка и реализации. В большинстве случаев 
предполагается, что программисту незачем вникать во внутреннюю структуру
переменных, управляющих вводом-выводом, и при передаче информации следует 
полностью полагаться на предназначенные для этого процедуры. Их реализация 
должна оставаться чем-то наподобие черной магии. В Basic файлы обозначаются 
числовыми значениями — дескрипторами. В C/C++ программисты манипулируют
указателями на структуру FILE. И только в Delphi файловая структура — это переменная. 

    Указательные типы 

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

    pointer  //  Указатель   без  типа, 
    ^typel   //  Указатель   с  типом. 

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

    Таблица 3.8. Средства работы с указателями 

    Средство	Описание 

    New		Распределяет новый участок динамической памяти и записывает его адрес 
			в переменную указательного типа 
    Оператор @	Направляет переменную-указатель на область памяти, содержащую 
			любую существующую переменную, процедуру или функцию, включая 
			переменные, имеющие идентификаторы 
    GetMem		Создает новую динамическую переменную заданного объема и записывает 
			ее адрес в переменную указательного типа 

    Указатели и адресные функции 

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

    Функция	Описание 

    Addr		Возвращает адрес указанного объекта 
    Assigneed	Проверяет, равно ли значение процедурной функции Nil 
    Ptr		Преобразует адрес в указатель 

    Зарезервированное слово Nil указывает значение указателя, который ни на 
что не указывает. Такие указатели называют неопределенными. В Object Pascal 
только при определении указателей можно нарушать правило, по которому все 
указываемые идентификаторы, в том числе идентификаторы типов должны быть 
объявлены выше. Здесь можно указать идентификатор еще необъявленного типа, 
как в следующем примере: 

    type 
      PointerType = ^NotYetDefinedType; 


    Глава 3. Object Pascal: основные понятия	67 

    Однако необъявленный тип необходимо объявить ниже в том же блоке 
объявления типов. 
    Определенный в Object Pascal тип Pointer— это указатель без типа. 
Обратиться к переменной через такой указатель невозможно (к переменной типа 
Pointer нельзя дописывать символ "^"). Однако можно задать ей другой 
указательный тип. 
    По значениям переменных тип Pointer совместим с остальными указательными типами. 

    На заметку
    Во многих языках указательные типы как таковые отсутствуют. Однако в С и 
C++ они есть и определяются звездочкой перед типом объявляемой переменной. 
Указатели в C/C++ трактуются наподобие целых переменных. Программисты 
Delphi избегают подобного манипулирования указателями. 


    Процедурные типы 

    Указатели удобны для записи информации заданного типа и обращения к 
переменным. Однако записывать адреса процедур и функций довольно опасно. 
Программистам, работавшим с ранней версией Turbo Pascal, приходилось 
основательно изучать способы размещения процедур и функций в памяти и 
обращения к ним. Процедурные типы существенно упрощают эту проблему, позволяя 
трактовать процедуры и функции как значения, которыё можно присваивать 
переменным и передавать в качестве параметров. 
    Объявление процедурного типа подобно объявлению заголовка процедуры или 
функции. Единственная разница состоит в том, что опускается имя, следующее 
обычно после ключевых слов procedure и function. 
    Вне типа Class в Object Pascal разрешены только глобальные процедурные 
переменные. Это означает, что процедурной переменной не может быть присвоена 
процедура или фyнкция, объявленная внутри другой процедуры или функции. 
    Кроме того, в Delphi можно применять данные процедурных типов, 
представляющих собой методы. При выполнении программы можно обращаться к 
определенным методам определенных переменных-объектов. Использование методов в 
качестве данных процедурных типов позволяет модифицировать поведение 
переменной-объекта некоторого класса, не объявляя нового класса с новыми методами.
    В Delphi указатели процедурного типа на методы применяются для 
сопоставления событий с образцами текста программы. Например, в начале этой 
главы приведен пример, когда при создании формы был вызван метод TForml.FormlCreate. 
    С точки зрения синтаксиса, единственное отличие процедурного типа для 
метода от обычного процедурного типа состоит в фразе of object, следующей за 
прототипом процедуры или функции в случае метода. Особым образом применяется в 
процедурных методах указательное значение Nil. Это единственное указательное 
значение, которое можно присвоить процедурной переменной. После 
присвоения такого значения процедурная переменная становится неопределенной. 
Состояние определенности можно проверить с помощью функции Assigned. 
    Глобальные процедурные типы и процедурные типы для методов взаимно 
несовместимы. Нельзя присваивать значение одного типа переменной другого. 

    На заметку
    Физически процедурные типы в Delphi совпадают с указательными, однако они 
различаются синтаксически, а поэтому нельзя обращаться к функции или процедуре 
через указатель. Тем не менее при обращении к процедурной переменной 
задействуется именно значение указательного типа. В C/C++ переменная может иметь 
тип указателя на функцию. В версиях языка С до введения стандарта ANSI для 
обращения к соответствующей функции приходилось явно адресовать указатель. В версиях 
С, соответствующих стандартам ANSI, возможны как явная, так и неявная адресации 
указателей. Delphi удобна для тех, кому близок стиль ANSI. 


    Тип Variant 

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

    Вариантные значения 

    При рассмотрении типа Record мы ознакомились с вариантной частью записи, 
где в одном фрагменте памяти можно хранить информацию нескольких типов. Такой 
метод недостаточно нагляден. Много ли пользы от того, чтобы найти в памяти 
действительное значение с фиксированной запятой и интерпретировать его, как 
целое! Тип Variant (не имеющий ничего общего с вариантной частью записи) более 
"проворен" и полезен в управлении данными разных типов. Переменным типа 
Variant можно присваивать любые значения любых целых, действительных, 
строковых и булевых типов. Для совместимости с другими языками 
программирования предусмотрена также возможность присвоения этим перемен-

    68	Часть I. Основные положения 

ным значений даты/времени и объектов OLE Automation. Кроме того, 
вариантные переменные могут содержать массивы переменной длины и размерности 
с элементами указанных типов.
    Все целые, действительные, строковые, символьные и булевы типы совместимы с 
типом Variant в отношении операции присваивания. Вариантные переменные можно 
сочетать в выражениях с целыми, действительными, строковыми, символьными и 
булевыми; при этом все необходимые преобразования Delphi выполняет 
автоматически. Можно произвольно задавать для выражений тип Variant в форме 
Variant(X). 
    В Object Pascal определены два особых значения Variant. Значение 
Unassigned применяется для указания, что вариантной переменной пока не 
присвоено значение какого бы то ни было типа. Значение Null указывает на 
наличие в переменной данных неизвестного типа или потерю данных. Разницу между 
этими двумя значениями трудно уловить. Значение Unassigned присваивается 
вариантным переменным автоматически при их создании, независимо оттого, 
локальная это переменная или глобальная, и является ли она частью другой, 
структурной, переменной, такой как запись или массив. Unassigned означает, что 
к данной вариантной переменной еще не обращались. Null же означает, что к 
вариантной переменной обращались, но не ввели в нее никакой информации. Таким 
образом, Null указывает, что значение вариантной переменной недействительно 
или отсутствует. 
    Вариантные переменные предоставляют широкие возможности формирования 
выражений с переменными разных типов. Однако за это приходится платить 
большим, по сравнению с жестко задаваемыми типами, расходом памяти. К тому же 
на выполнение операций с вариантными переменными требуется больше времени. 
    Интересна проблема использования вариантной переменной как массива. 
Элементы этого массива должны быть одного типа. На первый взгляд, это вполне 
естественное условие. Однако элементам массива можно присвоить и тип Variant! 
Тогда каждый элемент сможет содержать информацию разных типов, в том числе 
массив Variant. Как правило, вариантные массивы создаются с помощью процедуры 
VarArrayCreate. 
    Для передачи двоичной информации между контроллерами автоматизации OLE и 
серверами обычно применяются вариантные массивы с элементами varByte. 
Вариантные массивы типа varByte не могут подвергаться никаким преобразованиям. 
Нельзя также переформатировать содержащуюся в них двоичную информацию. 
Эффективный доступ к ним осуществляется с помощью процедур VarArrayLock и 
VarArrayUnlock. 

    На заметку
    Элементы вариантного массива не могут иметь тип varString. Для создания 
вариантных массивов со строковыми элементами следует выбрать тип varOleStr. 


    Процедуры обработки вариантных массивов 

    В табл. 3.9 перечислены стандартные процедуры и функции обработки 
вариантных массивов, определенные в модуле System. 

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

    Процедура/функция	Описание 

    VarArrayCreate	Создает вариантный массив с заданными пределами и типом 
    VarArrayDimCount	Возвращает число измерений данного вариантного массива 
    VarArrayHighBound	Возвращает верхний предел измерения вариантного массива 
    VarArrayLock		Фиксирует вариантный массив 
    VarArrayLowBound	Возвращает нижний предел измерения вариантного массива 
    VarArrayOf		Возвращает вариантный массив с указанными элементами 
    VarArrayRedim		Изменяет верхний предел вариантного массива 
    VarArrayUnlock	Отменяет фиксацию вариантного массива      
    Var As Type		Преобразует вариантную переменную в указанный тип        
    VarCast			Преобразует вариантную переменную в указанный тип и записывает значение 
    VarClear		Сбрасывает значение вариантной переменной 
    VarCopy			Копирует одну вариантную переменную в другую 
    VarFromDateTime	Возвращает вариантную переменную, содержащую переменную даты/времени 


    Глава 3. Object Pascal: основные понятия	69 


    Окончание табл. 3.9 

    Процедура/функция	Описание

    VarlsArray		Возврашает True, если вариантная переменная является массивом 
    VarlsEmpty          Возвращает True, если вариантная переменная содержит Unassigned 
    VarlsNull           Возвращает True, если вариантная переменная содержит Null 
    VarToDateTime		Преобразует вариантную переменную в значение даты/времени 
    VarType  		Преобразует вариантную переменную в указанный тип и записывает 
				значение 


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

    Таблица 3.10. Вариантные типы 

    Тип выражения				Вариантный тип 

    Целый					varlnteger 
    Действительный, кроме Currency  varDouble 
    Currency  				varCurrency 
    Строковый и символьный          varString 
    Булев                           varBoolean 

    Вариантные переменные в отношении операции присвоения совместимы с 
элементарными типами данных Object Pascal (Integer, Real, String и Boolean). 
Все нужные преобразования Delphi выполняет автоматически. При необходимости 
конкретно указать, что вариантное значение надо интерпретировать как целое, 
действительное, строковое или булево, следует задать тип в форме TypeName(V), 
где TypeName — идентификатор соответствующего типа, V — выражение Variant. 
Задание типа изменяет только способ считывания значения из вариантной 
переменной, а не само значение внутри ее. Внутреннее же представление 
изменяется с помощью процедур VarAsType и VarCast. 

    OLE Automation 

    Вариантные переменные удобно применять для изменения свойств объектов OLE 
Automation и вызова методов этого объекта. Чтобы инициировать эту возможность, 
необходимо подключить модуль OleAuto. 
    Синтаксис вызова метода или обращения к свойству объекта OLE Automation 
такой же, как вызова из созданного класса. Есть, однако, несколько важных 
отличий. Во-первых, вызов метода объекта OLE Automation происходит по схеме 
позднего связывания, т.е. компилятор не проверяет, существует ли данный метод 
и правильно ли определены типы параметров. Для компилятора приемлемы любой 
идентификатор метода и любое число параметров разных типов. А это означает, 
что при выполнении вызванного таким образом метода может произойти ошибка. 
    Что же касается идентификаторов методов объекта OLE Automation, то они 
могут содержать любые алфавитные символы из международного набора, в том числе 
а, ь и ш. 

    Операции и выражения 

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

    70 Часть I. Основные положения 


    Таблица 3.11. Операции Object Pascal (в порядке приоритета) 

    Операция				Категория 

    .^    					Обращение к полю, к переменной через указатель (высший приоритет) 
    @ not					Операции с одним операндом 
    * / div mod as and shl shr	Операции умножения, деления и задания типа__   
    + - or xor     			Операции сложения (конъюнкции) 
    = <><><=>= in is			Операции сравнения (отношения, принадлежности множеству и 
						сравнения типов) (низший приоритет)

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

    + (сложение), - (вычитание), * (умножение), 
    / (деление с плавающей запятой), 
    div (целочисленное деление), mod (остаток целочисленного деления) 

    Для работы со строками предназначена только одна операция (возвращающая 
строковое значение): 

    + (последовательное соединение) 

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

    = (равно), о (не равно), < (меньше), > (больше), 
    <= (меньше или равно), >= (больше или равно) 


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

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

    Побитовая операция       Описание 

    not			Инверсия битов: 1 меняется на 0, 0 — на 1 
    and			Побитовое И. I and J возвращает значение, в котором 
				установлены только биты, равные 1 одновременно в I и J 
    or			Побитовое ИЛИ. I or J возвращает значение, в котором сброшены только 
				биты, равные 0 одновременно в I и J 
    xor			Побитовое исключающее ИЛИ. I and J возвращает значение, в котором 
				установлены биты, значения которых в I и J различны 
    shl			Смещение побитовое влево. I shl J возвращает значение I, смещенное на 
				J бит влево. Аналогично умножению на 2 J раз 
    shr			Смещение побитовое вправо. I shl J возвращает значение I, смещенное на 
				J бит вправо. Аналогично делению на 2 J раз 

    Слова not, and, or и xor обозначают также булевы операции. 


    Глава 3. Object Pascal: основные понятия	71 


    На заметку 
    Разработчикам, использующим Object Pascal, которые раньше работали с 
другими языками, необходима особая внимательность при интерпретации некоторых 
операций. В С и C++ звездочкой (*) обозначается операция адресации через 
указатель, а косой чертой ("/") — целочисленное деление (в Delphi — "div"). В 
Basic "^" — это знак возведения в степень. 


    Операторы 

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

    простые операторы; 
    структурные операторы. 

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

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

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

    операторы присваивания (: =); 
    процедурные операторы; 
    операторы перехода (GoTo). 

    Существует еще более простой оператор, именуемый нулевым или пустым. Он не 
выполняет никаких действий, тем не менее это вполне реальный (в отношении 
синтаксиса языка Object Pascal) оператор. Вы узнаете, что это такое, случайно 
наткнувшись на него в цикле. 
    Оператор перехода GoTo — это, в какой-то степени, недоразумение. Он 
существует и существовал всегда. Однако применение этого оператора никогда не 
поощрялось, и многие профессиональные программисты ни разу не использовали его 
в программах языка Pascal. Оператор GoTo совершенно изменяет структуру 
исходного текста программы. Поэтому тем, кому до сих пор удавалось обходиться 
без этого оператора, лучше не учиться с ним работать. Лучше потратить время на 
изучение двух других разновидностей простых операторов. 
    Эти разновидности являются фундаментом написания эффективных программ. На 
самом нижнем уровне программирования не произойдет ничего, пока не изменится 
значение какой-либо переменной. Такое изменение можно выполнить 
непосредственно, с помощью оператора присваивания, либо опосредованно — путем 
вызова процедуры. Все структурные операторы могут вычислять довольно сложные 
выражения, однако выполнение программы не даст никакого результата, если в 
этом структурном операторе нет оператора присваивания или вызова процедуры. 

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

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

    target := expression; 

    Выражение (expression) обычно вычисляется независимо от объема и типа 
переменной target, однако, для того чтобы этот оператор был действующим, типы 
target и expression должны быть совместимыми. Правила совместимости в 
операторе присваивания довольно сложны. Две переменные одного типа обычно 
совместимы, файловая же переменная несовместима ни с одной структурой, 
содержащей файловое поле или элемент. Действительным переменным можно 
присваивать целые значения, а целым переменным действительные значения — 
нельзя. Совместимы короткие и длинные строки. Строке можно присвоить 
символьное значение, но не наоборот. Опять же, строке можно присвоить 
символьный массив (упакованную строку), но и здесь обратное присвоение 
запрещено. Строку с нуль-окончанием можно присвоить переменной AnsiString 
(длинная строка), но не наоборот. Переменной PChar можно присвоить значение 
строковой постоянной; обратное присвоение лишено смысла. Той же переменной 
PChar можно присвоить значение массива символов с первым индексом 0, и, опять 
же, не наоборот. Переменные и выражения Variant взаимно совместимы с целыми, 
действительными, строковыми и булевыми переменными. 

    72 Часть I. Основные положения 

    На заметку
    В С и C++ присваивания не являются операторами, а входят в выражения, так 
что они могут появляться в любом месте где допустимы выражения. Delphi запpeщaeт 
размещение присваиваний в тех местах, где они разрешены в C/C++ (например, в условии 
оператора if. . .then. . .), или попытки умножения присваиваний в одном выражении. 


    Оператор вызова 

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

    procname(parml, parm2) 

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

    Оператор перехода 

    Оператор перехода GoTo передает выполнение программы оператору, 
помеченному указанной меткой. В классическом Pascal метки — это числа от 0 до 
9999 (ведущие нули игнорируются). В Object Pascal в качестве меток разрешены 
идентификаторы: 

    GoTo labell; 

    Метка должна быть определена в разделе меток того блока, в котором 
выполняется оператор GoTo. Помеченный меткой оператор должен находиться в том 
же блоке. Нельзя переходить из одной процедуры в другую. 
    Хороший стиль программирования предполагает минимальное применение 
оператора перехода. В языках структурного программирования этот оператор 
вообще никогда не был нужен. С явлением народу операторов прерывания цикла 
(break) и выхода из_процедуры (exit) оператор GoTo стал лишним и в Object 
Pascal.

    На заметку 
    В большинстве языков программирования, в том числе в Basic и C/C++, 
оператор перехода существует, но его применение не поощряется. В Object 
Pascal, однако, использовать этот оператор удобнее, чем в остальных. 


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

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


    Составной оператор (begin...end) 

    Составной оператор указывает порядок, в котором должны выполняться 
содержащиеся в нем операторы. Когда последовательность операторов заключается 
в тело begin...end, она рассматривается как один оператор, к которому можно 
обращаться по правилам синтаксиса Object Pascal: 

    Begin 
      statement1; 
      statement2; 
    end 


    Глава 3. Object Pascal: основные понятия	73 

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

     На заметку
     В С и C++ точка с запятой — это, скорее, символ конца оператора, а 
не разделитель, как в Object Pascal. В случае составного оператора, 
в котором за последним оператором может стоять точка с запятой, разница 
неочевидна. Более отчетливо она проявляется в операторе if, где Object 
Pascal запрещает использование точки с запятой перед альтернативной 
частью else. 

     На заметку
    В С и C++ для объединения операторов часто применяются 
фигурные скобки ({}). Следовательно, при переходе от одного языка к 
другому лучше приравнять левую фигурную скобку в С к begin в Object Pascal 
правую — к end. Однако эти символы не вполне эквивалентны. В Object Pascal пара 
begin...end содержит тoлько выполняемые oпepaтopы. В C/C++ в фигурные 
скобки могут заключаться как выполняемые операторы, так и объявления 
данных. Таким образом, объединение операторов в C/C++ больше напоминает то,
что в Object Pascal именуется синтаксическим блоком, в котором объявления 
данных заканчиваются одним end. В C/C++ конструкции, заключенные в фигурные 
скобки, именуются блочными операторами. 

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

    Условный оператор определяет, следует ли выполнять другие операторы. 
Сначала проверяется некоторое условие (или предикат), а затем, в случае 
выполнения этого условия, выполняется следующий за ним оператор. 
    В Object Pascal определены два вида условных операторов: if и case. В 
операторе if происходит выбор одного из двух вариантов, в case — из нескольких. 

    Оператор if...then 

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

    if boolean_axpression  then 
       statementl;  //  Выполнять либо не выполнять оператор 
                     //  statementl. 

    if boolean_expression then 
      statementl   
    else
      statement2;      //  Выполнить   statementl   или  statement2. 

    Если булево выражение, следующее за if, возвращает True, выполняется 
оператор, следующий за then. Если же булево выражение возвращает False и 
присутствует часть else, выполняется оператор, следующий за else. При 
отсутствии части else выполняется оператор, следующий за оператором if. Точка 
с запятой перед else не допускается. 

    Логические и функциональные условия

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

    Булева алгебра 

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

    Оператор	Операция 

    Not	Отрицание. Обращает булево значение. Not True = False. Операция с 
		одним операндом 
    And	Логическое И. X and Y = True, если X = True и Y = True 
    Or	Логическое ИЛИ. X or Y = True, если X = True или Y = True 
    Xor	Исключающее ИЛИ. X xor Y = True, если только X или только Y равно True 

    Операторы case...of 

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

    74	Часть I. Основные положения 

ного оператора, которому предшествует слово else и который выполняется, 
если ни одна постоянная не оказывается равной селектору. 

    case ordinal_expression  of    //  В  случае  селектора,   равного 
      1: statement1;               //  константе:   оператор; 
      2,3:  statement2;            //   константе:   оператор; 
    else  catchallstatement;	//   иначе   оператор; 
    end;//  Конец 

    Селектор может быть только порядковым выражением. Действительные и 
строковые выражения в качестве селектора недопустимы. 
    Case-константы должны иметь тот же порядковый тип, что и селектор, и не 
совпадать один с другим. Выполняется только оператор, которому предшествует 
постоянная, равная значению селектора. Если селектор не равен ни одной 
константе, выполняется оператор, следующий за else. Если часть else 
отсутствует, выполняется оператор, следующий за оператором case. 

    На заметку 
    Аналогом оператора case в С и C++ является оператор switch. Между ними 
существует важное различие, состоящее в том, что switch представляет 
собой множество точек входа в последовательность операторов,  а не 
последовательность операторов, из которых выполняется один. Для разбивки 
switch-операторов на альтернативные в C/C++ применяется оператор break.

    На заметку 
    Оператор SELECT CASE из Basic совместим с case из Delphi. 


    Циклы 

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

    for...to/downto...do 
    while...do 
    repeat...until 

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

    Break 		Прерывает цикл for, while или repeat, выполняя переход к оператору, 
			следующему непосредственно за циклом 
    Continue 	Переходит к началу цикла и выполняет его еще раз 


    На заметку 
    Оператор break в С и C++ аналогичен процедуре Break из Delphi. Кроме того, 
в C/C++ оператором break заканчивается ветвление в операторе switch. 

    На заметку 
    В Basic операторы EXIT FOR и EXIT DO подобны процедуре Break из Delphi. 


    Цикл For...Do 

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

    For controle_variable := initial_value To  final_value  Do  statement; 
    For controle_variable := initial_value Downto final_value Do  statement; 

    Управляющая переменная (controle_variable) всегда отсчитывается от 
начального значения (initial_value). В цикле с То, чтобы оператор был 
выполнен, управляющая переменная должна быть не больше конечного значения, при 
этом после каждого выполнения она возрастает на 1. В цикле с Downto 
управляющая переменная должна быть не меньше конечного значения, и после 
каждого выполнения она уменьшается на 1. 

    Глава 3. Object Pascal: основные понятия 75 

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

    На заметку
    В Delphi скорость выполнения цикла For...Do оптимизирована, и отсюда 
некоторые ограничения на управляющую переменную. В других языках эта 
конструкция более гибка. Операторам DO из FORTRAN, FOR — из Basic и 
for — из C/C++ в Delphi соответствует цикл While. . . Do. 


    Цикл Repeat...Until 

    Операторы, расположенные между Repeat и Until, последовательно выполняются 
до тех пор, пока Until не возвратит True. 

    Repeat 
      statement1; 
      statement2; 
    Until boolean_expression; 

    Эта конструкция не характерна для Object Pascal, поскольку здесь для 
выполнения последовательности операторов нет необходимости заключать их в 
операторные скобки (begin...end). 
    Последовательность операторов между Repeat и Until выполняется минимум 
один раз, поскольку только после этого впервые вычисляется булево выражение. 

    На заметку
    В С и C++ есть оператор цикла do... while (...}, подобный Repeat...Until 
из Delphi, но отличающийся противоположным толкованием условия.


    While...Do... 

    Оператор While также предназначен для многократного выполнения оператора: 

    While boolean_expression Do statement; 

    В конструкции While оператор, расположенный после Do, выполняется до тех 
пор, пока булево выражение после While возвращает True. Булево выражение 
вычисляется до выполнения оператора. Если выражение с самого начала равно 
False, оператор не выполняется вовсе. 

    На заметку 
    В С и C++ оператор while (...)... эквивалентен оператору цикла While...Do 
из Delphi. 

    На заметку 
    В Basic оператор WHILE...WEND эквивалентен оператору цикла While...Do из Delphi. 


    Оператор With...Do 

    Этот оператор обеспечивает ускоренный доступ к полям записей и членам объектов: 

    With record_variable Do statement; 

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

    var arecord : record  fieldl: integer; field2 : integer; end; 

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

    arecord.fieldl:=1; arecord.fieId2:=2; 
    with arecord do begin fieldl:=1; field2:=2; end; 


    76	Часть I. Основные положения 

    Здесь требуется некоторая осторожность. Оператор With — это ускоренный 
способ указания, какой записи принадлежит поле. Однако эта конструкция не 
расширяется, как макрокоманда. Если обращение к записи включает индексирование 
элементов массива или адресацию через указатель, то такая операция будет 
выполнена только один раз. В операторе with идентификатору поля может быть 
сопоставлена только одна запись.	~ 

    На заметку 
    В других языках не существует оператора, точно соответствующего оператору 
With...Do. 


    Процедуры и функции 

    В начале данной главы указывалось, что программа состоит из заголовка и 
блока. Процедуры представляют собой дополнительные блоки, вставленные в 
программный блок. Как и программа, процедура состоит из заголовка и следующего 
за ним блока. 
    Заголовок процедуры состоит из зарезервированного слова procedure, 
идентификатора и, возможно, формальных параметров (перечисляемых в скобках). 
Процедура запускается оператором вызова, состоящим из идентификатора процедуры 
с фактическими параметрами. 
    Блок процедуры состоит из следующих разделов. 
    В разделе объявлений объявляются локальные постоянные, типы, переменные, 
процедуры и функции. 
    Операторы, заключенные в составной оператор (begin. . .end), 
последовательно выполняются после запуска процедуры оператором вызова. 
    Функции, как и процедуры, применяются для размещения дополнительных блоков 
внутри основного программного блока. Однако функция после выполнения 
операторов возвращает значение, используемое для вычисления выражения, из 
которого она вызывалась. Объявления функции состоят из заголовка функции и 
блока функции. Заголовок функции состоит из зарезервированного слова 
function, имени функции, необязательных формальных параметров и типа 
результата. Тип результата функции может быть любым, за исключением файловых 
или структурных, включающих файловое поле или элементы, вложенные в структуру 
на любую глубину. 
    Блок функции состоит из объявлений локальных меток, постоянных, типов, 
переменных, процедур и функций, а также одного составного оператора 
(begin...end), в котором собраны операторы, выполняемые после вызова функции 
из выражения. 
    Составной оператор блока функции должен содержать минимум один оператор, в 
котором присваивается значение идентификатору функции или идентификатору 
result (результат). 
    Идентификатор result неявно определен в составном операторе каждой функции 
и имеет тот же тип, что и возвращаемое значение. Присвоение значения result 
равносильно его присвоению идентификатору функции. Однако, обратившись к 
идентификатору функции, нельзя провёрить её последний результат (попытка 
сделать это приведет к вызову функции). Смысл переменной result в том и 
состоит, чтобы, с ее помощью в любой момент получить текущее значение функции, 
находясь внутри ее. После выполнения функции последнее присвоенное таким 
образом значение и будет результатом, возвращаемым функцией выражению. 
    Выполнение функции происходит после ее вызова во время вычисления 
выражения. Вызов обозначается именем функции с указанием фактических 
параметров (если таковые имеются) в скобках после имени функции, как в 
следующем примере. 

   function  funcid(parameters) : typeid; //  Заголовок функции, const  
   constid = consttant_expression;	       //  Все,   что  следует  за 
   type  typeid = type_definition;        //  заголовком,   — это  блок 
   var : varl,  var2: type_definition;    //  функции. 
       // Локальные  константы, 
       //  типы,   переменные,   процедуры 
       //  и функции можно  объявлять   в 
       //  любом порядке, 
   procedure proc1; 
   procedure_definition; 
   function  funcl : typeid; 
   function_ definition; 
   begin 
     statements; 
     result: = typeid_expression; 
   end; 


    Глава 3. Object Pascal: основные понятия 77 


    Область видимости идентификатора 

    На первый взгляд, назначение процедур и функций состоит в объединении 
групп операторов, чтобы затем можно бы давать команду на их выполнение из 
любого места программы. Однако существенное влияние на значение идентификатор: 
том или ином месте программы (область видимости идентификаторов) оказывает 
вложение одних процедур в другие. 
    В Object Pascal определить область видимости очень непросто. Локальные 
идентификаторы видны только в том блок в котором они объявлены. Однако в 
программе или модуле может быть несколько уровней "локальности". Единственный
способ получить нечто, что можно было бы назвать глобальным идентификатором, — 
объявить его в интерфейсной части модуля. Такой глобальный идентификатор будет 
виден всем определениям этого модуля. Причем идентификаторы, объявленные в 
интерфейсе модуля Unit А, становятся видимыми для программы (или другого 
модуля), к которой данны модуль подключен с помощью условия uses. 
    Идентификатор виден только ниже его объявления и только в том блоке, в 
котором он объявлен. Внутри блока можно переобъявить любой идентификатор, 
объявленный вне его. Новое толкование идентификатора действует от его 
объявления до конца блока. 

    Передача параметров 

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

    Передача параметров по значению 

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

    parml, parm2 : atype;   // Параметры одного  типа,   передаваемые 
                            // по значению. 

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

    Передача по ссылке 

    Если формальный параметр помечен зарезервированным словом var, то в 
качестве фактического параметра передается переменная. По существу, передается 
лишь адрес фактического параметра, в вызванной же процедуре доступ к 
формальному параметру заменяется доступом к фактическому. Все изменения 
формального параметра тут же отражаются на фактическом, ибо оба они 
представлены одной и той же переменной!                                        
                                                                              
    var parml, parm2 : atype;  // Параметры одного типа, 
                               // передаваемые по  ссылке. 

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

    Const 

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

    const parml, parm2 : atype;  // Параметры-постоянные одного типа. 

    Внутри подпрограммы изменять значение параметра const нельзя. Нельзя также 
передавать его в качестве параметра var другой подпрограмме. 

    78     Часть I. Основные положения 

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

    Параметры без типа 

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

    var parml, parm2;      //  Параметр-переменная  без  типа, 
    const  parml, parm2;   //   Параметр-постоянная  без   типа. 

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

    Открытые массивы 

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

    var parml: array of atype;  // Открытый массив с типом. 

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

    var parml : array[0..N-1] of atype; 

    Здесь N — число элементов массива, являющегося фактическим параметром, 
т.е. диапазон значений индекса фактического параметра преобразуется в 0..N-1. 
Размерности первоначального массива не имеют значения. Фактический 
параметр-переменная типа atype трактуется как массив с одним элементом типа atype. 
    Имеется одно важное отличие от параметров, объявляемых обычным образом: к 
открытому массиву можно обращаться только поэлементно. Присвоения с указанием 
всего массива запрещены. 

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

    AverageThese([18.3, 19.8, 122.133, 3.43098V4]); 

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

    Таблица 3.12. Результаты обработки открытых массивов стандартными 
функциями

    Функция  Результат 

    Low(А)      Нуль 
    High(А)     Максимальный индекс фактического параметра 
    SizeOf(A)   Объем(в байтах) фактического массива 


    Глава 3. Object Pascal: основные понятия 79 


    Открытый массив array of const 

    Синтаксическая конструкция array of const дает возможность относительно 
надежно передавать параметр открытый массив значений более чем одного типа: 

    const parml : array of const; 

    Это средство позволяет написать подпрограмму, которой можно передавать 
любое количество параметров разных типов. Компилятор всегда трактует объявление 
array of const как эквивалент array of TVarRec. Тип TVarRec — особый тип записи, 
определенный в модуле System. 
    Внутри вызываемой подпрограммы TVarRec применяется для доступа к отдельным 
элементам массива array of const. Тип каждого параметра, передаваемого в 
массиве, записывается в поле TVarRec.VType. 

    Модули Delphi 

    Схема модуля имеет следующий вид. 

    unit unitid; // Заголовок модуля, 
    interface // Интерфейс модуля. 
    uses 
      unitid2, unitid3; 
    const 
      constid = constant_expression; 
    type 
      typeid = type_definition; 
    var 
      varl: type_definition; 
    procedure proc1;         // Для отображения в интерфейсе 
    function fund : typeid;  // процедур и функций указываются 
                             // только их заголовки, 
    implementation           // Реализация модуля. 
    uses
      unitid, unitid2, unitid3; 
    const  
      constid = constant_expression; 
    type 
       typeid = type_definition; 
    var 
      varl, var2 : type_definition; 
    procedure prod; 
    procedure_block; 
    function func1 : typeid; 
    function_block; 
    initialization // Инициализация модуля. 
      statements;  // Операторы, выполняемые до запуска 
                   // программы; процедура запуска, 
    finalization   // Деинициализация модуля. 
      statements;  // Операторы, выполняемые после завершения 
                   // программы; процедура выхода. 
    End.           // Конец модуля. 


    Интерфейс 

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

    80 Часть I. Основные положения 


    Реализация 

    В разделе реализации можно объявлять постоянные, типы, переменные, а также 
заголовки процедур и функций, доступ к которым возможен только из данного модуля. 
Кроме того, здесь размещаются блоки всех процедур и функций, объявленных в 
разделе интерфейса. 
    Все идентификаторы, объявленные в интерфейсе данного модуля, видны в его 
разделе реализации. (Раздел реализации как бы присоединяет собственный модуль 
с помощью условия uses). Объявления, содержащиеся в части реализации, видны только 
в данном модуле. 
    Порядок следования объявлений в интерфейсе не имеет для раздела реализации 
никакого значения. Процедуры и функции здесь могут следовать в любом порядке. 
    Если раздел реализации содержит условие uses, оно должно помещаться сразу 
после зарезервированного слова implementation, до любого другого объявления. 
Раздел реализации заканчивается зарезервированным словом realization или 
end. (с точкой). 

    Инициализация 

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

    Деинициализация 

    Раздел деинициализации необязателен. Более того, он возможен только при 
наличии раздела инициализации. Этот раздел состоит из зарезервированного слова 
finalization, за которым следуют операторы, выполняемые при завершении 
программы. Любые ресурсы (память, файлы и т.п.), распределенные или открытые в 
разделе инициализации, обычно освобождаются и закрываются в разделе 
деинициализации. Однако в этом же разделе можно освобождать и ресурсы, 
распределенные во время обычного выполнения программы. 
    Разделы деинициализации модулей выполняются в порядке, обратном выполнению 
инициализационных разделов, т.е. в порядке, обратном перечислению модулей в 
условии uses программы. 
    После инициализации выполняется основной код, а затем наступает 
деинициализация. Здесь опять требуется некоторая осторожность. Раздел 
деинициализации должен быть в состоянии обработать данные, инициализированные 
частично, что может произойти из-за возникновения исключительной ситуации во время 
инициализации. 

    Резюме 

    Используемый в Delphi язык Object Pascal предназначен для написания 
сложных программ на основе расширения простых понятий. В программах Object 
Pascal можно объявлять собственные данные, процедуры и функции и управлять 
всем, что происходит во время выполнения программы, с помощью 
одного-единственного составного оператора. 
    Основной строительный элемент программы Object Pascal — типы данных. На 
нижнем уровне программирования данные могут представлять собой числа, строки 
или конструируемые порядковые типы. С помощью записей можно создавать 
собственные типы данных. Большое количество образцов однотипных данных можно 
хранить в массиве. Обращение к данным можно осуществлять опосредованно, с 
помощью указателей. 
    Из данных можно составлять выражения. Object Pascal содержит элементарные 
бинарные (с двумя операндами) операции арифметических действий, 
последовательного соединения строк и логического сравнения. 
    Вычисление выражений контролируется благодаря структурному применению 
операторов. Простейшие операторы — оператор присваивания и вызовы процедур. 
Порядок выполнения операторов задается путем их помещения в составной оператор 
(begin...end). Можно обусловить выполнение оператора операторами if...then 
и case...end. С помощью операторов цикла for, while и repeat... until можно 
задавать многократное выполнение оператора. 
    Логически связанные операторы можно объединять в процедуры и функции. Во 
многих отношениях процедуры и функции подобны небольшим программам с 
собственными данными. Они могут выполняться как составные операторы и даже 
иметь собственные процедуры и функции, к которым нет доступа извне (из других 
частей программы). 
    Логически связанные типы данных, процедуры и функции могут объединяться в 
модули (unit), используемые затем разными программами. Модули подобны 
небольшим программам, однако разбиваются на разделы интерфейса (в котором 
указываются компоненты модуля, доступные другим модулям и программам), 
реализации, инициализации и деинициализации. 
    Конечно же, представленное в данной главе описание Object Pascal не дает 
полной картины того, что есть в Delphi. Здесь была затронута только 
классическая часть языка Pascal, а не то, что дало повод назвать его Object 
Pascal. Именно об объектном программировании пойдет речь в следующей главе, 
"Object Pascal: углубленное изложение". 

    Глава 3. Object Pascal: основные понятия 81 
