| { $DEFINE variant0 }
| BEGIN
| {$IFDEF variant0 }
| WriteLn ( 'Вариант программы номер 0' );
| {$ENDIF)
| {$IFNDEF variant0 }
| WriteLn ( 'Ненулевая версия программы' );
| {$ENDIF}
| END.
Рис. 3.3
- 58 -
Если в тексте программы определено ключевое слово (здесь variant0), то будет откомпилирован блок, зависящий от активности этого слова, т.е. заключенный между ключами {$IFDEF variant0} и {$ENDIF}. Альтернативный вариант блока будет компилироваться только, когда ключевое слово variant0 неопределено (пассивно). На это указывают обрамляющие его ключи {$IFNDEF variant0}...{$ENDIF}. Но если, например, изменить в тексте ключа $DEFINE слово variant0 на variant1 и заново откомпилировать программу, то все получится наоборот: будет пропущен первый блок (его слово не определено), но откомпилирован второй (условие отсутствия слова выполняется).
Можно заметить, что обязательная директива {$ENDIF} всегда замыкает блок, начатый ключом {$IF...}. Пример на рис. 3.3 можно без ущерба для смысла переписать в ином виде (рис. 3.4).
| { SDEFINE variant0}
| BEGIN
| {$IFDEF variant0 }
| WriteLn ( 'Вариант программы номер 0');
| {$ELSE}
| WriteLn ('Ненулевая версия программы');
| {$ENDIF}
| END.
Рис. 3.4
Здесь задействован ключ {$ELSE}, направляющий ход компиляции в альтернативное русло, если не выполняется условие предшествующего ключа {$IF...}. Для такого сложного условия ключ конца все равно будет один.
Блоки, компилируемые условно, могут содержать любое число операторов. Части программы, находящиеся вне блоков условной компиляции {$IF...}...{$ENDIF}, никак не зависят от ключевых слов. Само ключевое слово может содержать сколько угодно символов (только латинских и цифр), хотя распознаются только первые 63 из них. Ключевые слова имеют смысл только в ключах-командах условной компиляции и никак не перекликаются с идентификаторами самой программы.
Однажды объявленное ключевое слово можно отменить по ходу процесса компиляции (перевести из активного состояния в пассивное) ключом
- 59 -
{$UNDEF КлючевоеСлово}
После такой строки в тексте слово считается не заданным.
До сих пор речь шла о вводимых программистом ключевых словах. Кроме них, всегда определены три слова:
VER55 - ключевое слово версии компилятора (языка); для версий 5.0 и 4.0 оно было другим - VER50 и VER40 соответственно;
MSDOS - ключевое слово типа ОС; в MS-DOS, PC-DOS или их аналогах это слово именно такое;
CPU86 - ключевое слово семейства центрального процессора; если он не из семейства 80X86, то это слово будет другим.
К этому списку слов может быть добавлено еще одно, если компилятор обнаружил наличие математического сопроцессора 80X87:
CPU87 - ключевое слово, определенное, если в ПЭВМ имеется математический сопроцессор.
Ключевое слово сопроцессора позволяет установить порядок компиляции в зависимости от комплектации ПЭВМ:
{$IFDEF CPU87 -
{$N+ включаем режим использования сопроцессора }
TYPE
объявляем типы с повышенной точностью;
{$ELSE}
{$N- не используем возможности сопроцессора }
TYPE
объявляем типы с обычной точностью:
{$ENDIF}
В списке ключей условной компиляции был еще один ключ {$IFOPT}. Принцип его работы такой же, как и ключа {$IFDEF}. Отличие состоит лишь в условии срабатывания. Здесь им является состояние какого-либо ключа режима компиляции. Например, если программа компилируется в режиме {$N+}, заданном в тексте или умолчанием, то условие {$IFOPT N+} - истинно, a {$IFOPT N-} - ложно.
Теперь есть возможность управлять ходом компиляции, опираясь на состояние различных режимов. Ключ {$IFOPT} может иметь альтернативную ветвь {$ELSE} и по-прежнему обязан иметь закрывающую блок условной компиляции директиву {$ENDIF}.
Напомним, что все ключи условной компиляции имеют смысл только в процессе компиляции программ и не принимают участия в выполнении их.
- 60 -
Глава 4. Введение в систему типов языка
Турбо Паскаль является языком с сильной системой типизации. Это означает, что все данные, обрабатываемые программой, должны принадлежать к какому-либо заранее известному типу. В языке предопределено достаточное количество типов данных (например, целые и вещественные числа, символы, строки и т.п.) и имеются большие возможности для объявления новых типов, более подходящих для конкретных практических приложений.
Объявление новых типов в программе на Паскале происходит в блоке описания типов TYPE. Алгоритм объявления нового типа прост: ставится ключевое слово TYPE, и за ним следует перечисление новых имен типов, которые будут введены, и конструкций из уже известных или ранее введенных типов, непосредственно определяющих новый тип. Схематично это выглядит так:
TYPE
НовыйТип1 = Массив целых чисел;
НовыйТип2 = Множество символов;
...
НовыйТип101 = Целое число;
НовыйТип102 = Перечисленные здесь значения;
В реальной программе, конечно, слева должны стоять имена - названия новых типов (идентификаторы введенных типов), а справа - определяющие тип зарезервированные слова и имена образующих типов. Между именем и его определением обязателен знак равенства "=" (не путать со знаком присваивания ":="). Также обязательна точка с запятой ";" после завершения определения каждого нового типа. Концом блока описания типов считается начало любого другого блока (например, VAR, CONST, BEGIN) или описание заголовков процедур и (или) функций.
Какие же возможные типы данных и способы их развития предоставляет Турбо Паскаль? Система типов Турбо Паскаля значительно шире, чем в стандартном Паскале. В первую очередь, это обусловливается большим количеством базовых (простых) типов языка. Так, одних только целочисленных типов вводится пять (с математическим сопроцессором - все шесть)!
Основной (стандартный) набор простых, т.е. определяющих тип только одного отдельного значения, типов таков:
- 61 -
1. Числовые типы:
короткое целое без знака - Byte (0..255);
короткое целое со знаком - ShortInt (-128..127);
целое без знака - Word (0..65535);
целое со знаком - Integer (-32768..32767);
длинное целое со знаком - LongInt (-2147483648..2147483647);
вещественное - Real (точность 11-12 знаков после запятой).
2. Логический тип - Boolean.
3. Символьный тип - Char.
4. Строковый тип - String, String[n].
5. Адресный тип (указатель) - Pointer.
6. Перечислимый тип.
7. Ограниченный тип (диапазон).
Все эти типы могут участвовать в определении сложных типов. Обращаем внимание на отсутствие типа ALPHA, встречающегося во многих реализациях Паскаля. Здесь его заменяет более универсальный и гибкий тип String. Список числовых типов может быть расширен за счет использования математического сопроцессора. Подробно они будут рассмотрены в гл. 9 "Математические возможности Турбо Паскаля".
Набор сложных типов, определяющих структуры из простых типов весьма широк:
1) массив - Array ... of ...;
2) множество - Set of ...;
3) файлы (3 вида) - Text, File, File of ... ;
4) запись - RECORD;
5) объект - OBJECT;
6) ссылка - ^БазовыйТип.
Кроме того, Турбо Паскаль вводит особый тип, называемый процедурным. Он не имеет отношения к данным и используется для организации работы процедур и функций. Файлы в системе типов Турбо Паскаля могут быть трех различных типов: текстовые (Text), обобщенные или бестиповые (File), и компонентные или типизированные (File of ...). Из них только последний является действительно сложным, т.е. составным из прочих типов. Типы Text и File предопределены в языке и включены в этот список больше для наглядности. Некоторой натяжкой является включение ссылок в список сложных типов. Вводится принципиально новый тип - объекты. С их включением язык Турбо Паскаль обрел возможности, присущие до этого только объектно-ориентированным языкам (C++, Smalltalk).
- 62 -
Сложные типы достаточно сложны, чтобы их можно было кратко рассмотреть по ходу введения в систему типов. Подробно мы их обсудим в разд. 4.2 и гл. 7, 11, 12, 13.
4.1. Простые типы языка
Без обсуждения простых стандартных типов невозможно переходить к подробному рассмотрению всех прочих элементов языка. Поэтому дадим им достаточно полные характеристики вместе с правилами записи значений разных типов. Кроме того, детально рассмотрим вводимые простые типы, с тем чтобы в дальнейшем уже лишь ссылаться на них.
4.1.1. Целочисленные типы
Обилие целочисленных типов позволяет эффективно использовать память ПЭВМ и более гибко вводить целочисленные переменные в программу. Целочисленные типы отличаются размером при хранении в памяти (Byte и ShortInt - 1 байт, Word и Integer - 2 байта, LongInt - 4 байта) и способом кодировки значений (с представлением знака или без него). Типы без знака переводят допустимый диапазон значений целиком в неотрицательную область.
Целочисленные значения записываются в программе привычным способом:
123 4 -5 -63333 +10000
Число должно быть записано без всяких преобразований. Будет ошибкой задать целое число следующим образом:
1Е+2 (в смысле 100), или 123.0
Знак "+" перед числом может опускаться. Турбо Паскаль разрешает записывать целые значения в шестнадцатеричном формате, используя префикс $:
$FF $9 $FFFFAB0D
Регистр букв A, B, ..., F значения не имеет. Разрешается непосредственно приписывать знак числа, если значения (со знаком или без) не превышают допустимый для данного типа диапазон: от -$80 до +$7F для типа ShortInt, и от -$8000 до +$7FFF для типа Integer. Отрицательные значения для переменных типа LongInt могут быть записаны аналогичным способом. Но здесь есть особенность. Для этого типа отрицательные значения могут записываться и как целые величины без знака. При этом запись отрицательных значений в
- 63 -
шестнадцатеричном формате должна соответствовать обратному отсчету от максимального для размера LongInt положительного числа. Например, число $FFFFFFFF (условное максимальное положительное значение, размещающееся в четырех байтах) трактуется как значение типа LongInt, равное -1. Число $FFFFFFFE (это $FFFFFFFF-l) будет соответствовать уже -2 и т.д. Следуя этой схеме, значение, например -65, в шестнадцатеричном формате для типа LongInt вычислится так: от числа $FFFFFFFF, соответствующего значению -1, нужно "вычесть" еще 64:
$FFFFFFFF - 64 = $FFFFFFFF - $40 = $FFFFFFBF.
Мы специально рассмотрели запись отрицательных чисел в шестнадцатеричном формате, потому что встроенный отладчик Турбо Паскаля при выводе отрицательных целых значений в формате H приводит их к длине LongInt и выводит в обратном отсчете. Здесь необходимо сделать небольшое техническое замечание. Целые значения типов Word, Integer и LongInt хранятся в памяти в "перевернутом" виде: первым идет наименее значащий байт, а последним - наиболее значащий. Так, если мы запишем в переменную W типа Word значение $0102, то оно будет храниться как два байта $02 и $01. Аналогично, если переменной L типа LongInt присвоить значение $01020304, то оно расположится в памяти как четыре байта : $04, $03, $02, $01. Эта машинная "кухня" не важна при работе с переменными - они позволяют вообще не знать механизмов хранения данных. Но при прямом доступе в память или преобразовании данных (что разрешается языком Турбо Паскаль) эти технические подробности становятся необходимыми.
4.1.2. Вещественные числа
Вещественные значения (значения типа Real) могут записываться несколькими способами:
-1.456 | 0.00239 | -120.00 | .09 |
66777 | -10 | +123 | |
123E+2 | -1.4E-19 | 5E4 | 0.1234E+31 |
Как видно, они могут быть представлены: обычным способом с десятичной точкой; как целые, если дробная часть равна 0; в экспоненциальном формате. Экспоненциальный формат соответствует умножению на заданную степень 10. Так,
-1.4E-19 = -1.4 * (10 в степени -19).
Написание буквы E может быть как прописным, так и строчным. Без
- 64 -
использования типов повышенной точности, работающих с математическим сопроцессором 80Х87, степень может иметь не более двух цифр (в диапазоне (-38) ... (+38)), но при использования этих типов - уже до четырех цифр:
1.23456789+0120
Знак числа + может опускаться, в том числе и в экспоненте. В вещественную переменную можно записать шестнадцатеричную константу. При этом она преобразуется в вещественную форму.
4.1.3. Логический тип
Логический тип Boolean состоит из двух значений: False (ложно) и True (истинно). Слова False и True определены в языке и являются, по сути, логическими константами. Регистр букв в их написании несущественен: FALSE = false. Значения этого типа участвуют во всевозможных условных операторах языка. С логическим типом связан ряд операций языка, реализующий Булеву алгебру (логические НЕ, И, ИЛИ и др.)
4.1.4. Символьный тип
Символьный тип Char - это тип данных, состоящих из одного символа (знака, буквы, кода). Традиционная запись символьного значения представляет собой собственно символ, заключенных в одиночные кавычки: 'ж', 'z' '.' ' ' (пробел) и т.п. В Турбо Паскале имеются альтернативные способы представления символов. Все они будут рассмотрены в гл. 8 "Обработка символов и строк". Значением типа Char может быть любой символ из набора ASCII - однако на каждый из них можно "написать" на клавиатуре.
4.1.5. Строковый тип
Очень важным и полезным является тип динамических строк String. (здесь "динамические" означает переменной длины). Можно задать, например, тип String[126] - и переменные такого типа смогут иметь в себе строки длиной от 0 до 126 символов. В Турбо Паскале строки - это больше, чем просто массив символов. К ним прилагается библиотека средств, позволяющих делать со строками буквально все, что угодно. Значения типа "строка" в простейшем случае записываются как обычные текстовые строчки, заключенные в одиночные кавычки:
- 65 -
'строчка '
'строка из цифр 12345'
'В кавычках может стоять любой символ, кроме кода 13'
's'
'' (пустая строка)
'Это - '' - одиночная кавычка в строке'
4.1.6. Адресный тип
Язык Турбо Паскаль объявляет специальный адресный тип - Pointer. Значением этого типа является адрес ячейки памяти, представленный по правилом MS-DOS. Тип Pointer - сугубо внутренний. Его значения нельзя вывести на печати или записать в переменную, как мы записываем числовые значения. Вместо этого всегда приходится использовать специальные функции для преобразования условной общепринятой записи адресов памяти в формат типа Pointer и наоборот.
Мы рассмотрели типы, вводимые языком. Кроме них, есть категории типов данных, вводимых программистом, которые мы далее рассмотрим подробно. К ним относятся в первую очередь перечислимые типы.
4.1.7. Перечислимые типы
Перечислимый тип - это такой тип данных, при котором количество всех возможных значений ограничено (конечно). Например, тип Word соответствует этому определению. В нем 65536 значений - от 0 до 65535. И уж точно перечислимыми являются типы: Byte - 256 значений от 0 до 255 и Char - в нем 256 символов с кодами от 0 до 255. Можно перечислить и все значения типов ShortInt, Integer и даже LongInt. Только перечисление начнется не с нуля, а с отрицательного целого значения.
Есть и еще один предопределенный перечислимый тип - Boolean. У него всего два значения - False и True. Принято, что номер False внутри языка равен 0, а номер True равен 1. Перечислимый тип можно расписать в ряд по значениям. Тип Char можно было расписать в синтаксисе Паскаля как
TYPE
Char = ( симв0, симв1..., симв64, 'A', 'B', 'C', ...симв255);
тип Byte выглядел бы так:
Byte = (0, 1, 2,...,254, 255);
- 66 -
а логический тип - как
Boolean = ( False, True );
Но такие определения проделаны еще во время написания самого компилятора языка, осталось только пользоваться ими. Зато мы можем вводить новые перечислимые типы, придумывая им имена и перечисляя через запятую в круглых скобках названия элементов-значений этого типа:
| TYPE
| Personages = ( NifNif, NufNuf, NafNaf );
| Test = ( Level0, Level1, Level2, Level4, Level5);
| MusicCard = ( IBM, Yamaha, ATARI, other, None);
| Boolean3 = (false_, Nolnfo_, true_);
Значения в скобках - это значения новых типов. Можно теперь объявлять переменные этих типов, а их значениями можно индексировать массивы или организовывать по ним циклы. Но всегда переменная такого типа сможет содержать только те значения, которые указаны в его перечислении.
Перечислимые данные (их можно называть атомами) должны иметь синтаксис идентификаторов, и поэтому не могут перечисляться цифры, символы, строки.
Идентификаторы не могут повторяться в одной программе. Заметьте, как введен тип Boolean3 для моделирования трехзначной логики: чтобы избежать использования уже задействованных имен True и False, они чуть-чуть видоизменены. Регистр написания идентификаторов по-прежнему не играет роли. Максимальное число элементов в одном вводимом перечислении равно 65535.
Применение вводимых перечислимых типов имеет ряд преимуществ:
1) улучшается смысловая читаемость программы;
2) более четко проводится контроль значений;
3) перечислимые типы имеют очень компактное машинное представление.