Песни о Паскале
Шрифт:
Табл. 2 – Целочисленные типы данных (для Borland Pascal)
Тип данных
Размер в байтах
Диапазон возможных значений
От
До
Byte
1
0
255
Shortint
1
–128
127
Word
2
0
65535
Integer
2
–32768
32767
Longint
4
–2147483648
2147483647
Хорошо, ну а если
Капля, переполняющая чашу
Конечно, вы догадались, что размер числового типа определяет его емкость, то есть диапазон возможных значений. А что случится при попытке выйти за этот диапазон? На ум приходит доверху наполненная чаша: очевидно, что лишняя капля стечет по стенке, и в чаше ничего не изменится. Так ли будет с числовой переменной? Вопрос не праздный, и, для ответа на него, проведем эксперимент.
{$R+ – включить проверку диапазонов }
var N : byte;
begin
N:= 255; { 255 – максимальное значение для байта }
N:= N+1;
Writeln(N); Readln;
end.
Введите и откомпилируйте эту программу. В первой её строке вставлена директива, разрешающая компилятору следить за диапазонами числовых переменных. Эта директива соответствует флажку «Range checking» в окне опций компилятора (рис. 74).
Рис.74 – Окно опций компилятора
Запуск программы приведет к сообщению об ошибке «Runtime Error 201». Это значит, что попытка превысить диапазон для байтовой переменной, вызвала аварию программы.
Теперь измените директиву в первой строке, отключив проверку диапазонов (замените знак «+» знаком «–»).
{$R- – отключить проверку диапазонов }
Этот вариант программы не выдаст сообщений об ошибке, но результат ошеломит вас – это будет ноль! Вот так чаша! Числовая переменная оказалась необычной посудой, – лишняя капля полностью опустошила её! И теперь можно вновь заполнять пустую чашу. Убедитесь в этом, поменяв единицу на другое слагаемое, например 5, – в результате сложения получится 4. Открытое нами явление называют переполнением (по-английски – OVERFLOW).
В следующем опыте запустим такую программу.
{$R- – отключить проверку диапазонов }
var N : byte;
begin
N:= 0; { 0 – минимальное значение для байта }
N:= N-1;
Writeln(N); Readln;
end.
Результат
Проделав опыты с переменными других числовых типов, вы убедитесь, что переполнение и антипереполнение может постигнуть любую из них. Так, добавление единицы к положительному числу 32767 в переменной типа INTEGER дает отрицательный результат -32768. Отсюда следует общее правило: добавление единицы к максимальному значению для числового типа дает минимальное значение. И наоборот: вычитание единицы из минимального значения дает максимальное. Рис. 75 наглядно показывает это.
Рис.75 – Изменение числовых переменных при переполнении и антипереполнении
Такая вот чудная арифметика! Причина переполнений и антипереполнений кроется в устройстве регистров процессора, — в свое время мы узнаем о них больше при изучении двоичной системы счисления. Или вспомните одометр — прибор для подсчёта пробега автомобиля: по достижении предельного количества километров (99999) одометр сбрасывается в ноль.
Сейчас важно понять, что присвоение переменной некоторого выражения не гарантирует правильного результата, – он будет верным лишь при отсутствии переполнений и антипереполнений. Когда в вычислении участвуют переменные разных типов, оно выполняется в самом емком формате, то есть в Longint, а затем результат «обрубается» в соответствии с типом принимающей переменной, например:
{ $R- }
var B: Byte; S: ShortInt; W: Word; N: Integer;
...
N:= B + S + W;
Здесь даже при положительных значениях всех суммируемых операндов, результат в переменной N может оказаться отрицательным! Если вам не по нраву такое поведение программы, включайте директиву проверки диапазонов $R+.
Инкремент и декремент
Угадайте, что чаще всего делают с целыми переменными? — прибавляют и вычитают единицу. Потому в процессорах стараются ускорить эти операции. Паскаль не обошел вниманием эту особенность программ, и предлагает вам две процедуры, объявленные так:
procedure Inc (var N : longint); { прибавление единицы к переменной N }
procedure Dec (var N : longint); { вычитание единицы из переменной N }
Хотя параметр N в процедурах объявлен как LONGINT, в действительности здесь может стоять переменная любого порядкового типа: INTEGER, WORD, BYTE, CHAR и даже BOOLEAN.
var B: byte; N: integer; C: char;
...
Inc(B); { B:= B+1 }
Dec(N); { N:= N–1 }
C:= ‘A‘; Inc(C); { ‘B‘}