Урок №114. Обробка помилок, cerr і exit()

  Юрій  | 

  Оновл. 23 Січ 2021  | 

 117

При написанні програм виникнення помилок майже неминуче. Помилки в мові C++ діляться на дві категорії: синтаксичні та семантичні.

Синтаксичні помилки

Синтаксична помилка виникає при порушенні правил граматики мови 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 і виводить повідомлення про помилки в консоль (як і cout), але тільки ці повідомлення можна ще і перенаправити в окремий файл з помилками. Тобто основна відмінність cerr від cout полягає в тому, що cerr цілеспрямовано використовується для виведення повідомлень про помилки, тоді як cout — для виведення всього іншого. Наприклад:

У прикладі, наведеному вище, ми не тільки пропускаємо код, який безпосередньо залежить від правильності припущення, але також реєструємо помилку, щоб користувач міг пізніше визначити, чому програма виконується не так, як потрібно.

Спосіб №6: Якщо ви працюєте в якомусь графічному середовищі, то поширеною практикою є виведення спливаючого вікна з кодом помилки, а потім негайне завершення програми. Те, як це зробити, залежить від конкретного середовища розробки.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *