Под ошибкой программы будем понимать несоответствие поведения программного артефакта его спецификации (явной или подразумеваемой). В инженерной терминологии различают:
Для ЕГЭ по информатике знание типов ошибок и способов их предупреждения критично: многие задания проверяют корректность работы с типами и границами, анализ фрагментов кода, аккуратность арифметики (целочисленной и вещественной), работу циклов и условий, оценку корректности алгоритма через инварианты и предусловия/постусловия.
По фазе обнаружения
Лексические и синтаксические (compile-time): неверные ключевые слова, скобки, пунктуация операторов. Компилятор/интерпретатор прекращает сборку/запуск.
Семантические и типовые: несовместимость типов, обращение к необъявленной переменной, неверные параметры вызова.
Линковочные/конфигурационные: отсутствующие объявления/модули, конфликты версий библиотек.
Ошибки времени выполнения (run-time): деление на ноль, выход за границы массива, разыменование нулевого указателя, переполнение стека/кучи, нарушения доступа к файлам/сети.
Логические (алгоритмические): программа завершается без исключений, но вычисляет неверный результат (например, off-by-one, неверное условие цикла).
По природе
Численные: переполнение/потеря точности/катастрофическая отмена.
Структурные: выход за границы, работа с «висячими» ссылками, утечки ресурсов.
Состояния/потоков управления: зацикливание, пропущенные ветви, нарушение инвариантов цикла/структуры данных.
Конкурентные: гонки данных, взаимная блокировка (deadlock), живое зависание (livelock).
Ввод-вывод/средовые: неверные пути, кодировки, права доступа, EOF-ловушки.
Проектные/спецификационные: неверные требования, двусмысленные формулировки, несогласованные единицы измерения.
Контракты (Хоаровская тройка)
Для фрагмента кода S корректность формулируется как:
{P} S {Q}
где P – предусловие (что истинно до), Q – постусловие (что гарантируется после).
Частичная корректность: если P истинно и S завершилась, то Q истинно.
Полная корректность: частичная корректность + гарантированная завершимость.
Инвариант цикла
Инвариант I должен быть истинным:
после инициализации перед первой итерацией;
сохраняться каждой итерацией;
вместе с условием выхода давать постусловие задачи.
Инварианты – главный инструмент обнаружения логических ошибок в циклах (типичные задачи ЕГЭ).

