Арифметические операции
Как мы уже упоминали, для каждого типа данных определены действия, применимые к его значениям. Например, если переменная относится к порядковому типу данных, то она может фигурировать в качестве аргумента стандартных функций ord(), pred() и succ() (см. п. "Совместимость типов данных" ниже). А к вещественным типам эти функции применить невозможно.
Итак, поговорим теперь об операциях - стандартных действиях, разрешенных для переменных того или иного базового типа данных. Основу будут составлять арифметические операции, но, конечно же, мы не забудем и о логическом типе данных (операции, определенные для значений символьного типа, будут подробно рассмотрены в лекции 5).
Замечание: Все перечисленные ниже операции (за исключением унарных '-' и not) требуют двух операндов.
Логические операции (and, or, not, xor) применимы только к значениям типа boolean. Их результатом также служат величины типа boolean. Приведем таблицы значений для этих операций:
not | and | true false or | true false xor | true false |
true false | false true true false | true false true false false false | true true true true false false | false true true false |
a div b - деление а на b нацело (не нужно, наверное, напоминать, что деление на 0 запрещено, поэтому в таких случаях операция выдает ошибку). Результат будет принадлежать к типу данных, общему для тех типов, к которым принадлежат операнды. Например, (shortint div byte = integer). Пояснить это можно так: integer - это минимальный тип, подмножествами которого являются одновременно и byte, и shortint.
a mod b - взятие остатка при делении а на b нацело. Тип результата, как и в предыдущем случае, определяется типами операндов, а 0 является запрещенным значением для b. В отличие от математической операции mod, результатом которой всегда является неотрицательное число, знак результата "программистской" операции mod определяется знаком ее первого операнда. Таким образом, если в математике (-2 mod 5)=3, то у нас (-2 mod 5)= -2.
a shl k - сдвиг значения а на k битов влево (это эквивалентно умножению значения переменной а на 2k). Результат операции будет иметь тот же тип, что и первый ее операнд (а).
a shr k - сдвиг значения а на k битов вправо (это эквивалентно делению значения переменной а на 2k нацело). Результат операции будет иметь тот же тип, что и первый ее операнд (а).
and,or,not,xor - операции двоичной арифметики, работающие с битами двоичного представления целых чисел, по тем же правилам, что и соответствующие им логические операции.
Операции общей арифметики (+, -, *, /) применимы ко всем арифметическим типам. Их результат принадлежит к типу данных, общему для обоих операндов (исключение составляет только операция дробного деления /, результат которой всегда относится к вещественному типу данных).
Арифметические выражения
Все арифметические операции можно сочетать друг с другом - конечно, с учетом допустимых для их операндов типов данных.
В роли операндов любой операции могут выступать переменные, константы, вызовы функций или выражения, построенные на основе других операций. Все вместе и называется выражением. Определение выражения через выражение не должно вас смущать, ведь рекурсивное задание конструкций вообще свойственно программированию (см. лекцию 9).
Примеры арифметических выражений:
(x<0) and (y>0) - выражение, результат которого принадлежит к типу boolean;
z shl abs(k) - вторым операндом является вызов стандартной функции;
(x mod k) + min(a,b) + trunc(z) - сочетание арифметических операций и вызовов функций;
odd(round(x/abs(x))) - "многоэтажное" выражение.
Другие операции
Помимо арифметических, существуют и другие операции, специфичные для значений некоторых стандартных типов данных языка Pascal. Эти операции мы рассмотрим в соответствующих разделах:
#, in, +, *, [] : см. лекцию 5 @, ^ : см. лекцию 10
Функции, изменяющие тип данных
В заключение мы приведем список стандартных функций, аргумент и результат которых принадлежат к совершенно различным типам данных:
trunc3): real -> integer; round: real -> integer; val4): string -> byte/integer/real; chr5): byte -> char; ord: <порядковый_тип> -> longint;
on_load_lecture()
« |
1
|
2
|
3
|
вопросы | »
учебники
|
для печати и PDA
Курсы | Учебные программы | Учебники | Новости | Форум | Помощь
Телефон: +7 (495) 253-9312, 253-9313, факс: +7 (495) 253-9310, email: info@intuit.ru © 2003-2007, INTUIT.ru::Интернет-Университет Информационных Технологий - дистанционное образование |
Явное приведение типов данных
Тип значения можно изменить и явным способом: просто указав новый тип выражения, например: a:= byte(b). В этом случае переменной а будет присвоено значение, полученное новой интерпретацией значения переменной b. Скажем, если b имеет тип shortint и значение -23, то в a запишется 233 (= 256 - 23).
Приводить явным образом можно и типы, различающиеся по длине. Тогда значение может измениться в соответствии с новым типом. Скажем, если преобразовать тип longint в тип integer, то возможны потери из-за отсечения первых двух байтов исходного числа. Например, результатом попытки преобразовать число 100 000 к типу integer станет число 31 072, а к типу word - число 34 464.
Эквивалентность
Эквивалентность - это наиболее высокий уровень соответствия типов. Она требуется при действиях с указателями (см. лекцию 10), а также при вызовах подпрограмм. "А как же тогда быть с оговоркой, сделанной двумя абзацами выше?" - спросите вы. Мы не станем сейчас описывать механизм передачи аргументов процедурам и функциям, поясним лишь, что эквивалентность типов требуется только для параметров-переменных (см. лекцию 8).
Итак, два типа - Т1 и Т2 - будут эквивалентными, если верен хотя бы один вариант из перечисленных ниже:
Т1 и Т2 совпадают;Т1 и Т2 определены в одном объявлении типа;Т1 эквивалентен некоторому типу Т3, который эквивалентен типу Т2.
Поясним это на примере:
type T2 = T1; T3 = T1; T4,T5 = T2;
Здесь эквивалентными будут Т1 и Т2; Т1 и Т3; Т1 и Т4; Т1 и Т5; Т4 и Т5. А вот Т2 и Т3 - не эквивалентны!
Конструируемые типы данных
Эти типы данных (вместе с определенными для них операциями) мы будем рассматривать далее на протяжении нескольких лекций:
Лекция 3. Массивы
Лекция 5. Строки и множества
Лекции 6 и 7. Файлы
Лекция 7. Записи
Лекция 8. Процедурный тип данных
Лекция 10. Указатели
on_load_lecture()
1
|
2
|
3
|
вопросы | »
учебники
|
для печати и PDA
Курсы | Учебные программы | Учебники | Новости | Форум | Помощь
Телефон: +7 (495) 253-9312, 253-9313, факс: +7 (495) 253-9310, email: info@intuit.ru © 2003-2007, INTUIT.ru::Интернет-Университет Информационных Технологий - дистанционное образование |
Неявное приведение типов данных
Как мы упомянули в п. "Арифметические операции" выше, тип результата арифметических операций (а следовательно, и выражений) может отличаться от типов исходных операндов. Например, при "дробном" делении ( / ) одного целого числа на другое целое в ответе все равно получается вещественное. Такое изменение типа данных называется неявным приведением типов.
Если в некоторой операции присваивания участвуют два типа данных совместимых, но не совместимых по присваиванию, то тип присваиваемого выражения автоматически заменяется на подходящий. Это тоже неявное приведение. Причем в этих случаях могут возникать изменения значений. Скажем, если выполнить такую последовательность операторов
a:= 10; {a: byte} a:= -a; writeln(a);
то на экране мы увидим не -10, а 246 (246 = 256 - 10).
Неявным образом осуществляется и приведение при несоответствии типов переменной-счетчика и границ в циклах for (см. лекцию 3).
Неявное приведение типов данных можно отключить, если указать директиву компилятора {$R+}, которая принуждает компилятор всегда проверять границы и диапазоны. Если эта директива включена, то во всех ситуациях, в которых по умолчанию достаточно совместимости типов данных, будет необходима их эквивалентность.
По умолчанию такая проверка отключена, поэтому во всем дальнейшем изложении (если, конечно, явно не оговорено противное) мы будем считать, что эта директива находится в выключенном состоянии {$R-}.
Полнота вычислений
В общем случае вычисление сложного логического выражения прекращается в тот момент, когда его окончательное значение становится понятным (например, true and (b<0)). Зачастую такой подход позволяет заметно сэкономить на выполнении "лишних" действий. Скажем, если есть некоторая сложно вычислимая функция my_func, вызов которой входит в состав выражения
if (x<=0) and my_func(z+12),
то для случая, когда x положительно, этих сложных вычислений можно избежать.
Однако включение директивы {$B+} принудит компилятор завершить эти вычисления даже в таком случае. Ее выключение {$B-} вернет обычную схему вычислений.
Порядковые типы данных
Среди базовых типов данных особо выделяются порядковые типы. Такое название можно обосновать двояко:
Каждому элементу порядкового типа может быть сопоставлен уникальный (порядковый) номер. Нумерация значений начинается с нуля. Исключение - типы данных shortint, integer и longint. Их нумерация совпадает со значениями элементов.Кроме того, на элементах любого порядкового типа определен порядок (в математическом смысле этого слова), который напрямую зависит от нумерации. Таким образом, для любых двух элементов порядкового типа можно точно сказать, который из них меньше, а который - больше2).
Порядок вычислений
Если в выражении расставлены скобки, то вычисления производятся в порядке, известном всем еще с начальной школы: чем меньше глубина вложенности скобок, тем позже вычисляется заключенная в них операция. Если же скобок нет, то сначала вычисляются значения операций с более высоким приоритетом, затем - с менее высоким. Несколько подряд идущих операций одного приоритета вычисляются в последовательности "слева направо".
Унарные2) операции | +, -, not, @, ^, # | Первый(высший) |
Операции, эквивалентные умножению | *, /, div, mod, and, shl, shr | Второй |
Операции, эквивалентные сложению | +,-, or, xor | Третий |
Операции сравнения | =, <>, >, <, <=, >=, in | Четвертый |
Замечание: Вызов любой функции имеет более высокий приоритет, чем все внешние относительно этого вызова операции. Выражения, являющиеся аргументами вызываемой функции, вычисляются в момент вызова (см. лекцию 8).
Примеры выражений (с указанием последовательности вычислений) для целых чисел:
a + b * c / d (результат принадлежит к вещест- 3 1 2 венному типу данных);
a * not b or c * d = 0 (результат принадлежит к логиче- 2 1 4 3 5 скому типу данных);
-min(a + b, 0) * (a + 1) (результат принадлежит к целочис- 3 2 1 5 4 ленному типу данных).
on_load_lecture()
« |
1
|
2
|
3
|
вопросы | »
учебники
|
для печати и PDA
Курсы | Учебные программы | Учебники | Новости | Форум | Помощь
Телефон: +7 (495) 253-9312, 253-9313, факс: +7 (495) 253-9310, email: info@intuit.ru © 2003-2007, INTUIT.ru::Интернет-Университет Информационных Технологий - дистанционное образование |
Совместимость
Совместимость типов требуется при конструировании выражений, а также при вызовах подпрограмм (для параметров-значений). Совместимость означает, что для переменных этих типов возможна операция присваивания - хотя во время этой операции присваиваемое значение может измениться: произойдет неявное приведение типов данных (см. п. "Приведение типов данных" ниже).
Два типа Т1 и Т2 будут совместимыми, если верен хотя бы один вариант из перечисленных ниже:
Т1 и Т2 эквивалентны (в том числе совпадают);Т1 и Т2 - оба целочисленные или оба вещественные;Т1 и Т2 являются подмножествами одного типа;Т1 является некоторым подмножеством Т2;Т1 - строка, а Т2 - символ (см. лекцию 5);Т1 - это тип pointer, а Т2 - типизированный указатель (см. лекцию 10); Т1 и Т2 - оба процедурные, с одинаковым количеством попарно эквивалентных параметров, а для функций - с эквивалентными типами результатов (см. лекцию 8).
Совместимость по присваиванию
В отличие от простой совместимости, совместимость по присваиванию гарантирует, что в тех случаях, когда производится какое-либо присваивание (используется запись вида a:=b; или происходит передача значений в подпрограмму1) или из нее и т.п.), не произойдет никаких изменений присваиваемого значения.
Два типа данных Т1 и Т2 называются совместимыми по присваиванию, если выполняется хотя бы один вариант из перечисленных ниже:
Т1 и Т2 эквивалентны, но не файлы2); Т1 и Т2 совместимы, причем Т2 - некоторое подмножество в Т1;Т1 - вещественный тип, а Т2 - целый.
Совместимость типов данных
В общем случае при выполнении арифметических (и любых других) операций компилятору требуется, чтобы типы операндов совпадали: нельзя, например, сложить массив и множество, нельзя передать вещественное число функции, ожидающей целый аргумент, и т.п.
В то же время, любая процедура или функция, написанная в расчете на вещественные значения, сможет работать и с целыми числами.
Правила, по которым различные типы данных считаются взаимозаменяемыми, мы приводим ниже.
Стандартные арифметические функции
К арифметическим операциям примыкают и стандартные арифметические функции. Их список с кратким описанием мы приводим в таблице.
abs(x) | Абсолютное значение (модуль) числа | Арифметический | Совпадает с типом аргумента |
arctan(x) | Арктангенс (в радианах) | Арифметический | Вещественный |
cos(x) | Косинус (в радианах) | Арифметический | Вещественный |
exp(x) | Экспонента (ex) | Арифметический | Вещественный |
frac(x) | Взятие дробной части числа | Арифметический | Вещественный |
int(x) | Взятие целой части числа | Арифметический | Вещественный |
ln(x) | Натуральный логарифм (по основанию e) | Арифметический | Вещественный |
odd(x) | Проверка нечетности числа | Целый | boolean |
pi | Значение числа | - | Вещественный |
round(x) | Округление к ближайшему целому | Арифметический | Целый |
trunc(x) | Округление "вниз" - к ближайшему меньшему целому | Арифметический | Целый |
sin(x) | Синус (в радианах) | Арифметический | Вещественный |
sqr(x) | Возведение в квадрат | Арифметический | Вещественный |
sqrt(x) | Извлечение квадратного корня | Арифметический | Вещественный |
Стандартные подпрограммы, обрабатывающие порядковые типы данных
Только для величин порядковых типов определены следующие функции и процедуры:
Функция ord(x) возвращает порядковый номер значения переменной x (относительно того типа, к которому принадлежит переменная х).Функция pred(x) возвращает значение, предшествующее х (к первому элементу типа неприменима).Функция succ(x) возвращает значение, следующее за х (к последнему элементу типа неприменима).Процедура inc(x) возвращает значение, следующее за х (для арифметических типов данных это эквивалентно оператору x:=x+1).Процедура inc(x,k) возвращает k-е значение, следующее за х (для арифметических типов данных это эквивалентно оператору x:=x+k).Процедура dec(x) возвращает значение, предшествующее х (для арифметических типов данных это эквивалентно оператору x:=x-1).Процедура dec(x,k) возвращает k-e значение, предшествующее х (для арифметических типов данных это эквивалентно оператору x:=x-k).
На первый взгляд кажется, будто результат применения процедуры inc(x) полностью совпадает с результатом применения функции succ(x). Однако разница между ними проявляется на границах допустимого диапазона. Функция succ(x) неприменима к максимальному элементу типа, а вот процедура inc(x) не выдаст никакой ошибки, но, действуя по правилам машинного сложения, прибавит очередную единицу к номеру элемента. Номер, конечно же, выйдет за пределы диапазона и за счет усечения превратится в номер минимального значения диапазона. Получается, что процедуры inc() и dec() воспринимают любой порядковый тип словно бы "замкнутым в кольцо": сразу после последнего вновь идет первое значение.
Поясним все сказанное на примере. Для типа данных
type sixteen = 0..15;
попытка прибавить 1 к числу 15 приведет к следующему результату:
+ 1 1 1 1 1 1 0 0 0 0
Начальная единица будет отсечена, и потому получится, что inc(15)=0.
Аналогичная ситуация на нижней границе допустимого диапазона произвольного порядкового типа данных наблюдается для процедуры dec(x) и функции pred(x):
dec(min_element)= max_element
Типы данных языка Pascal
Компиляторы языка Pascal требуют, чтобы сведения об объеме памяти, необходимой для работы программы, были предоставлены до начала ее работы. Для этого в разделе описания переменных (var) нужно перечислить все переменные, используемые в программе. Кроме того, необходимо также сообщить компилятору, сколько памяти каждая из этих переменных будет занимать. А еще было бы неплохо заранее условиться о различных операциях, применимых к тем или иным переменным...
Все это можно сообщить программе, просто указав тип будущей переменной. Имея информацию о типе переменной, компилятор "понимает", сколько байт необходимо отвести под нее, какие действия с ней можно производить и в каких конструкциях она может участвовать.
Для удобства программистов в языке Pascal существует множество стандартных типов данных и плюс к тому возможность создавать новые типы.
Конструируя новые типы данных на основе уже имеющихся (стандартных или опять-таки определенных самим программистом), нужно помнить, что любое здание должно строиться на хорошем фундаменте. Поэтому сейчас мы и поговорим об этом "фундаменте".
На основании базовых типов данных строятся все остальные типы языка Pascal, которые так и называются: конструируемые.
Разделение на базовые и конструируемые типы данных в языке Pascal показано в таблице:
Арифметические типы данных | |||||
Целые | Вещественные | ||||
Логический boolean | Символьный (литерный) char | shortint byte integer word longint | real single double extended comp | Нетипизированный указатель pointer | |
Перечисляемый week = (su, mo, tu, we, th, fr,sa); | Типизированный указатель ^<тип> | Массив array | |||
Строка string | |||||
Запись record | |||||
Интервал (диапазон) budni = mo..fr; | Файл text file | ||||
Процедурный | |||||
Объектный1) | |||||
Типы данных, конструируемые программистом |
Типы данных, конструируемые программистом, описываются в разделе type по следующему шаблону:
type <имя_типа> = <описание_типа>;
Например:
type lat_bukvy = 'a'..'z','A'..'Z';
Базовые типы данных являются стандартными, поэтому нет нужды описывать их в разделе type. Однако при желании это тоже можно сделать, например, дав длинным определениям короткие имена. Скажем, введя новый тип данных
type int = integer;
можно немного сократить текст программы.
Стандартные конструируемые типы также можно не описывать в разделе type. Однако в некоторых случаях это все равно приходится делать из-за требований синтаксиса. Например, в списке параметров процедур или функций конструкторы типов использовать нельзя (см. лекцию 8).
Типы данных, относящиеся к порядковым
Опишем теперь порядковые типы данных более подробно.
Логический тип boolean имеет два значения: false и true, и для них выполняются следующие равенства:
ord(false)=0, ord(true)=1, false<true, pred(true)=false, succ(false)=true, inc(true)=false, inc(false)=true, dec(true)=false, dec(false)=true.В символьный тип char входит 256 символов расширенной таблицы ASCII (например, 'a', 'b', 'я', '7', '#'). Номер символа, возвращаемый функцией ord(), совпадает с номером этого символа в таблице ASCII.Целочисленные типы данных сведем в таблицу:
shortint byte integer word longint | 1 1 2 2 4 | 8 8 16 16 32 | -128..127 0..255 -32768..32767 0..65535 -2147483648..2147483647 | -27..27-1 0..28-1 -215..215-1 0..216-1 -231..231-1 |
type week =(sun,mon,tue,wed,thu,fri,sat) 0 1 2 3 4 5 6
Напомним, что для этого типа данных:
inc(sat) = sun, dec(sun) = sat.Интервальные типы данных задаются только границами своего диапазона. Например:
type month = 1..12; budni = mon..fri;Программист может создавать и собственные типы данных, являющиеся комбинацией нескольких стандартных типов. Например:
type valid_for_identifiers = 'a'..'z','A'..'Z','_','0..9';
Этот тип состоит из объединения нескольких интервалов, причем в данном случае изменен порядок латинских букв: если в стандартном типе char 'A' < 'a', то здесь, наоборот, 'a' < 'A'. Для величин этого типа выполняются следующие равенства:
inc('z')='A'; dec('0')='_', pred('9')='8'; ord('b')= 2.
Вещественные типы данных
Напомним, что эти типы данных являются арифметическими, но не порядковыми.
single real double extended comp | 4 6 8 10 8 | 1.5*10-45..3.4*1038
2.9*10-39..1.7*1038 5.0*10-324..1.7*10308 3.4*10-4932..1.1*104932 -263+1..263-1 |