- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
Ранее мы специально оговорили, что такое удобное правило действует только до тех пор, пока аргументы и результат остаются в рамках допустимого диапазона. Рассмотрим, что произойдет, если мы выйдем за его пределы. Пусть в беззнаковой записи нам нужно из 130 вычесть 10. 130 — это 10000010, после вычитания получим 01111000 (120). Но если попытаться интерпретировать эти двоичные значения как знаковые числа, получится, что из -126 мы вычитаем 10 и получаем 120. Такими парадоксальными результатами приходится расплачиваться за унификацию операций со знаковыми и беззнаковыми числами.
Рассмотрим другой пример: из пяти (в двоичном представлении 00000101) вычесть десять (00001010). Здесь уместно вспомнить вычитание в столбик, которое изучается в школе: если в разряде уменьшаемого стоит цифра, большая, чем в соответствующем разряде вычитаемого, то из старшего разряда уменьшаемого приходится занимать единицу. То же самое и здесь: чтобы вычесть большее число из меньшего, как бы занимается единица из несуществующего девятого разряда. Это можно представить так: из числа (1)00000101 вычитается (0)00001010 и получается (0)11111011 (несуществующий девятый разряд показан в скобках: после получения результата мы про него снова забываем). Если интерпретировать полученный результат как знаковое целое, то он равен -5, т.е. именно тому, что и должно быть. Но с точки зрения беззнаковой арифметики получается, что 5-10=251.
Приведенные примеры демонстрировали ситуации, когда результат укладывался в один из диапазонов (знаковый или беззнаковый), но не укладывался в другой. Рассмотрим, что будет, если результат не попадает ни в тот, ни в другой диапазон. Пусть нам нужно сложить 10000000 и 10000000. При таком сложении снова появляется несуществующий девятый разряд, но на этот раз из него единица не занимается, а в него переносится лишняя. Получается (1)00000000. Несуществующий разряд потом игнорируется. С точки зрения знаковой интерпретации получается, что 128 + 128 = 0. С точки зрения беззнаковой — что -128 + (-128) = 0, т.е. оба результата, как и можно было ожидать с самого начала, оказываются некорректными.
Знаковые целые представлены в Delphi типами ShortInt (N=8, диапазон -128..127), SmallInt (N=16, диапазон -32 768..32 767), LongInt (N=32, диапазон -2 147 483 648..2 147 483 647) и Int64 (N=64, диапазон -9 223 372 036 854 775 808..9 223 372 036 854 775 807).
Примечание32-разрядные процессоры не могут выполнять операции непосредственно с 64-разрядными числами, поэтому компилятор генерирует код, который обрабатывает это число по частям. Сначала операция сложения или вычитания выполняется над младшими 32-мя разрядами а потом — над старшими 32-мя, причем, если в первой операции занималась единица из несуществующего (в рамках данной операции) 33-го разряда или единица переносилась в него, при второй операции эта единица учитывается.
Далее приведены несколько примеров, иллюстрирующих сказанное.
3.1.2. Выход за пределы диапазона при присваивании
Начнем с рассмотрения простого примера (листинг 3.1. проект Assignment1 на компакт-диске).
Листинг 3.1. Неявное преобразование знакового числа в беззнаковое при присваиванииprocedure TForm1.Button1Click(Sender: TObject);
var
X: Byte;
Y: ShortInt;
begin
Y := -1;
X := Y;
Label1.Caption := IntToStr(X);
end;
При выполнении этого примера будет выведено значение 255. Здесь мы сталкиваемся с тем, что все разряды значения Y без дополнительных проверок копируются в X, но если Y интерпретируется как знаковое число, то X — как беззнаковое, а числам 255 и -1 в восьмиразрядном представлении соответствует одна и та же комбинация битов.
ПримечаниеПромежуточная переменная Y понадобилась потому, что прямо присвоить переменной значение, выходящее за ее диапазон, компилятор не позволит — возникнет ошибка компиляции "Constant expression violates subrange bounds".
Строго говоря, в Delphi предусмотрена защита от подобного присваивания. Если включить опцию Range checking (включается в окне Project/Options... на закладке Compiler или директивой компилятора {$R+} или {$RANGECHECKS ON}), то при попытке присвоения X := Y возникнет исключение ERangeError. Но по умолчанию эта опция отключена (для повышения производительности — дополнительные проверки требуют процессорного времени), поэтому программа без сообщений об ошибке выполняет такое неправильное присваивание.
В следующем примере (листинг 3.2, проект Assignment2 на компакт-диске) мы рассмотрим присваивание числу такого значения, которое не укладывается ни в знаковый, ни в беззнаковый диапазон.
Листинг 3.2. Присваивание переменной значения, выходящего за рамки диапазонаprocedure TForm1.Button1Click(Sender: TObject);
var
X: Byte;
Y: Word;
begin
Y := 1618;
X := Y;
Label1.Caption := IntToStr(X)
end;
На экране появится число 82. Разберемся, почему это происходит. Число 1618 в двоичной записи равно 00000110 01010010. При присваивании этого значения переменной X старшие восемь битов "некуда девать", поэтому они просто игнорируются. В результате в Х записывается число 01010010, т.е. 82.
Разумеется, при включенной опции Range checking и в этом случае произойдет исключение ERangeError.
Приведенные примеры показывают два основных источника неожиданностей, возникающих при присваивании значения целой переменной:
1. При смешении знаковых и беззнаковых чисел значение меняется из-за того, что старший бит интерпретируется то как знак числа, то как старший разряд.
2. При присваивании переменной значения, требующего большего числа разрядов, "лишние" разряды просто игнорируются.
Все проблемы при присваивании сводятся к одному из этих случаев или к их комбинации.
Все эти ситуации при выключенной опции Range checking приводят к ошибкам, которые бывает очень трудно обнаружить. Из-за этого рекомендуется включать эту опцию хотя бы на этапе отладки.
В некоторых случаях возможность присваивания значений, выходящих за пределы диапазона переменной, может быть необходимой (например, для реализации "хитрых" алгоритмов или при сопряжении сторонних библиотек, одна из которых использует знаковые типы, другая — беззнаковые). Чтобы включение ERangeError не возникало, следует предусмотреть явное приведение типа. Например, следующий код работает без исключений при включенной опции Range checking (листинг 3.3).
Листинг 3.3. Явное приведение типа для подавления исключенийprocedure TForm1.Button1Click(Sender: TObject);
var
X: Byte;
Y: ShortInt;
begin
Y := -1;
X := Byte(Y);
Label1.Caption := IntToStr(X)
end;
В результате его выполнения переменная X получает значение 255.
3.1.3. Переполнение при арифметических операциях
Переполнением принято называть ситуацию, когда при операциях над переменной результат выходит за пределы ее диапазона. Рассмотрим следующий пример (листинг 3.4, проект Overflow1 на компакт-диске).
Листинг 3.4. Переполнение при вычитанииprocedure TForm1.Button1Click(Sender: TObject);
var X: Byte;
begin
X := 0;
X := X - 1;
Label1.Caption := IntToStr(X)
end;
Переменная X получит значение 255, поскольку при вычитании получается -1, что в беззнаковом формате соответствует 255. В принципе, этот пример практически эквивалентен примеру Assignment1, за исключением того, что значение -1 появляется в результате арифметических операций.
Немного изменим этот пример — заменим оператор вычитания функцией Dec (листинг 3.5, пример Overflow2 на компакт-диске).
Листинг 3.5. Переполнение при декременте{$R+}
procedure TForm1.Button1Click(Sender: TObject);
var X: Byte;
begin
X := 0;
Dec(X);
Label1.Caption := IntToStr(X);
end;
Результат получается тот же (X получает значение 255), но обратите внимание: несмотря на то, что опция Range checking включена, исключение не возникает. Этим пример Overflow2 отличается от Overflow1 — там исключение возникнет. Связано это с тем, что переполнение при использовании Dec и подобных ей функций контролируется другой опцией — Overflow checking (в коде программы включается директивой {$Q+} или {$OVERFLOWCHECKS ON}). Эта опция по умолчанию тоже отключена и ее также рекомендуется включать при отладке. При ее включении в данном примере возникнет исключение EIntOverflow.
3.1.4. Сравнение знакового и беззнакового числа