Урок 108. Обработка ошибок, cerr и exit

   ⁄ 

 Обновлено 19 Янв 2018  ⁄ 

⁄   1893

При написании программ возникновение ошибок почти неизбежно. Ошибки делятся на две категории: синтаксические и семантические.

Синтаксические ошибки

Синтаксическая ошибка возникает, когда вы нарушаете правила грамматики языка C++. Например:

если 7 не равно 8, то пишем "not equal";

Хотя этот стейтмент нам (людям) понятен — для компьютера он будет неразборчив. В соответствии с правилами грамматики C++, правильно будет:

Синтаксические ошибки почти всегда улавливаются компилятором и их обычно легко исправить. Следовательно, о них слишком беспокоиться не стоит.

Семантические ошибки

Семантическая ошибка (смысловая) возникает, когда код синтаксически правильный, но выполняет не то, что нужно программисту. Например:

Возможно, программист хотел, чтобы вывелось 0 1 2 3, но на самом деле выведется 0 1 2 3 4.

Семантические ошибки не улавливаются компилятором и могут иметь разное влияние: некоторые могут вообще не отображаться, что приведет к неверным результатам, повреждению данных или вообще к сбою программы. Поэтому об семантических ошибках беспокоиться уже придется.



Они могут возникать несколькими способами. Одной из наиболее распространенных семантических ошибок является логическая ошибка. Логическая ошибка возникает, когда программист неправильно программирует логику выполнения кода. Например, фрагмент кода выше имеет логическую ошибку. Вот еще один пример:

Что произойдет, если x будет равен 4? Условие выполнится как true, а программа выведет «x is greater than 4». Логические ошибки иногда бывает довольно таки трудно найти.

Другой распространенной семантической ошибкой является ложное предположение. Ложное предположение возникает, когда программист предполагает, что что-то будет либо истинным, либо ложным, а оказывается наоборот. Например:

Заметили потенциальную проблему здесь? Предполагается, что пользователь введет значение между 0 и длиной «Hello, world!». Если же пользователь введет отрицательное число или число побольше, то этот index окажется за пределами диапазона массива. В этом случае, поскольку мы просто выводим значение по индексу, результатом будет вывод какого-то мусора (при условии, что пользователь введет число вне диапазона). Но в других случаях ложное предположение может привести и к изменениям значений переменных, и к сбою в программе.

Безопасное программирование — это методика разработки программ, которая включает анализ областей, где могут быть допущены ложные предположения, и написания кода, который обнаруживает и обрабатывает любой случай такого нарушения, чтобы снизить к минимуму риск возникновения сбоя или повреждения программы.

Определение ложных предположений

Оказывается, мы можем найти почти все предположения, которые необходимо проверить в одном из следующих трех мест:

  При вызове функции, когда caller может передать некорректные или семантически бессмысленные аргументы.

  При возврате значения функцией, когда возвращаемое значение может быть индикатором выполнения (произошла ли ошибка или нет).

  При обработке данных ввода (либо от пользователя, либо с файла), когда эти данные могут быть не того типа, что нужно.

Поэтому, придерживаясь безопасного программирования, нужно следовать трём правилам:

  В верхней части каждой функции убедитесь, что все параметры имеют соответствующие значения.

  После возврата функцией значения, проверьте возвращаемое значение (если оно есть) и любые другие механизмы сообщения об ошибках — произошла ли ошибка.

  Проверяйте данные ввода на соответствие ожидаемому типу данных и его диапазону.

Рассмотрим примеры проблем.

Проблема №1. При вызове функции caller может передать некорректные или семантически бессмысленные аргументы.

Можете ли вы определить потенциальную проблему здесь? Дело в том, что caller может передать нулевой указатель вместо допустимой строки C-style. Если это произойдет, то в программе будет сбой. Вот как правильно (с проверкой параметра функции – не является ли он нулевым):

Проблема №2. При возврате функцией значения, оно может указывать – произошла ли ошибка.

Можете ли вы определить потенциальную проблему здесь? Пользователь может ввести символ, который не находится в строке hello. Если это произойдет, то функция find() вернет индекс -1, который и выведется.

Правильно:

Проблема №3. При обработке данных ввода (либо от пользователя, либо с файла), эти данные могут быть не того типа и диапазону, что нужно. Возьмем программу с предыдущего примера – в ней хорошо можно проиллюстрировать эту ситуацию:

Вот как правильно (с проверкой пользовательского ввода):

Обратите внимание, здесь проверка двухуровневая:

  во-первых, мы должны убедиться, что пользователь введет значение того типа данных, который мы используем;

  во-вторых, это значение должно находиться в диапазоне массива.

Обработка ложных предположений

Теперь, когда вы знаете, где обычно возникают ложные предположения, давайте поговорим о способах их решения. Одного универсального способа для всех ошибок нет — всё зависит от характера проблемы.

Но есть несколько способов:

1. Пропустите код, который зависит напрямую от правильности предположения:

В примере выше, если cstring окажется NULL, то мы ничего не будем выводить. Мы пропустили тот код, который напрямую зависит от значения cstring и с ним работает (в коде мы просто выводим этот cstring). Это может быть хорошим вариантом, если пропущенный стейтмент не является критическим и не влияет на логику программы. Основной недостаток при этом состоит в том, что caller или пользователь не имеет возможности определить, что что-то пошло не так.

2. В функции возвращайте код ошибки обратно в caller и позволяйте caller-у обработать эту ошибку:

Здесь функция возвратит -1, если caller передаст некорректный index. Возврат перечислителя, в качестве кода ошибки, будет еще лучше.



3. Если нужно немедленно завершить программу — используйте функцию exit, которая находится в <cstdlib> для возврата кода ошибки обратно в операционную систему:

Если caller передаст некорректный index, то программа немедленно завершит своё выполнение и передаст код ошибки 2 в операционную систему.

4. Если пользователь ввел данные не того типа, что нужно — попросите пользователя ввести данные еще раз:

5. Еще одним механизмом для вывода сообщений об ошибках является cerr. cerr – это объект вывода (как и cout), который находится в заголовочном файле <iostream>. cerr выводит сообщения об ошибках в консоль (как и cout), но эти сообщения можно перенаправить и в отдельный файл об ошибках. Т.е. основное различие cerr от cout заключается в том, что cerr используется целенаправленно для вывода сообщений об ошибках, тогда как cout для всего остального.

В примере выше мы не только пропускаем код, который напрямую зависит от правильности предположения, но также регистрируем ошибку, чтобы пользователь мог позже определить, почему программа выполняется не так, как нужно.

6. Если вы работаете в какой-то графической среде (например, MFC, SDL, QT и т.д.), то распространенной практикой является вывод сплывающего окна (box-а) с кодом ошибки, а за ним немедленное завершение программы. То, как это сделать – зависит конкретно от среды разработки.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (21 оценок, среднее: 4,95 из 5)
Загрузка...
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

ПОДПИСЫВАЙТЕСЬ

НА КАНАЛ RAVESLI В TELEGRAM

@ravesli

ПОДПИСАТЬСЯ БЕСПЛАТНО