Переполнение целых (двухкомплементарный код)
Для n-битового целого:
min_signed = -2^(n-1)
max_signed = 2^(n-1) - 1
max_unsigned = 2^n - 1
Проверка умножения на переполнение (без длинной арифметики):
если a ≠ 0 и |b| > ⌊max_signed / |a|⌋ ⇒ переполнение при a*b
Аналогично для сложения:
если (a > 0 и b > 0 и a > max_signed - b) ⇒ переполнение
если (a < 0 и b < 0 и a < min_signed - b) ⇒ переполнение
Вещественная арифметика
Сравнение с допуском:
|x - y| ≤ ε
Накопление погрешности суммирования n чисел при фиксированной абсолютной погрешности операции δ:
|Δ_sum| ≤ (n - 1) * δ
Катастрофическая отмена: при вычитании близких чисел относительная ошибка резко возрастает – избегать вычислительных схем с близкими разностями, использовать эквивалентные устойчивые формулы.
Структурные и логические ошибки (паттерны)
Гонки данных: два потока одновременно читают/пишут одну переменную без синхронизации ⇒ недетерминированность.
Deadlock (условия Кофмана – необходимые):
взаимное исключение,
удержание и ожидание,
отсутствие принудительного изъятия,
циклическое ожидание.
Стратегии: порядок захвата ресурсов, таймауты, избегание циклов ожиданий.
Livelock: потоки «вежливо уступают» друг другу бесконечно, прогресса нет.
ABA-проблема: при lock-free структурах; лечится тегами/версионностью.
Предупреждение (правила дисциплины)
Правило 1. Инициализация по умолчанию:
sum := 0; count := 0; flag := false
Правило 2. Явные границы и инварианты: записывать в комментарии до кода цикла.
Правило 3. Проверка входа: предусловия на диапазоны/значения.
Правило 4. Использование «безопасных» операций сравнения вещественных: |x-y| ≤ ε.
Правило 5. Контроль переполнений перед операцией (см. формулы).
Правило 6. Минимальная область видимости переменных; избегать теневания.
Правило 7. Исключения: ловить ожидаемые, не глушить все подряд; разделять «восстановимые» и «фатальные».
Правило 8. Ресурсы – через RAII/with-контексты/try/finally: утечки недопустимы.
Правило 9. Юнит-тесты: краевые случаи, пустые/минимальные входы, большие входы.
Правило 10. Статический анализ: стиль, возможные нулевые ссылки/выход за границы, неиспользуемые переменные.
Правило 11. Логирование с контекстом, но без утечки секретов.
Правило 12. Детерминированность: фиксировать порядок итерации/сортировок, не полагаться на случайный порядок структур.
Отладка (методика)
Репродукция и минимизируемый пример.
Бинарный поиск по изменениям (bisect по коммитам).
Инварианты как runtime-asserts:
assert(предусловие); ...; assert(инвариант); ...; assert(постусловие);
Разделяй и властвуй: изолировать модуль, подменять внешние зависимости заглушками.
Неверные границы циклов (off-by-one) в подсчёте количества/суммы.
Смешение целочисленного и вещественного деления:
7 div 2 = 3, 7 / 2 = 3.5
Переполнение при больших произведениях/степенях для 32-битного типа.
Неправильная работа с флагом «найдено»: не обнулил/не прервал цикл.
Логические выражения: приоритеты операций, отрицание сложных условий (де Морган).
Неправильная обработка EOF/«сторожевых» значений.
Нарушение инварианта при рекурсии: отсутствует/неверное условие остановки.
Off-by-one
# неверно: пропустим последний элемент
s = 0
for i in range(0, n-1):
s += a[i]
# верно для 0..n-1:
for i in range(0, n):
s += a[i]
Вещественные сравнения
def eq(x, y, eps=1e-9):
return abs(x - y) <= eps
Проверка переполнения сложения (32-бит)
if (a > 0 and b > max32 - a) → overflow
if (a < 0 and b < min32 - a) → overflow
Инвариант подсчёта
I(t): после t итераций цикла s = sum(a[1..t])
Упражнение 1. Границы и инвариант
Дан псевдокод:
s := 0
for i := 1 to n-1 do
s := s + a[i]
a) Объясните, почему пропущен элемент a[n]. Предложите исправление двумя способами.
b) Запишите инвариант цикла и докажите коррекцию исправленного варианта.
c) Оцените сложность по времени и памяти.
Упражнение 2. Целочисленное vs вещественное деление
Требуется вычислить среднее значение целых a[1..n]. Рассмотрены варианты:
avg1 := sum / n
avg2 := sum div n
a) Для каких sum, n значения совпадают?
b) Какой тип нужно выбирать для avg1 и avg2?
c) Приведите вход, на котором неверный выбор операции даёт неправильный ответ, и укажите корректную форму вывода.
Упражнение 3. Переполнение и безопасная мультипликация
Дано 32-битное знаковое целое.
a) Запишите проверку на переполнение при вычислении p := a * b (см. формулы §3).
b) Покажите, как изменить алгоритм вычисления факториала, чтобы избежать переполнения: (1) переход на 64-бит, (2) логарифмическая оценка, (3) длинная арифметика.
c) Докажите корректность выбранной проверки для крайних значений a, b.
Упражнение 4. Логическое выражение и закон де Моргана
Дан фрагмент:
if not (x >= 0 and y <> 0) then ...
a) Преобразуйте условие к эквивалентной форме без внешнего not (используйте де Моргана).
b) Составьте таблицу истинности обеих форм и докажите эквивалентность.
c) Объясните, какая из форм менее подвержена ошибкам чтения/написания в контексте ЕГЭ.
Упражнение 5. Флаг и досрочный выход
Задача: проверить наличие элемента x в массиве a[1..n].
Даны два варианта:
(found, i) := (false, 1)
while i ≤ n do
if a[i] = x then found := true
i := i + 1
и
(found, i) := (false, 1)
while i ≤ n and not found do
if a[i] = x then found := true else i := i + 1
a) Покажите логическую ошибку первого варианта (лишняя итерация после нахождения).
b) Сформулируйте инвариант второго варианта и докажите, что он завершится не более чем за n шагов.
c) Оцените среднее число итераций при равновероятном расположении x и при отсутствии x.
Грамотная классификация ошибок и владение строгими приёмами – контракты, инварианты, проверка границ и переполнений, численная устойчивость, аккуратная работа с типами и ресурсами – позволяют системно предупреждать дефекты и быстро локализовывать сбои. Для ЕГЭ это напрямую конвертируется в успех: правильно выбранные операции, верные границы циклов, корректная арифметика и формальная аргументация («почему это правильно») обеспечивают устойчивые высокие результаты.