Здавалка
Главная | Обратная связь

Приклад створення нового консольного застосунку та його налаштування



Розглянемо можливу послідовність дій по налаштуванню програми на прикладі задачі розробки програми відшукання коренів квадратного рів­няння

, .

Умовно окремі групи дій будемо нумерувати з метою їх структу­ризації.

Етап 1. Створимо консольний застосунок і наберемо такий текст про­грами:

#include <iostream>
#include <conio.h>

using namespace std;
int main()
{
double a, b, c, x1, x2;
cout << "a = ";
cin >> a;
cout << "b = ";
cin >> b;
cout << "c = ";
cin >> a; d = b * b - 4 * a * a;
if (d < 0)
cout << "The equation has no roots";
cout << " x1 = " << (-b + sqrt(d)) / 2 * a
cout << " x2 = " << (-b - sqrt(d)) / 2 * a;
cout << "Press any key";
_getch();
return 0;
}

Етап 2. Виконаємо компіляцію. Як це видно з рис. 2.1, компілятор ви­явив 5 син­таксичних помилок і вивів відповідні пояснення у вікно Error List.

Рис. 2.1 – Результати компіляції

Програміст повинен шукати синтаксичну помилку перед курсором за текстом програми, причому не обов’язково в цьому ж рядку. Якщо здійснити подвійний клік мишкою у вік­ні Error List над будь-яким з рядків, у вікні коду буде здійснений перехід до рядка, в якому вперше знайдена відповідна по­милка.

Насправді, у даному випадку різних повідомлень тільки два:

error C2065: 'd' : undeclared identifier – невизначений ідентифікатор 'd';

error C2146: syntax error : missing ';' before identifier 'cout' – синтаксична по­милка: відсутній символ ';' перед ідентифікатором cout.

Перше з вказаних повідомлень зустрілося 4 рази і говорить про те, що змінна d (для запису значення дискримінанту) використовується, але не ви­значена в програмі. Усім цим повідомлення відповідає тільки одна помилка, яка зроблена раніше першої точки, де вона була виявлена. Тому вклю­чимо змінну d у перелік змінних, описуваних у верхній частині тексту про­грами.

Друга помилка вказує, що оператори мови C++ повинні закін­чу­ватися символом «крапка з комою». Місце, де виявлена помилка, також не співпадає з міс­цем, де вона була зроблена, – треба дописати символ «крапка з комою» в кі­нець попереднього рядка, завершивши записаний у ньому опера­тор.

Відзначимо, що обидві помилки виправлялися не в тих рядках, де вони були виявлені.

Виправлений текст набирає такий вигляд:

#include <iostream>
#include <conio.h>

using namespace std;
int main()
{
double a, b, c, d, x1, x2;
cout << "a = ";
cin >> a;
cout << "b = ";
cin >> b;
cout << "c = ";
cin >> a; d = b * b - 4 * a * a;
if (d < 0)
cout << "The equation has no roots";
cout << " x1 = " << (-b + sqrt(d)) / 2 * a;
cout << " x2 = " << (-b - sqrt(d)) / 2 * a;
cout << "Press any key";
_getch();
return 0;
}

Етап 3. Натиснемо інструментальну кнопку , запускаючи програму на виконання, і переконаємося, що компіляція пройде успішно і програма почне виконуватися, видавши запит на уведення кое­фіцієнту a. Якщо увести значення a = 1, b = 2, c = 1, то ми переконаємося, що отримані корені є пра­вильними. Однак для a = 1, b = 5, c = 2 результати обчислень будуть неправильними. Тому треба вже шукати логічні по­милки.

Етап 4. Ми можемо підозрювати, що в програмі неправильно обчислю­ється дискримінант. Відкриємо вікно Watch 1, додамо в нього змінну d, після чого поміс­тимо курсор у рядок, наступний за обчисленням значення дискри­мінанта d, і на­тис­немо клавішу F5. Після уведення вказаного вище другого набору зна­чень коефіцієнтів переконуємося, що дискримінант обчислений невірно (у вікні Watch 1 виводиться значення d, яке дорівнює 9 замість 17). По­мічаємо, що в тексті про­грами оператор

d = b * b - 4 * a * a;

неправильний. Виправляємо помилку:

d = b * b - 4 * a * c;

Якщо виконати ті ж дії, що й раніше, то виконання програми пере­рветься у зв’язку з виявленням помилки часу виконання, а на екран буде ви­ведене діалогове віконце з інформацією про помилку (див. рис. 2.2).

Рис. 2.2 – Віконце помилки часу виконання

У зображеному на рис. 2.2 віконці виведене таке повідомлення:

Run-Time Check Failure #3 – The variable 'c' is being used without being initialized. – Помилка перевірки часу виконання № 3 – Змінна 'c' використовується без попередньої ініціалізації.

Це повідомлення говорить про те, що в змінну c не було записане початкове значення перед її використанням.

Закриємо віконце кліком мишкою на кнопкою Break (Перервати) і пе­ре­ко­наємося, що у вікні Watch1 для змінної d буде виведене якесь дивне зна­чення -1.#IND. У вікнах Autos і Locals ми побачимо, що таке ж число записане і в змінну c (цим числом позначається невизначене значення типу double). Крім того, ми побачимо, що в змінну a записане значення 2, а не 1. Та­ким чи­ном, десь вище є логічна помилка, яку треба виправити.

Етап 5. Для відшукання помилки припиняємо виконання застосунку і натискаємо клавішу F10 для нового запуску програми в режимі налашту­вання. Після декількох натискань клавіші F10 і уведення вказаних трьох зна­чень, ми помічаємо, що третій оператор уведення замість того, щоб записати значен­ня 2 в змінну c, записав його в змінну a (це можна було побачити дещо ра­ніше, але людина звичайно бачить ті значення, що явно виділяються, – тут невизначене значення в змінній c).

