+ z)) + 1 = 999 + (-1000) + 1 = 0.
Подібні проблеми виникають тоді, коли додаються числа, які близькі до мінімальних і максимальних для розрядної сітки.
Деякі типові помилки в програмах.
Що стосується деяких видів типових помилок, яких може припустись розробник програми, то вони можуть бути такими:
вибраний невірний алгоритм (наприклад, для чисельного розв’язання систем лінійних алгебраїчних рівнянь існують різні методи, зокрема, метод Гаусса, метод Зейделя, метод прогонки і т.д. Ці методи є збіжними при виконанні певних (своїх для кожного метода) умов, які накладаються на систему рівнянь, і цей факт обов’язково необхідно враховувати при виборі метода в кожному конкретному випадку);
помилки аналізу (невірне програмування правильного алгоритму);
семантичні помилки (наприклад, якийсь оператор насправді діє не зовсім так, або зовсім не так, як передбачає програміст);
помилки при виконанні операцій (наприклад, ділення на нуль, втрата точності, вихід за межі типу даних);
помилки даних (наприклад, символьні замість числових);
неініціалізовані змінні; змінні без початкових значень – часта помилка в програмах, яку важко знайти debugger’ом, оскільки останній якраз може проініціалізувати змінну;
непроініціалізовані вказівники, які використуваються так, наче вони адресують динамічні змінні, можуть привести до тяжких наслідків для програми;
індексація з виходом за межі масиву, тобто використання як елементів масиву змінних, що знаходяться поза його межами;
непередбачені особливі випадки вводу-виводу – наприклад, коли не обробляється сигнал кінця файлу.
І якщо про синтаксичні помилки піклується компілятор, то помилки, перелічені вище, можуть бути виявлені лише на етапі тестування.
Методологічні принципи налагодження програм.
Забудьте про свою самовпевненість (див. закон Мерфі).
Використовуйте невеликі модулі (оскільки кількість і складність помилок зростає експоненційно в залежності від довжини програми).
Слід зауважити, що згідно з цим принципом виникає проблема: із зростанням кількості модулів зростає кількість зв’язків між ними, а отже, збільшується і кількість можливих помилок. Єдиний вихід з цієї проблеми – структурне програмування.
Дублюйте тести. Дуже добре, коли при повторі тесту ви отримуєте ту саму помилку. Найгірші помилки – ті, які то з’являються, то зникають.
“Зрозумій себе”. Якщо ви знаєте, що досить часто припускаєтесь однієї й тієї самої помилки, можливо й на цей раз ви зробили те саме.
Підхід до відшукання помилок повинен бути систематичним. Не обмежуйтесь випадковим зондуванням своєї програми – це може тільки зробити ситуацію ще більш незрозумілою. Головне, щоб кожний наступний тест, спрямований на локалізацію помилки, давав би чергову інформацію про неї. Одним із прикладів систематичного підходу до пошуку помилок є так званий дихотомічний пошук (відомий також під героїчною назвою “ловля лева в пустелі”). Він базується на тому, що при кожному черговому тесті з програми виключається та половина програмного коду, де помилка напевне не проявляється. Проаналізувавши результати тестового прогону, знову виключається частина програми, і так до тих пір, поки помилка не буде повністю локалізована.
Користуйтеся принципом розумної недовіри. Якщо програма працює давно і успішно, це ще зовсім не означає, що в ній немає помилок! Можливо, просто програма ще не була поставлена перед тестом, який виявить приховану помилку.
Контролюйте правдоподібність даних. Деякі типові приклади помилок:
змішуються символьні і числові дані;
дані виходять за межі можливої області значень змінних;
елементи інформації, які повинні мати фіксовану довжину (індекс, номер телефона), не відповідають цій умові;
податок перевищує суму, на яку він нараховується...
8. Спілкуйтеся з колегами. Вже не кажучи про те, що інші можуть побачити те, чого не бачите ви, є ще один важливий момент: дуже часто людина сама знаходить свою помилку, коли намагається комусь пояснити, які саме дії вона хоче виконати.
Тестування програм.
Найголовніший принцип в тестуванні програм полягає в тому, що про цю стадію потрібно думати під час всього періода розробки програми. Створюючи черговий фрагмент програми, обов’язково треба мати на увазі тест, який міг би перевіряти коректність його роботи. Якщо немає відповіді на питання про спосіб тестування даного фрагменту, можливо потрібно розбити його на підпрограми, які тестувати легше, або просто переписати.
Проблемним є також і питання про те, коли можна призупинити тестування, вважаючи програму правильною. Один із корисних принципів полягає в тому, щоб кожний оператор програми був би випробуваний принаймні раз, тобто тестові дані повинні забезпечувати перевірку всіх можливих умов виникнення помилок. Потрібно перевірити кожну гілку алгоритму. Тестова інформація повинна включати в себе всі типи даних. Такі перевірки (які є необхідними, але недостатніми), при яких тести проходять по всіх гілках логічної схеми програми, називаються тестуванням гілок. Тим не менш, слід мати на увазі, що абсолютно повного обсягу тестування добитись надто важко. Детальніші пояснення з цього приводу можна знайти в [5].
Якість тестування визначається значним чином не кількістю тестових прогонів. Головне, щоб кожний черговий тестовий прогін контролював би щось таке, що не було перевірено в попередніх прогонах. Задача тестування полягає в тому, щоб створити для програми максимально напружений режим роботи.
При проведенні всіх тестів ви повинні чітко уявляти собі правильний результат. Якщо ви не розумієте, яку інформацію ви одержите на виході тесту, ви даремно витрачаєте час. Отже, тестові дані мають бути ретельно підготовані. При цьому треба починати з найпростіших перевірок, поступово нарощуючи обсяг і глибину тестування.
Перший тест може бути зовсім простим. Його головна мета – перевірити, чи спрацює програма взагалі. Тому його називають ще “димовим” тестом. Подальше ускладнення тестів має відбуватись поступово, додаючи до перевірених елементів програми при кожному тесті по одному. Якщо з допомогою одного тесту ви намагаєтесь перевірити відразу декілька підпрограм