Переполнение типа
Ещё один очень важный аспект работы с типами данных - это переполнение.
Мы уже косвенно сталкивались с ним при рассмотрении небезопасного преобразования типов, но давайте поговорим об этом подробнее.
Что это такое
Переполнение переменной - это ситуация, когда мы пытаемся сложить в переменную значение, которое в неё не помещается.
Можно себе представить аналогию с коробками и песком. Если переменная (коробка) занимает определённое количество памяти (вмещает в себя определённое количество песка), и мы пытаемся положить в неё слишком "большие" данные (засыпаем в неё слишком большое количество песка), часть данных будет потеряна (часть песка просто высыпется).
Частично переполнение уже обсуждалось в статье про приведение типов, в разделе о небезопасных преобразованиях - там мы разбирали, что происходит, когда мы пытаемся в переменную типа char
сложить число 300
.
Пример переполнения
Предположим, что в переменной char a
уже лежит число 127. В бинарном виде это будет выглядеть как:
01111111
Тип char
формально может принимать и отрицательные значения, поэтому первый бит, по аналогии с int
, равен 0.
Что же будет, если мы попытаемся к переменной a
добавить ещё единицу? Результат в принципе будет предсказуем. Бинарное представление переменной будет выглядеть так:
10000000
Как видим, первый бит теперь равен 1 - а это для типа char
это означает, что число отрицательное. Далее, за битом знака, идут только нули. -0
автоматически переведётся в -128
.
Получается, мы хотели в char
положить 128
, а получилось, что там теперь лежит -128
.
Аналогично, если мы попытаеимся в char
положить заведомо слишком большое число, например 300
:
100101100
-> 1[00101100]
Квадратными скобками здесь я выделил часть числа 300
, которая непосредственно будет записана в переменную char
. Таким образом, мы запишем в переменную не число 300
, а число \( 101100_2 = 44_{10} \).
Последствия переполнений переменных в реальном мире
Как я уже говорил, в истории человечества было множество примеров, когда неправильная работа с типами данных в С/C++ приводила к катастрофическим последствиям. Когда-то это были потерянные огромные суммы денег, когда-то это были реальные человеческие жертвы. Иногда это просто вызывало небольшой дискомфорт для пользователей программы, но всё равно критичность ошибки это сильно не меняет.
Therac-25
В 1985-1987 медицинской аппаратурлй Therac-25 смертельной дозой радиации были убиты 6-7 человек.
Причиной послужила неправильная работа с многопоточностью и возникший вследствие этого integer overflow
(переполнение переменной int
).
На эту тему есть отличное видео-эссе, которое я оставил в источниках.
Баги в видеоиграх
Очень много ошибок в видеоиграх, которые при желании очень легко нагуглить.
Наверное, самый легендарный пример - это сломанный экран в пакмане
Если играть достаточно долго, переменная с номер уровня переполняется, и экран уровня заполняется случайными символами, из-за которых пройти игру становится невозможно.
Справедливости ради должен отметить, что все эти примеры, мягко говоря, случились достаточно давно - но это не значит, что проблемы, описанные в этой статье, неактуальны.
Просто если их не учитывать, по уровню разработки вы откатитесь на 20-30 лет назад.