Вказану помилку можна було побачити, якби ми на етапі 3 не за­пус­кали програму одразу на виконання, а зробили попередню компіляцію. У та­кому разі ми б побачили у вікні Error List такі три попередження:

warning C4101: 'x2' : unreferenced local variable – 'x2' : невикористовувана локальна змінна;

warning C4101: 'x1' : unreferenced local variable – 'x1' : невикористовувана локальна змінна;

warning C4700: uninitialized local variable 'c' used – використовується локальна змінна 'c', яка не була ініцалізірована.

Перші два попередження говорять про те, що локальні змінні x1, x2 оголошені, але ніколи не використовуються. Третє повідомлення свідчить, що в опера­торі, в якому обчислюється значення дискримінанту (а саме на нього здійс­нюється перехід, якщо клікнути мишкою по повідомленню) вико­ристову­ється локальна змінна c, в яку явно не записувалося ніяке зна­чення.

Таким чином, ми переконалися, що навіть попередження можуть свід­чити про помилку, і на них треба звертати увагу.

У той же час на повідомлення про змінні x1, x2 можна не звертати ува­гу, хоча, якщо ми не плануємо використовувати ці змінні, краще видалити їх з програми, виключивши тим самим появу попередження.

З наявних у програмі двох операторів

cin >> a;

виправимо другий, записавши його так:

cin >> c;

Крім того, видалимо змінні x1, x2 з оператора опису.

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

Етап 6. Запустимо програму на виконання і уведемо тепер такі зна­чення: a = 1, b = 2, c = 3. У цьому разі ми бачимо, що, з одного боку, програ­ма виводить повідомлення про відсутність коренів, а, з другого боку, вона ще виводить два корені з досить дивними значеннями -1.#IND. Знову треба зайня­тися налаштуванням.

Натискаючи декілька разів клавішу F10, бачимо, що оператори викону­ються в потрібній послідовності (по лівій межі вікна коду буде переміщува­тися жовта стрілка, вказуючи на рядок, який буде виконуватися наступним), але після виведення повідомлення про відсутність коре­нів, здійснюється перехід до рядка, в якому обчислюється та виводиться пер­ший корінь, хоч цей рядок не повинен виконуватися. Справа в тому, що в програмі викорис­таний невір­ний для цієї задачі формат оператора if.

Вставимо перед рядком, де виводиться перший корінь, такий рядок:

else

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

За допомогою клавіші F10 переконуємося в тому, що в програмі вико­ну­єть­ся один оператор виведення, виконання якого не повинне було мати місце. Справа в тому, що в цьому випадку два оператори

cout << " x1 = " << (-b + sqrt(d)) / 2 * a;
cout << " x2 = " << (-b - sqrt(d)) / 2 * a;

повинні розглядатися як один так званий складений оператор (блок), для чого їх треба забрати у фігурні дужки.

Зробимо це і отримаємо такий текст програми:

#include <iostream>
#include <conio.h>

using namespace std;
int main()
{
double a, b, c, d;
cout << "a = ";
cin >> a;
cout << "b = ";
cin >> b;
cout << "c = ";
cin >> c; d = b * b - 4 * a * c;
if (d < 0)
cout << "The equation has no roots";
else
{
cout << " x1 = " << (-b + sqrt(d)) / 2 * a;
cout << " x2 = " << (-b - sqrt(d)) / 2 * a;
}
cout << "Press any key";
_getch();
return 0;
}

Запуск програми зі значеннями a = 1, b = 2, c = 3 тепер приведе до отримання правильного результату.

Етап 7. Запустимо програму на виконання і уведемо нові значення ко­ефіцієнтів: a = 2, b = 5, c = 3. У цьому разі ми бачимо, що обчислені корені (x1 = -4, x2 = -6) знову є неправильними (повинні бути отримані зна­чення x1 = -1, x2 = -1.5). Треба знову здійснювати покрокове вико­нан­ня програми, натискаючи кла­вішу F10. Оскільки ми не передбачили (точніше, знищили) змінні для запису коренів рівняння, прийдеться дивитися на ре­зуль­тат, що виводиться на екран користувача. Це можна зробити в даному випадку, але ніяк не може бути рекомендоване для використання. Правиль­ним є або включення у вікно Watch виразів для обчислення коренів, або по­вернення в програму змінних x1, x2 і добавлення операторів для запису в ці змінні коренів. У результаті ми побачимо, що всі обчислення до виведення значення першого кореня виконуються правиль­но, а сам корінь є невірним. Висновок – є помилка в обчисленні коре­ня. Уважно подивившись на опера­тор, ми ба­чимо, що в ньому порушений порядок виконання операцій, а саме, оскільки операції множення та ділення мають один і той же пріоритет, при обчисленні кореня спочатку здійснюється ділення на 2, після чого отриманий результат помножується на a. Щоб отри­мати правильний результат, необ­хідно забрати знаменник 2 * a в дуж­ки, причому це, звісно, потрібно зро­бити і при об­численні другого кореня.

Після виправлення тексту отримуємо правильний результат.

Таким чином, одноразове отримання правильного результату не дає гарантії того, що програма буде правильною. Остання помилка не проявлялася раніше, тому що для коефіцієнта a уводилося значення 1, яке не впливало на результат і в разі попадання цього значення в чисельник, і в разі попадання його в знаменник. Це ще раз говорить про необхідність ретельного підбору тестових даних та багаторазового тестування.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.