You are on page 1of 62

ХЕРСОНСЬКИЙ НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ

КАФЕДРА ІНФОРМАЦІЙНИХ ТЕХНОЛОГІЙ


ФАКУЛЬТЕТУ КІБЕРНЕТИКИ ТА СИСТЕМНОЇ ІНЖЕНЕРІЇ

МЕТОДИЧНІ РЕКОМЕНДАЦІЇ

до виконання лабораторних робіт

з дисципліни «Технологія програмування»

для студентів 2 курсу


підготовки першого (бакалаврського) рівня вищої
освіти
галузі знань 12 - "Інформаційні технології"
спеціальності 123 - "Комп'ютерна інженерія"
освітньо-професійної програми Комп'ютерна інженерія
факультету Кібернетики та системної інженерії

Херсон – 2018 р.

1
Методичні рекомендації до виконання лабораторних робіт з дисципліни «Технологія
програмування» підготовки фахівців на першому (бакалаврському) рівні вищої освіти галузі
знань 12 - "Інформаційні технології" спеціальності 123 - "Комп'ютерна інженерія"

РОЗРОБНИКИ: Дроздова Євгенія Анатоліївна, старший викладач кафедри Інформаційних


технологій

РЕЦЕНЗЕНТ: Рудакова Г.В., зав.кафедри ТК, д.т.н.,професор

Затверджено на засіданні кафедри Інформаційних технологій


Протокол № ___ від «___» _______ 2018 року
В.о. завідувача кафедри Г.О.Райко

Відповідальний за випуск Г.О.Райко, в.о. зав.кафедри ІТ, к.т.н., доцент

Узгоджено з навчально-методичним відділом


Реєстраційний номер №______________________________

© ХНТУ, 2018р.

2
Метою даних методичних вказівок є надання студентові методичних матеріалів для
виконання лабораторних робіт, передбачених робочою програмою вивчення дисципліни
«Технологія програмування».
Даний цикл лабораторних робіт виконується в ході вивчення курсу з метою
закріплення теоретичних знань, отриманих на лекціях, і придбання практичних навичок.
Лабораторні роботи виконуються у відведений для цієї мети аудиторний час,
відповідно до розкладу навчальних занять, у дисплейному класі, обладнаному
персональними комп'ютерами.
Як правило, виконання кожної лабораторної роботи вимагає від студента наступних
дій:
- ретельного вивчення теоретичного матеріалу, що відноситься до теми
лабораторної роботи (конспекту лекцій, додаткової літератури й теоретичної
частини даних методичних вказівок);
- ознайомлення із завданням, що потрібно виконати в ході виконання роботи;
- розробки алгоритму, якщо потрібно, і написання програми мовою С/С++, що
реалізує поставлене завдання;
- налагодження написаної програми на комп'ютері із прорахунком контрольного
приклада;
- оформлення звіту про лабораторну роботу відповідно до вимог даних методичних
вказівок;
- захисту лабораторної роботи.
Звіти про лабораторні роботи оформляються в учнівському зошиті або у вигляді
зшитих аркушів формату А4, можуть бути надруковані або написані від руки. Звіт про
кожну роботу повинен містити наступні розділи:
- номер роботи, назва роботи;
- ціль роботи;
- короткі теоретичні відомості. Цей розділ повинен містити короткі відомості по
темі виконуваної лабораторної роботи. Не допускається дослівне копіювання
змісту справжніх методичних вказівок; студент повинен виявити творчий підхід і
вміння конспективно викладати основні положення досліджуваної теми;
- завдання на лабораторну роботу;
- текст налагодженої програми, що реалізує поставлене завдання. Лістинг програми
може бути надрукований або написаний від руки.
- контрольний приклад (результат виконання програми з усіма виведеними нею
повідомленнями для конкретних значень уведених користувачем даних). Якщо
програма має кілька галузей обчислень, то контрольний приклад приводиться для
кожної з галузей;
- відповіді на контрольні питання (за бажанням студента або відповідно до вимог
викладача);
- висновки, зроблені студентом після виконання лабораторної роботи.
Захист лабораторних робіт виконується кожним студентом персонально. Для
успішного захисту необхідно:
- наявність правильно оформленого звіту про лабораторну роботу;
- наявність правильно працюючих програм для кожної лабораторної роботи;
- уміння студента виразно пояснити дію розробленого алгоритму й програми;
- уміння студента відповісти на контрольні питання, наведені наприкінці кожної
лабораторної роботи, а також додаткові питання, пов'язані з темою лабораторної
роботи.
До лабораторних занять допускаються студенти, що пройшли інструктаж з техніки
безпечної роботи в дисплейному класі. Студенти, що порушують дисципліну або не
дотримуються техніки безпеки, видаляються з комп’ютерного класу.

3
Лабораторна робота №1

Програмування з використанням прототипів функцій і констант

Ціль роботи: ознайомитися із прийнятими в мові С++ прототипами функцій і


константами й навчитися використовувати їх при програмуванні на С++.

Теоретичні відомості
Прототипи функцій стали частиною стандарту ANSI C після того, як були
запропоновані при розробці С++.
Прототип функції складається з наступних частин:
тип_знач Ім'я_функції (тип1 параметр1,...,тип n параметр n);
Наприклад:
int square(int value);
Ця зміна є істотною, тому що при старому стилі програмування без прототипів функцій
компілятор не перевіряє відповідність типів при виклику функції. Якщо при виклику функції
використовується параметр неправильного типу, або невірне число параметрів, або вони
ідуть не в тім порядку, то без прототипів функцій компілятор проігнорує ці помилки. Навіть
тоді, коли функція не має параметрів і не повертає значення, потрібне оголошення її
прототипу, що у цьому випадку виглядає в такий спосіб:
void FunctionName(void);
Прототипи функцій звичайно описуються на початку програми перед їхнім
використанням. У тому випадку, коли функції використовуються декількома файлами, їхні
прототипи бажано описувати в заголовному файлі (header-file), що потім включається в
програму директивою #include. Наприклад:
//заголовний файл first.h
extern char *name;
extern void fun(void);
//основна програма second.c
#include ”first.h”
char *name=”Використання прототипів”;
main()
{
fun();
}
//файл third.c, що містить функцію fun()
#include ”first.h”
#include <stdio.h>
void fun(void)
{
printf(“%s”, name);
}
Цю програму потрібно відкомпілювати, використовуючи опцію PROJECT верхнього
меню оболонки.
Специфікатор const також став частиною стандарту ANSI C завдяки С++. Він може
використовуватися для заміни директиви препроцесора #define. Значення об'єктів, які були
оголошені як const, не можуть бути змінені в програмі. Наприклад:
const int true=1;
const double pi= 4*atan(1);
const int size=5000;
char *const names= “Procter&Gamble”;
const char *names1= “Mickey&Minny”;
Четверте оголошення вказує, що names - це константний покажчик на char, тобто ця

4
адреса не може бути змінена. Останнє оголошення вказує, що рядок “Mickey&Minny” є
константою. Покажчик names1 (його адреса) константою не є й може бути перевизначений.
Оскільки константу не можна змінити, вона повинна бути ініціалізована при
присвоюванні, тобто нема рації в такому записі:
const float radius;
radius=2.135; //помилка
Завдання:
Використовуючи прототипи функцій і константи, написати програму:
1. Обчислення довжини окружності.
2. Обчислення площі кругу.
3. Обчислення ваги тіла масою m по формулі P=mg, g=9.8
4. Друку рядка-константи.
5. Уведення й виводу елементів масиву з розміром-константою.

Контрольні питання

1. Для чого потрібні прототипи функцій?


2. Який синтаксис прототипів функцій?
3. Які можливі наслідки використання функцій без прототипів?
4. Для чого використовуються константи?
5. У чому різниця між константою-покажчиком і покажчиком на константу?

Лабораторна робота №2

Уведення й вивід у С++

Ціль роботи: навчитися використовувати засоби уведення-виводу С++ при


програмуванні.
Теоретичні відомості
На додаток до засобів уведення-виводу, що існує в С, у С++ найпростіше уведення-вивід
забезпечують об'єкти cin і cout. Об'єкт cout виводить інформацію на стандартний пристрій
виводу (екран, якщо не призначено інше). Інформацію про ці об'єкти містить файл
iostream.h. Формат запису:
cout << data [<< data] ;
де data - це змінні, константи, вирази або комбінація всіх трьох типів. Символи << - це
оператор вставки, за допомогою якого дані поміщаються у вихідний потік. Об'єкту cout
можуть передаватися множинні повідомлення:
cout<<"The value of IntVar is -"<< intVar << "\n";
На відміну від функції printf(), про тип об'єкта, що повинен бути надрукований,
повідомляє тільки сам об'єкт. Наприклад:
#include <iostream.h>
main()
{
char *init= “Іванов”;
int age= 32;
float salary=350.00;
cout<<”Прізвище:\n”;
cout<<init<<”\n”;
cout<<”Вік:\n”;
cout<<age<<”\n”;
cout<<”Оклад:\n”;
cout<<salary<<”\n”;
return 0;}
5
Однак, у виведеному рядку можуть бути використані параметри форматування. Для
цього служить функція form( ), що здійснює форматування так само, як і функція printf( ).
Наприклад:
cout<<form("Число із двома десятковими знаками = %.2f", aFloat );
При друці таблиць для розміщення даних у колонку зручно використовувати символ
табуляції \t. Для друку шістнадцятковіх і восьмеричних чисел використовуються
маніпулятори hex і oct. Наприклад:
int h_value=0x4C;
int o_value=054;
cout<<”друк шістнадцяткового числа\n”;
cout<<hex<<h_value;
cout<<”Друк восьмеричного числа\n”;
cout<<oct<<o_value;
Для уведення інформації зі стандартного пристрою уведення (клавіатури, якщо не
призначено інше), служить об'єкт cin. Форма запису:
cin[>>values];
де values - змінні, у які будуть розміщатися дані, >> - оператор добування. Операція
уведення використовує ті ж маніпулятори, що й операція виводу. Використання cin
приводить до певних проблем. Оператор уведення вимагає, щоб користувач уводив саме такі
дані, які очікує cin. Через те, що неможливо проконтролювати дії користувача при уведенні,
ця відповідність не може бути гарантована.
Приклад програми, що використовує засоби уведення-виводу С++:

#include <iostream.h>
main()
{
char fam[20], name[20];
int age;
float salary;
cout<<”Як Ваше ім'я?\n”;
cin>>name;
cout<<”Як Ваше прізвище?\n”;
cin>>fam;
cout<<”Ваш вік?\n”;
cin>>age;
cout<<”Ваш оклад?\n”;
cin>>salary;
cout<<”От Ваші дані:\n”<<fam<<name<<”,”<<age<<”років, оклад“<<salary<<”грн.”;
return 0;
}
Завдання:
Написати програму, що
1. Запитує прізвища 10 студентів і їхні оцінки по 10 предметам. Обчислити середню оцінку
кожного студента й середню оцінку по кожному предметі. Результати й вихідні дані
надрукувати у вигляді таблиці.
2. Друкує таблицю множення (таблицю ПІфагора).

Контрольні питання:

1. У чому різниця між уведенням-виводом у С і С++?


2. Де втримується інформація про об'єкти cin і cout?
3. Як здійснюється вивід восьмеричних і шістнадцяткових чисел?
4. У чому складається недолік використання cin?

6
Лабораторна робота №3
Програмування з використанням
перевантаження функцій

Ціль роботи: навчитися використовувати при програмуванні на С++


перевантаження функцій.

Теоретичні відомості

Імена функцій і операцій можуть бути перевантажені в межах однієї й тієї ж області
видимості. Компілятор відрізняє одну функцію від іншої (один оператор від іншого) по її
сигнатурі. Сигнатура функції задається числом, порядком слідування й типами її параметрів.
Перевантаження дозволяє використовувати одне ім'я для реалізації різних функцій, якщо
реалізація може бути розпізнана по типу або числу параметрів. Наприклад:
#include<stdio.h>
#include<string.h>
int noName(int first)
{
return first*first;
}
int noName(unsigned first)
{
return -first*first;
}
char noName(char first)
{
return first+3;
}
int noName(int first, char *second)
{
return first*strlen(second);
}
int noName(int first, char second)
{
return first*second;
}
float noName(float r)
{
return r*r;
}
double noName(double r)
{
return r*r;
}
main()
{
printf(“%d\n”,noName(4));
printf(“%d\n”,noName(unsigned)4));
printf(“%c\n”,noName('a'));
printf(“%d\n”,noName(4,”abc”));
printf(“%d\n”,noName(4,'a'));
printf(“%0.2f\n”,noName((float)1.2));
7
printf(“%0.2f\n\n”,noName((double)1.2));
}
Функція noName перевантажена сім разів. Перші два визначення відрізняються тільки
типом одного параметра first. Четверте й п'яте визначення відрізняються типом другого
параметра second. Шосте й сьоме визначення відрізняються тим, що в один параметр має тип
float, а в другий - double.
Сигнатура функції залежить тільки від типу й кількості параметрів, але не залежить від
типу значення, що повертається.
Завдання:
1. Написати програму, що містить функцію для виводу дати. Цю функцію перевантажити
для одержання дати у вигляді рядка символів і у вигляді трьох цілих чисел.
2. Написати програму, що містить функцію для визначення найбільшого із двох аргументів.
Перевантажити цю функцію для цілих, аргументів типу long і аргументів з плаваючою
точкою.

Контрольні питання:
1. Для чого використовується перевантаження функцій?
2. Яким вимогам повинні задовольняти перевантажені функції?

Лабораторна робота №4

Значення формальних параметрів за замовчуванням і невизначене число


аргументів

Ціль роботи: навчитися використовувати при програмуванні на С++ значення


формальних параметрів функцій за замовчуванням і невизначене число аргументів.
Теоретичні відомості
У С++ формальним параметрам функцій можуть бути привласнені значення за
замовчуванням. Якщо параметр має значення за замовчуванням, то всі параметри, що стоять
праворуч від нього, теж повинні мати значення за замовчуванням. Ці значення передаються
функції, коли даний параметр не указаний при виклику. Тип аргументу за замовчуванням
перевіряється в момент оголошення функції, і сам аргумент обчислюється в момент виклику.
Наприклад:
#include<stdio.h>
void noName1(float x, int y, char z='B')
{
printf(“x=%0.1f y=%d z=%d\n”, x,y,z);
}
void noName2(float x, int y=16, char z='A')
{
printf(“x=%0.1f y=%d z=%d\n”, x,y,z);
}
void noName3(int y=1,float x=2.0,char z='C')
{
printf(“x=%0.2f y=%d z=%d\n”, x,y,z);
}
main()
{
noName1(1.0,2);
noName2(100.0);
noName3();
}

8
Результат роботи цієї програми:
x=1.0 y=2 z=66
x=100.0 y=16 z=65
x=2.00 y=1 z=67
Аргументи за замовчуванням часто корисні для простоти внесення змін у програму.
Існуюча функція може бути змінена додаванням аргументів без зміни вже існуючих її
викликів.
Для деяких функцій неможливо вказати кількість і типи аргументів, очікуваних при
виклику. При оголошенні такої функції наприкінці декларацій списку аргументів ставиться
еліпсис (...), що значить “і може бути ще аргументи”. Наприклад,
int printf(char* ...);
Це вказує, що у виклику функції printf повинен бути указаний принаймні один аргумент
char*; крім того, функція може мати або не мати додаткові аргументи. Наприклад:
printf(“Hello”);
printf(“Мене звуть %s %s\n”, first_name, second_name);
printf(“%d+%d=%d”,2,3,5);
Такій функції при інтерпретації аргументів доводиться покладатися на інформацію,
недоступну компілятору. У цьому випадку перший аргумент - це форматний рядок, що
містить спеціальні послідовності символів, що дозволяють функції printf() правильно
обробляти свої аргументи; %s значить очікування аргументу типу char* і т.д. Але
компілятору це невідомо, тому що вміст строкової константи не обробляється ним, тому він
не може гарантувати, що очікувані аргументи дійсно присутні, або що аргументи мають
правильні типи.
Окремий випадок еліпсиса, наприклад, func(...), повністю відключає перевірку
аргументів. Це може викликати безліч проблем, тому тільки в крайніх випадках, коли й
кількість аргументів, і тип аргументів можуть змінюватися, необхідно застосовувати еліпсис.
У всіх інших випадках для функцій, аргументи яких не повністю визначені, потрібно
застосовувати функції, що перевантажуються, і функції з аргументами за замовчуванням.
Найчастіше застосування еліпсиса - задати інтерфейс до бібліотек функцій мовою С, які були
визначені в той час, коли інших можливостей не було. Стандартний набір макросів, за
допомогою яких можна працювати з невизначеними аргументами в подібних функціях,
можна взяти із заголовного файлу <stdargs.h>.

Завдання:
Написати програму з використанням функції, що порівнює два рядки, задані в якості її
аргументів. Значення другого рядка задати за замовчуванням. Якщо при виклику функції
заданий тільки один аргумент, то знайти довжину цього рядка. Протестувати в функції
main() обидві варианти виклику функції.

Контрольні питання:
1. Які правила використання аргументів функцій за замовчуванням?
2. Для чого використовуються аргументи за замовчуванням?
3. Що таке функції з невизначеним числом аргументів?
4. Які особливості використання функцій з невизначеним числом аргументів?
5. У яких випадках варто використовувати функції з невизначеним числом аргументів?

Лабораторна робота №5
Програмування з використанням посилань
Ціль роботи: навчитися використовувати при програмуванні посилання й
параметри посилання.
Теоретичні відомості
Операція & означає посилання, якщо вона використовується в наступному контексті:
type &varName=InitExpr;
9
Посилання визначає місце розташування в пам'яті ініціалізуючого виразу InitExpr. Воно
істотно відрізняється від покажчика. Наприклад:
int y=16;
int &x=y;
printf(“x=%d\n”, x);
y=12;
printf(“x=%d\n”, x);
Результат роботи:
x=16
x=12
Посилальна змінна x визначає місце розташування змінної y у пам'яті в ініціалізуючому
виразі int &x=y; Змінна x обробляється як звичайна змінна типу int. При звертанні до такої
змінної немає необхідності зняття посилання. Коли змінній y привласнюється значення 12, то
змінна x приймає це нове значення, оскільки вона визначає місце розташування змінної y у
пам'яті. Наведений приклад еквівалентний наступному:
int y=16;
int *x=&y;
printf(“x=%d\n”, *x);
y=12;
printf(“x=%d\n”, *x);
Якщо в перший приклад додати рядок x=10;, значення y при цьому також стане рівним
10. Оскільки x - це посилання на змінну y, та будь-яка зміна змінної x приведе до зміни
змінної y.
Посилальна змінна повинна бути при оголошенні ініціалізована; оголошення
int &x;
неприпустимо, оскільки невідомо, на що посилається змінна x.
Посилання часто використовуються як формальні параметри у функціях. Це робиться,
по-перше, для підтримки режиму зв'язування (незважаючи на те, що посилання, фактично, є
звичайною змінною, у функцію вона передається не за значенням, а по посиланню, як
покажчики й масиви, відповідно, всі зміни, які відбуваються з нею у функції, видні визивній
функції). По-друге, посилання використовуються як параметри функції для того, щоб
уникнути копіювання змінних, переданих як параметри функції, у стек.
Наступний приклад демонструє різницю у використанні покажчиків і посилань як
параметрів функцій.
#include<stdio.h>
//Функція С++ використовує параметри-посилання
void increment(int &x)
{
x+=1;
}
//Функція С використовує параметр-покажчик
void increment(int *x)
{
*x=*x+1;
}
main()
{
int value=3;
increment(value);
int value1=5;
increment(&value1);
printf(“value=%d\n”,value);
printf(“value1=%d\n”,value1);

10
}
Результат роботи:
value=4
value1=5
Перша з перевантажених функцій використовує посилальний параметр x. Для
формального параметра int &x не потрібно ініціалізуючого виразу, тому що воно
підставляється при виклику функції increment. Зокрема, при виклику increment(value);
ініціалізуючий вираз приймає вигляд int &x=value. Компілятор розрізняє дві перевантажені
функції при викликах increment(value) і increment(&value1).

Завдання:
Написати програму з використанням посилань як параметрів функції для знаходження
мінімального з 3-х параметрів, що їй передані. Функція не повинна повертати значення,
результат помістити в перший аргумент і надрукувати в визивній функції .

Контрольні питання:
1. Що таке змінна типу посилання?
2. Як задати посилання на об'єкт?
3. У чому різниця посилання й покажчика?
4. Для чого використовуються посилання?

Лабораторна робота №6
Програмування з використанням
специфікатора inline

Ціль роботи: навчитися використовувати функції, що вбудовуються, при


програмуванні на С++.

Теоретичні відомості
Специфікатор inline може бути використаний перед визначенням функції для того, щоб
компілятор поміщав її код безпосередньо в місце виклику функції. Залежно від реалізації,
компілятор може виконувати або не виконувати цю вимогу.
Програма, представлена нижче, дозволяє досліджувати залежність продуктивності
програми від використання inline і не- inline функцій.
#include <stdio.h>
int maxo(int x, int y)
{
return(x>y)?x:y;
}
inline int maxi(int x, int y)
{
return(x>y)?x:y;
}
int main()
{
int i, j=5000, k, loop;
// Безпосереднє обчислення
for(loop=0; loop<100; loop++)
{
for( i=0; i < 10000; i++)
{
k=(i>j)? i : j ;
11
}
}
// Функція, що підставляється
printf( "Функція, що підставляється\n\n" );
for(loop=0; loop<100; loop++)
{
for( i=0; i < 10000; i++)
{
k=maxi(i, j) ;
}
}
// Звичайна функція
printf( "Звичайна функція\n\n" );
for(loop=0; loop<100; loop++)
{
for( i=0; i < 10000; i++)
{
k=maxo(i, j);
}
}
return 0;
}
Цикл із безпосереднім обчисленням і цикл із функцією, що підставляється, мають
ідентичний час виконання (23 умовних такти). Цикл зі звичайною функцією вимагає в три
рази більше часу (87 умовних тактів). Однак, при збільшенні складності функції, що
підставляється, виграш у часі зменшується, а при включенні у функцію оператора циклу
функція, що підставляється, звичайно перетвориться в таку, що не підставляється.
Рішення про те, використовувати чи ні специфікатор inline для певних функцій, повинне
прийматися після того, як досягнуте стійке функціонування програми. При цьому повинен
ураховуватися розмір функцій і число їхніх викликів у програмі. C++ дозволяє програмістові
проявляти гнучкість при ухваленні рішення про використання функцій inline.
Підстановка не змінює семантики виклику функції. Будь-яка функція може бути
оголошена як inline, але оптимізація виклику функції може бути зроблена не завжди, а
залежно від можливостей компілятора. Функція, що підставляється, може бути використана
тільки усередині файлу, у якому визначена, тому що визначення повинне бути очевидно із
точки виклику для того, щоб була можлива оптимізація.

Завдання:
Написати програму з використанням функцій, що підставляються, і звичайних функцій,
що виконують ті самі дії. За допомогою системного таймера визначити виграш у часі при
використанні inline-функцій (використати функцію clock() із бібліотеки time.h).

Контрольні питання:
1. Що таке функція, що вбудовується?
2. У яких випадках доцільно використовувати inline-функції?
3. Як використання inline-функцій впливає на ефективність програми?
4. Які правила використання функцій, що підставляються?

Лабораторна робота №7

Управління пам'яттю в С++

Ціль роботи: навчитися використовувати при програмуванні на С++ операції new і

12
delete.
Теоретичні відомості
Операції new і delete дозволяють програмісту виділяти пам'ять із купи й звільняти її.
Вони заміняють функції malloc, calloc і free, використовувані в С. Є три способи
використання операції new:
1. new type_name Приклад: float *r=new float;
2. new (type_name) Приклад: float *r=new ( float );
3. new type_name[expr] Приклад: float *r=new float[20];
Операція delete може використовуватися двома способами:
1. delete expr Приклад: float *r = new float[20];
delete r;
2. delete [expr] expr Приклад: float *r=new float[20];
delete[20] r;
Перші два способи використання операції new еквівалентні. Останній спосіб
використання операції delete може застосовуватися лише при звільненні пам'яті, зайнятої
масивом. Операція delete r у попередньому прикладі служить для тих же цілей.
Якщо операція delete використовується з покажчиком, для якого пам'ять не була
виділена за допомогою операції new, або була звільнена раніше, те це помилка. Кожна
система поводиться в цьому випадку по-своєму. У загальному випадку відповідальність за
недопущення подібних ситуацій лежить на програмісті.
Завдання:
Написати програму з використанням операцій управління пам'яттю для:
1. Знаходження максимального й мінімального елементів одномірного масиву.
2. Порівняння двох символьних рядків.
3. Визначення, чи є один рядок частиною іншого.

Контрольні питання:
1. Яка основна відмінність принципів управління пам'яттю в С и С++?
2. Як використовуються оператори new і delete?

Лабораторна робота №8
Опис класу
Ціль роботи: ознайомитися з розділами опису класу, навчитися використовувати при
програмуванні на С++ опис класу.

Теоретичні відомості
Опис класу складається з оголошень полів даних, прототипів функцій-членів і визначень
функцій-членів. В об’єктно-орієнтованій термінології поля даних - це екземпляри змінних
об'єктів (екземплярів) класу, а функції-члени - це методи, які обробляють повідомлення,
передані об'єктам класів.
Існує декілька способів розміщення різних частин опису класу. Перший простий спосіб -
це розміщення всіх трьох частин опису класу (полів даних, прототипів і визначень) в одному
файлі заголовка. Інший спосіб припускає розміщення полів і прототипів у файлі заголовка, а
визначень функцій-членів в одному або декількох файлах, що компілюються. Третя
можливість складається в розміщенні повного опису класу в одному файлі, що також містить
і програму користувача, що виконується. Останній варіант неприйнятний, коли доступ до
даного класу повинні здійснювати декілька користувачів.
Формальний опис класу починається ключовим словом class (за яким іде фігурна скобка,
що відкривається), а закінчується фігурною скобкою, що закривається, і крапкою з комою.
Приклад опису класу:
class ClassName
{
private: type data 1;
13
return_Type function1( parameter list);
public:
ClassName( parameter list a );// Перший конструктор
ClassName( parameter list b );// Другий конструктор
{ statements;
}
~ClassName( void ); // Деструктор
type data3;
return_Type function3(parameter list )
{ statements; }
};
Опис класу має три розділи, кожний з яких може містити поля даних і функції-члени. Ці
три розділи називаються: закритий (private), захищений (protected) і відкритий (public). Дані
й функції-члени, розташовані у відкритій частині класу, доступні для коду з будь-якого
користувальницького файлу, що включає даний опис класу за допомогою директиви
#include. Доступ до змінної (об'єкту) типу класу здійснюється тільки через відкритий розділ
опису даного класу.
Дані-члени є такими ж змінними, як елементи структури. Функції-члени - це функції,
визначені в рамках класу, які працюють із даними-членами цього класу. Використання
функцій-членів відрізняє клас від структури. Наприклад, клас, що визначає геометричне тіло
- сферу, може бути заданий у такий спосіб:
// Клас сфера
class Sphere
{
public :
float r; // Радіус сфери
float x, y, z; // Координати сфери
float volume ( )
{
return (r *r*r* 4* M_PI/3);
}
float surface_area ( )
{
return (r*r*4* M__PI) ;
}
};
Змінні типу клас описуються подібно структурним змінним. Наприклад, змінна, що
описує сферу, може бути оголошена так:
Sphere s;
Доступ до даних-членів і функцій-членів здійснюється за допомогою операції “крапка”,
як і доступ до полів структури. Таким чином, програму, що маніпулює зі змінною типу клас,
можна написати так:
#include <iostream.h>
#include <math.h> // M_PI (значення pi) визначається в MATH.Н
// Клас сфера
class Sphere
{
public :
float r; // Радіус сфери
float x, y, z; // Координати сфери
float volume ( ) // об'єм
{
return (r * r*r* 4 * M_PI/3);

14
}
float surface_area ( ) //площа поверхні
{
return (r*r*4* M_PI) ;
}
};
void main (void)
{
Sphere s ;
s.x = 1.0;
s.y = 2.0;
s.z = 3.0;
s.r = 4.0;
cout << “X =” << s.x
<< “Y =” << s.y
<< “Z =”<< s.z
<< “R =” <<s.r <<”\n”;
cout<<”Об'єм=”<<s.volume( );
}
Завдання:
Написати програму, що забезпечує найпростіші маніпуляції із класом, що визначає:
1. Картку відділу кадрів (забезпечити вивід на екран у зручному вигляді, друк повідомлення
при досягненні пенсійного віку).
2. Відомість на одержання зарплати ( забезпечити обчислення загальної суми по відомості й
друк прізвищ тих, кому гроші не нараховані).
Використовувати тільки відкритий розділ класу.
Контрольні питання:

1. Із чого складається опис класу?


2. Як задати змінну типу клас?
3. Чим клас відрізняється від структури?
4. Як здійснюється доступ до членів класу?

Лабораторна робота №9
Конструктори й деструктори
Ціль роботи: ознайомитися із правилами побудови й застосування конструкторів і
деструкторів при програмуванні на С++.

Теоретичні відомості
Конструктор - це функція-член, що викликається автоматично для створення й
ініціалізації екземпляра певного класу й має ім'я, що збігається з ім'ям класу. Його основне
призначення - ініціалізувати екземпляр значенням, припустимим для даного класу. У
шаблоні опису класу (лабораторна робота №8) показані два конструктори класу ClassName.
Створення й ініціалізація змінних даного типу можуть бути досить складні.
Конструктори класів автоматизують ці процедури, виключаючи імовірності пропуску кроків
або можливість їхнього неправильного виконання. Не слід включати в конструктор дії, які
можуть завершитися відмовою, тому що ця функція не повертає значення, не має навіть тип
void, і якщо якісь дії не будуть виконані, наприклад, виділення пам'яті, функція, що створює
клас, про це не довідається.
Конструктор викликається автоматично в момент створення екземпляра. Він не може
бути викликаний подібно звичайної функції-члену, тому немає можливості повторно
ініціалізувати екземпляр класу.
Нижче наведений текст завершеної програми, що використовує клас Sphere. Вона
15
показує, як конструктор створює й ініціалізує змінну s класу Sphere.
// Демонструє використання функції-конструктора класу
#include <iostream.h>
#include <math.h> // M_PI (значення pi) визначається в math.h
// Клас сфера
class Sphere
{
public :
float r; // Радіус сфери
float x, y, z; // Координати сфери
Sphere (float xcoord, float ycoord, float zcoord, float radius)
{ x = xcoord; y = ycoord; z = zcoord; r = radius; }
float volume ( )
{
return (r*r*r*4*M_PI/3) ;
}
float surface area ( )
{
return (r*r*4* M_PI) ;
}
};
void main (void)
{
Sphere s(1.0, 2.0, 3.0, 4.0);
cout <<"X = " << s.x
<< "Y = " << s.y
<< "Z = " <<s.z
<< "R = "<< s.r
<<"\n";
}
Результат виконання цієї програми:
X=1 Y=2 Z=3 R=4
Конструктор може мати й порожній список параметрів. Один або кілька параметрів
конструктора можуть мати значення за замовчуванням.
Функція-член ~ClassName - це деструктор. Деструктор також має ім'я класу, але містить
перед ім'ям приставку ~ (тільда). Він повинен мати порожній список параметрів і не мати
типу значення, що повертається. Якщо конструктор виділяє пам'ять для створення змінної
типу клас, то функція деструктор повинна звільняти виділену пам'ять і повністю руйнувати
змінну класу. Його тіло може містити код, що виконується, наприклад, вивід останніх
значень даних-членів класу, що зручно при налагодженні. Деструктор викликається
автоматично при знищенні екземпляра (локальний екземпляр знищується, коли функція,
його утримуюча, завершує роботу, глобальний - коли програма завершує роботу). Якщо
необхідно зруйнувати кілька екземплярів, деструктори викликаються в порядку, зворотному
викличу конструкторів.
Приклад демонструє виклик деструктора й виконання ним певних дій.
// Демонструє використання функції-деструктора класу
# include <iostream.h>
# include <math.h> //M_PI (значення pi) визначається в math.h
// Клас сфера
class Sphere
{ public :
float r; // Радіус сфери
float x, y, z; // Координати сфери

16
Sphere ( float xcoord, float ycoord, float zcoord, float radius)
{ x = xcoord; y = ycoord; z = zcoord; r = radius; }
~Sphere ( )
{cout << "Sphere ("<< x <<“,”<<y<<”,”<< z <<”,”<< r <<“)”destroyed\n"; }
float volume ( )
{
return (r * r* r* 4 * M_PI/3);
}
float surface area ( )
{
return (r*r*4* M_PI) ;
}
};
void main (void)
{
Sphere s (1.0, 2.0, 3.0, 4.0);
cout << “ X = “ << s.x
<< “ Y = “ << s.y
<< “ Z = ” << s.z
<< “ R = “ <<s.r
<<”\n”;
}
Результат виконання цієї програми:
X = 1 Y=2 Z=3 R=4
Sphere (1, 2, 3, 4) destroyed
Завдання:
Для класу, створеного в л.р. № 8, задати конструктор (для виділення пам'яті, відкриття
файлів, завдання початкових значень при необхідності) і деструктор (для звільнення пам'яті,
закриття файлів, друку останніх значень).

Контрольні питання:
1. Для чого використовується конструктор?
2. Які правила використання конструкторів?
3. Коли викликається конструктор?
4. Для чого використовується деструктор?
5. Наведіть правила використання деструкторів.

Лабораторна робота №10

Закриті члени класу


Ціль роботи: ознайомитися із властивостями закритої секції опису класу, навчитися
використовувати при програмуванні закриті члени класу.
Теоретичні відомості
Основною метою об’єктно-орієнтованого програмування є відділення реалізації об'єкта від
його використання. Це можна зробити за допомогою механізму приховання даних. Він
забезпечує наступні переваги:
- користувачі класу, тобто інші програмні об'єкти, що використовують елементи
класу, ізолюються від реального представлення даних;
- розроблювачі класу можуть змінювати представлення й джерело надходження
даних, не змінюючи структуру програми, у якій цей клас використовується.
Всі функції-члени й дані-члени класу мають атрибут, називаний його видимістю.
Видимість члена класу обмежує коло його потенційних користувачів. С++ має три рівні
видимості: загальний (public), закритий або приватний (private) і захищений (protected).
17
Парадигма приховання даних не дозволяє функціям, що не є членами класу, звертатися до
даних-членів. Наприклад, оголошення класу Sphere:
// Клас сфера
class Sphere
{ public :
Sphere ( float xcoord, float ycoord, float zcoord, float radius)
{ x = xcoord; y = ycoord; z = zcoord; r = radius; }
~Sphere ( )
{cout << "Sphere ("<< x <<“,”<<y<<”,”<< z <<”,”<< r <<“)”destroyed\n"; }
float volume ( )
{
return (r * r* r* 4 * M_PI/3);
}
float surface area ( )
{
return (r*r*4* M_PI) ;
}
private:
float r; // Радіус сфери
float x, y, z; // Координати сфери
float cube();
float square(); };
Тут дані-члени класу оголошені як private – закриті. До них мають доступ тільки
функції-члени цього класу, будь-які інші функції до них доступу не мають. Оголошені
закритими функції float cube(); float square(); також не можуть бути викликані за межами
класу.
Використання загальної й приватної видимості класу дає можливість сховати члени
класу від функцій, що не входять у цей клас. У класі можуть бути як загальні, так і приватні
члени. Загальні члени класу визначають те, як функції, що не є членами цього класу, можуть
використовувати даний клас. Ці члени називаються загальним інтерфейсом класу.
Добре розроблений інтерфейс звичайно містить у собі функції-члени, які виконують
наступні дії:
- ініціалізують змінну типу даного класу. Конструктори завжди мають тип public;
- звільняють пам'ять, використану змінною класу. Деструктори завжди мають тип
public;
- роблять первісну установку значень приватних змінних-членів класу;
- обчислюють значення приватних змінних-членів класу;
- виконують реальну роботу об'єкта.

Завдання:
Модифікувати програми з лабораторних робіт №№ 8,9 таким чином, щоб клас містив
закриті змінні й мав добре розроблений інтерфейс.

Контрольні питання:
1. У чому складається відмінність відкритих і закритих членів класу?
2. Які правила оголошення закритих членів класу?
3. Що таке загальний інтерфейс об'єкта й для чого він використовується?

Лабораторна робота №11


Перевантаження конструкторів

Ціль роботи: ознайомитися із призначенням і правилами використання перевантажених


конструкторів і одержати практичні навички написання програм з

18
використанням перевантажених конструкторів.

Теоретичні відомості

Конструктори мають у своєму розпорядженні додаткові можливості: вони можуть


мати аргументи і їх можна перевантажувати.
У класах може бути оголошене декілька конструкторів для автоматичної ініціалізації
об'єкта класу при його створенні, практично виключаючи у такий спосіб можливість
помилок, викликаних використанням неініціалізованих даних. У класах також може бути
оголошений (але тільки один) деструктор, викликуваний для очищення при виході об'єкта
класу з області видимості. Конструктори схожі на звичайні функції-члени, але вони рідше
викликаються безпосередньо в операторах програми. С++ автоматично викликають
конструктори для ініціалізації й очистки об'єктів класу.
У прикладі оголошується клас TTime. Цей клас має два конструктори для ініціалізації
створених об'єктів класу TTime. Це заголовний файл.
// time.h
# ifndef __TIME6.H
# define __TIME6_H 1
# include <iostream.h>
# include <time.h>
# include <string.h>
class TTime {
private :
long dt; //Дата й час - у секундах від 1 січня 1970року
char *dts; //Подання дати й часу у вигляді рядка
void DeleteDts (void); //Видаляє покажчик dts
public :
TTime (); //Конструктор
TTime (int m, int d=-1, int y=-1, int hr=-1, int min=-1);//Конструктор
~Ttime (); //Деструктор
void Display (void) { cout<<ctime (&dt);}
void GetTime (int &m, int &d, int &y, int &hr, int &min);
void SetTime (int m=-1, int d=-1, int y=-1, int hr=-1, int min=-1);
const *char *GetsTime (void);
void ChangeTime (long nminutes)
{dt+=(nminutes*60); DeleteDts ();}
};
# endif
Тут оголошена функція TTime() як конструктор класу TTime. Оскільки конструктор
оголошується без параметрів, він викликається за замовчуванням. У наступному рядку
оголошується другий перевантажений конструктор для TTime. Цей конструктор також має
однакове із класом ім'я, але цього разу він оголошений з п'ятьма цілими параметрами, з яких
останні чотири передають значення за замовчуванням - 1.
// time.cpp - реалізація класу TTime
# include <dos.h>
# include “time.h”
// конструктор за замовчуванням
TTime::TTime ()
{
dts=NULL; // Обнуління поточного рядка
SetTime (-1, -1, -1, -1, -1 );
}
//перевантажений конструктор
19
TTime::TTime (int m, int d, int y, int hr, int min)
{
dts= NULL; // Обнуління поточного рядка
SetTime (m, d, y, hr, min);
}
// Деструктор
TTime::~Ttime ()
{
delete dts; //Видалення рядка, що зберігається в об'єкті
}
// Видалення покажчика dts
void TTime::DeleteDts (void)
{
delete dts; // Видалення рядка, що зберігається в об'єкті
dts = NULL; // обнулення покажчика
}
// Повертає поточні дані-члени дату й час
void TTime::GetTime (int &m, int &d, int &y, int &hr, int &min)
{
struct date ds;
struct time ts;
unixtodos (dt, &ds, &ts);
y=ds.da_year;
m=ds.da_mon;
d=ds.da_day;
hr=ts.ti_hour;
min=ts.ti_min;
}
//Установлює член dt
void TTime::SetTime (int m, int d, int y, int hr, int min)
{
struct date ds;
struct time ts;
getdate (&ds); // Узяття поточних дати й часу
gettime (&ts);
if (y>=0) ds.da_year=y;
if (m>=0) ds.da_mon=m;
if (d>=0) ds.da_day=d;
if (hr>=0) ts.ti_hour=hr;
if (min>=0) ts.ti_min;
ts.ti_sec=0;
ts.ti_hund=0;
dt=dostounix (&ds, &ts);
DeleteDts (); // Видалення поточного рядка
}
const char *TTime::GetsTime (void)
{
if (dts) // Повертає поточний рядок, якщо він ініціалізований
return dts;
dts=strdup (ctime(&dt));
return dts;
}
У програмі задається конструктор TTime за замовчуванням - TTime(). Як і у випадку всіх

20
інших функцій-членів, заголовку реалізації передує ім'я класу й оператор розширення
області видимості. У цьому випадку символьний покажчик dts установлюється рівним
NULL, це значить, що покажчик ще не посилається на рядок. Оскільки С++ автоматично
викликає конструктор для ініціалізації об'єкта класу TTime, отже, всі такі об'єкти
гарантовано мають ініціалізований покажчик dts. Конструктор також викликає функцію-член
SetTime() з аргументами за замовчуванням –1, установлюючи в такий спосіб в об'єкті класу
поточну дату й час.
Задається другий перевантажений конструктор. Він схожий на конструктор за
замовчуванням, але оголошений з параметрами дати й часу й може використовуватися для
ініціалізації значень об'єкта класу. Перевантажені конструктори схожі на перевантажені
функції-члени. У класі можна оголосити стільки конструкторів, скільки буде потрібно, за
умови, що вони відрізняються хоча б одним своїм параметром. Конструктори повинні мати
однакове ім'я із класом, у якому вони визначені й, отже, перевантажуються за визначенням.
Завдання:
Написати програму, яка б реалізовувала бібліотечний каталог. Програма повинна
запитувати назву книги, автора, рік видання, ціну й видавати загальну кількість книг,
загальну вартість, назви книг, які дорожче певної суми. У цій програмі повинен бути
створений клас, у якому використовуються перевантажені конструктори.
Контрольні питання

1. Як перевантажуються конструктори?
2. Для чого використовуються перевантажені конструктори?
3. У якому місці програми звичайно визначається конструктор?

Лабораторна робота №12

Просте спадкування. Захищені члени класу


Ціль роботи: Навчитися використовувати при програмуванні на С++ просте
спадкування й захищені члени класу.
Теоретичні відомості

Просте спадкування описує спорідненість між двома класами, один із яких успадковує
другий. Клас, що перебуває на вершині ієрархії, називається базовим класом. Інші класи
називаються похідними класами.
Похідний клас і сам може бути базовим, який успадковують інші класи. Похідний клас
успадковує з базового дані-члени й функції-члени, але не конструктори й деструктори. У
загальному випадку похідний клас починає своє існування з копіювання членів базових
класів, включаючи всі члени, успадковані з більш далеких родинних класів.
Оголошення похідних класів. Нехай базовий клас Tbase оголошений у такий спосіб:
class Tbase {
private:
int count; // Представляє закриті дані класу
public:
Tbase () { count=0;}
void Set_Count (int n ){count=n;}
int Get_Count (void) {return count ; }
};
Член count містить закриті дані класу. Тільки відкриті функції – члени Tbase
безпосередньо можуть посилатися на count. Конструктор за замовчуванням Tbase()
ініціалізує count нульовим значенням. Функція-член Set_Count() привласнює count нове
значення. Функція член Get_Count() повертає поточне значення count .
Тепер необхідно створити новий клас, що має всі властивості Tbase (),але крім того,
здатний збільшувати й зменшувати значення об'єкта на задану величину. Замість зміни
21
Tbase, можна вивести з нього новий клас, що додатково має нові, необхідні властивості:
Class Tderived: public Tbase{
public:
Tderived(): Tbase() { }
void ChangeCount (int n) {Set Count (GetCount ()+n);}
}
Похідний клас названий Tderived. Одразу за ім'ям класу потрібна двокрапка й одне із
ключових слів (public, protected або private). Після цих елементів потрібне ім'я базового
класу (Tbase), від якого Tderived одержує спадок.
Відкриті члени класу залишаються відкритими й у похідному класі. Всі закриті члени
предка залишаються закритими у своєму спочатку оголошеному класі, і в похідному класі не
можна одержати доступ до цих членів предка.
Захищений член класу - щось середнє між закритим і відкритим членом. Подібно
закритим членам, захищені доступні тільки функціям-членам класу. Поза класом захищені
члени невидимі. Подібно відкритим членам, захищені успадковуються похідними класами й
доступні функціям-членам похідних класів.
Закриті члени доступні тільки в класі, у якому вони оголошені.
Захищені члени доступні членам їхнього власного класу й всіх членів похідних класів.
Відкриті члени доступні членам їхнього власного класу, членам похідних класів і всіх
інших користувачів цього класу. (Дружні функції можуть обходити ці обмеження)
Приклад:
Class Nfnyclass {
private:
int A ; //доступні тільки членам Nfnyclass
void fa(void);
protected:
int B ;
void fb(void);
public:
int C;
void fc(void);
};
Специфікатори доступу private, protected і public можуть також передувати імені
базового класу. Якщо жоден зі специфікаторів доступу не заданий, члени наслідуваного
класу за замовчуванням закриті.

Завдання:
Написати програму, використовуючи базовий і похідний класи («Люди» і «Студенти»),
а також захищені члени класу, що створювала масив об'єктів типу похідного класу,
ініціалізувала б їх відомостями про 10 студентів (ПІП, рік народження й середній бал сесії) і
видаляла їх.
Контрольні питання

1. Якими є члени базового класу в похідному за замовчуванням?


2. Чи доступні захищені члени базового класу в похідному класі?
3. Що не успадковує похідний клас із базового?

Лабораторна робота №13

Дружні функції

Ціль роботи: Навчитися використовувати при програмуванні на С++ дружні функції.

22
Теоретичні відомості

Важливою особливістю класів є їхня можливість ізолювати (приховувати) дані. За


замовчуванням дані-члени мають тип private – тобто використовуються тільки функціями-
членами класу. Тому може викликати подив факт існування категорії функцій, спеціально
призначених для усунення цієї можливості. Такі функції називаються «друзями» (friend)
класу або «дружніми». Дружні функції дозволяють використовувати приватну інформацію
класу, не будучи членами цього класу. Ці функції, не описувані в самому класі, можуть
працювати з тими ж ресурсами класу, до яких звертаються функції-члени.
Достоїнство дружніх функцій у тім, що вони - зовнішні стосовно опису класу, як
показано нижче:
#include<iostream.h>
#include<time.h>
#include<string.h>
#include<stdlib.h>
clas time_class;
{
long secs;
friend char * present_time(time_class); // дружня функція
public:
time_class(char*);
};
time_class::time_class(char*tm);
}
char *hours, *minutes, seconds;
// дані повертаються у вигляді рядка наступного формату
// ( день місяць дата години: хвилини: секунди рік )
// Тому необхідно пропустити три лексеми: день, місяць і дату
hours = strtok (tm,” ”);
hours = strtok (0,” “);
hours = strtok (0,” ”);
// одержуємо час із рядка
hours = strtok (0,” “);
minutes = strtok (0,” “);
seconds = strtok (0,” “);
// перетворимо дані в тип long і підрахуємо секунди
secs = atol (hours)*3600;
secs+=atol (minutes)*60;
secs+=atol (seconds);
}
char *present_time (time_class); //прототип
void main (void)
{
//одержуємо рядок “час_дата”
struct tm *ptr;
time_t ltime ;
ltime = time(NULL);
ptr = local time(& ltime);
time_class tz (asctime (ptr));
cout<<”Рядок інформації дата/час :”<<asctime (ptr) <<endi;
cout<<”час переведений в секунди : “ <<present_time (tz)<<endl;
return (0);
}
23
char *present_time(time_class tz)
{
char *ctbuf;
ctbuf=new char[40];
long int seconds_total;
seconds_total = tz.secs;
ltoa (seconds_total, ctbuf, 10);
return (ctbuf);
}
В описі класу ключове слово friend використовується разом із визначенням функції
present_time(). Після перегляду лістингу можна побачити, що ця функція, розташована поза
класом, з'являється після опису функції main(). Інакше кажучи, її синтаксис традиційний для
зовнішніх стосовно описаного класу функцій С++.
У функції main() системний час утворюється за допомогою типу time_t і пов'язаної з
ним структури tm. У даній програмі ltime – це ім'я змінної, пов'язаної з типом time_t. У
наступних двох рядках коду локальний час ініціалізується, а потім зчитується в покажчик
ptr. За допомогою функції asctime(ptr) цей покажчик перетвориться в покажчик, що
посилається на ASCII – рядок, що містить дату й час.
Отриманий рядок передається класу, для чого оголошується змінна tz,пов'язана із
класом time_class:
time_class tz (asctime(ptr));
Для визначення коду, необхідного для перетворення рядка в цілі дані, використовується
конструктор time_class(char*). Перетворення виконується за допомогою функції strtok().
Інформація “дата - час“ повертається в досить незвичному форматі. При її обробці
функція strok() повинна використовувати пробіл як роздільник для пропуску строкової
інформації, що представляє день, місяць і дату. У наведеній програмі змінна hours спочатку
служить у якості проміжної змінної для небажаних лексем. Наступний роздільник, двокрапка
(:), служить для вибору з рядка лексем, що представляють собою години та хвилини. І
нарешті, кількість секунд можна одержати, якщо зчитувати рядок до наступного пробілу.
Після цього строкова інформація перетвориться в тип long і перераховується у відповідну
кількість секунд. Змінна secs є приватною для класу, однак вона доступна для дружньої
функції.
Дружня функція present_time() одержує обчислену кількість секунд tz.seconds і
перетворює її назад у символьний рядок. Пам'ять для зберігання цього рядка виділяється за
допомогою операції new. Знову створений рядок є результатом використання дружньої
функції.
Програма виводить на друк наступну інформацію:
Рядок інформації дата/час :
Час переведений в секунди :
По-перше, оператор cout виводить на екран рядок, отриманий за допомогою функції
asctime(). Ця інформація поставляється функцією time_t(), і вона доступна функції main().
По-друге, друкується системний час, для чого значення present_time переміщається в потік
cout.

Завдання:
Написати програму, що переводить розмір кута із радіан у градуси з використанням
дружньої функції.
Контрольні питання:
1. Для чого використовуються дружні функції?
2. Як визначаються дружні функції?

24
Лабораторна робота №14

Перевантаження операторів

Ціль роботи: Дослідити принципи перевантаження операторів у С++ і навчитися


використовувати при програмуванні на С++ перевантаження операторів.
Теоретичні відомості

У С++ можливо перевантажувати в деякому класі такі операції, як +, -, *, / і т.д..


Ідея перевантаження операцій присутня в багатьох мовах програмування, навіть якщо
вона й не реалізована явно. У всіх мовах, що компілюються, можна скласти два числа із
плаваючою крапкою, два цілих числа і т.д. за допомогою однієї операції додавання +. У С++
ця проста концепція значно розширена.
Суть перевантаження операцій - одна операція використовується з різними типами
даних. У С++ можна перевантажувати наступні операції:
+ - * / = < > += -=
*= /= << >> >>= <<= == != <=
>= ++ -- % & ^ ! delete
Головне обмеження полягає в тому, що синтаксис і пріоритет операції не повинні
змінюватися в порівнянні зі спочатку визначеними.
Інший важливий момент - перевантаження операції можливе тільки в області дії того
класу, у якому вона виконується.
Для перевантаження операції використовується ключове слово operator, за яким слідує
сама операція:
тип operator операція (список параметрів)
Наприклад :
аngle_value operator + ( angl_argument);
Тут angle_value – назва типу класу, за ним іде ключове слово operator, символ самої
операції (+) і параметр, переданий перевантаженій операції.
У межах області дії відповідним чином описаного класу можна безпосередньо складати
значення кутів, виражені в градусах, хвилинах і секундах:
Angle_value angle1 (“37( 15' 56\””);
Angle_value angle2 (“10( 44' 44\”” );
Angle_value angle3 (“75( 17' 59\””);
Angle_value angle4 (“130( 32' 54\””);
Angle_value sum;
Sum=angle1+angle2+angle3+angle4 ;
Приклад:
#include<iostream.h>
#include<stdlib.h>
#include<string.h>
class angle_value{
int degrees, minutes,seconds;
public:
angle_value() {degrees=0;
minutes-0;
seconds=0;}// конструктор
angle_value(char*);
angle_value operator + (angle_value);
char* info_display(void);
};
angle_value::angle_value(char *angle_sum)
{
25
degrees=atoi(strok (angle_sum,”(”));
minutes=atoi (strok(0,”'”));
seconds=atoi (strtok (0,”\””));
}
angle_value angle_value::operator+(angle_value angle_sum)
{
angle_value ang;
ang.seconds = (seconds+ angle_sum.seconds)%60;
ang.minutes = ((seconds + angle_sum.seconds)/60+minutes + angle_sum, minutes )%60;
ang.degrees=((seconds+angle_sum/seconds)/60+ minutes+angle_sum/minutes)/60;
ang.degrees +=degrees+angle_sum.degrees;
return ang;
}
char* angle_value::info_display()
{
char *ang[15];
//для формування потрібен був файл strstream.h
strstream(*ang,sizeof (ang))<<degrees<<”(”
<<minutes<<”'”
<<seconds<<”\””
<<ends;
return *ang;
}
main ()
{
angle_value angle1 (“37( 15' 56\””); //символ градуса -alt-248
angle_value angle2 (“10( 44' 44\”” );
angle_value angle3 (“75( 17' 59\””);
angle_value angle4 (“130( 32' 54\””);
angle_value sum;
sum=angle1+angle2+angle3+angle4 ;
cout<<”сума кутів:”<<sum.info_display()<<endl;//сума кутів
return 0;
}
Програма обчислює й виводить на екран суму чотирьох кутів :
Сума кутів : 253 градусів

Завдання :
Написати програму, що виконує арифметичні операції над комплексними числами,
використовуючи перевантажені операції.
Контрольні питання
1. У якій області видимості можливе перевантаження операцій ?
2. У чому складається обмеження при використанні перевантаження операцій?
3. Який синтаксис перевантаження операцій?

Лабораторна робота №15


Алгоритм. Подання алгоритму.

Ціль роботи: практичне вивчення процесу специфікації алгоритмів за допомогою схем.

Теоретичні відомості
Слово «Алгоритм» походить від слова «Algorithmi», що є латинською транслітерацією
26
арабського імені узбецького математика Мохаммеда ібн Муса аль-Хорезмі, який в IX
столітті (825 г.) описав правила виконання чотирьох арифметичних дій у десятковій системі
числення.
Алгоритм — точне приписання, яке задає процес, що починається з довільних вихідних
даних і спрямований на одержання повністю обумовленого цими вихідними даними
результату. Алгоритмічний процес являє собою етапи (кроки) послідовного перетворення
вихідних даних у результуючі.
Представлений алгоритм пишеться на природній (розмовній) мові, з використанням термінів
галузі, у якій вирішується завдання. Уважається, що по представленому алгоритму можна
написати програму практично на будь-якій мові програмування.
Текст алгоритму повинен бути таким, що читається без додаткових пояснень автора. В
алгоритмі не показують опису (змінних), тому що описи - це особливості мови
програмування, а не алгоритму. Алгоритм не може бути «прив'язаний» до конкретної мови
програмування. У ньому описуються конкретні дії.
Існують різні форми опису алгоритмів: блок-схемний, словесний, формульно-словесний,
операторних схем та ін. Перші три форми опису алгоритмів є найпоширенішими, з них -
перший представляє логіку алгоритму наочно у вигляді схеми, використовуючи блоки, які
містять покрокові дії алгоритму.
Основні вимоги до блок-схеми:
Схема виконується в позначеннях стандарту (ГОСТ 19.701-90 ЄСПД. Схеми алгоритмів,
програм, даних і систем. Позначення умовні й правила виконання).
Після назви алгоритму необхідно описати цільові функції алгоритму в цілому.
У блоках схеми звичайно пишуть короткі тексти. Іноді її доповнюють пояснення в розділі
«Пояснення до алгоритму». Стандарт не накладає обмежень на символьні описи процесів
усередині блоків. Можна використовувати природню мову, математичну нотацію або мову
програмування. При використанні природньої мови на структуру фраз останнього накладають
ряд обмежень і одержують так звану структуровану природню мову.
Структурована природня мова застосовується для читабельного, строгого опису специфікацій
процесів. Вона є розумною комбінацією строгості мови програмування й читабельності
природньої мови й складається з підмножини слів, організованих у певні логічні структури й
арифметичні вирази.
До складу мови входять наступні основні символи:
− дієслова, орієнтовані на дію й застосовувані до об'єктів;
− терміни, певні на будь-якій стадії проекту ПЗ (наприклад, завдання, процедури, символи
даних і т.п.);
− прийменники й союзи, використовувані в логічних відношеннях;
− загальновживані математичні, фізичні й технічні терміни;
− арифметичні рівняння.
При використанні структурованої природньої мови дієслова в описах процесів повинні бути
активними, недвозначними й орієнтованими на цільову дію (заповнити, обчислит и, вит ягт и, а
не модернізуват и, обробит и). Логіка специфіцируємих процесів повинна бути виражена чітко
й недвозначно.
У поясненні до схеми описують дії окремих частин схеми і якщо буде потреба – дії окремого
блоку. Пояснення повинні містити деяку додаткову інформацію;
Керування за схемою повинне в основному йти вниз (вправо), вертаючись назад тільки в
циклах;
27
Альтернативно виконувані гілки повинні розміщатися паралельно;
Змінні повинні бути ініціалізовані в блоці з описом. Якщо коментарі до змінної не містяться
в блоці, то її опис необхідно помістити в розділ «Пояснення до алгоритму»;
Вихідні й вхідні блоки процедур (функцій) повинні містити, відповідно, вхідні (формальні)
параметри й значення, що вертаються;
Блоки можна поєднувати в більші пунктирними лініями, які потрібно коментувати –
описувати їхнє призначення.
ГОСТ 19.701-90 поширюється на умовні графічні позначення (символи) у схемах алгоритмів
і програм, які відображають основні операції процесу обробки даних і програмування для
систем програмного забезпечення обчислювальних машин, комплексів і систем незалежно
від них призначення й області застосування.
Стандарт не поширюється на записи й позначення, які містяться усередині символу або
поруч із ним і служать для уточнення виконаних їм функцій.
Стандарт установлює перелік, найменування, форму, розміри символів і функції, які
відображаються символами.
Умовні графічні позначення в блок-схемах алгоритмів і програм
Перелік, найменування, позначення й розміри деяких обов'язкових символів і функції, які
відображаються ними, в алгоритмі й програмі обробки даних, наведені нижче.
Процес
Виконання операції або групи операцій, у результаті яких змінюється значення, форма
подання або розміщення даних.

Рішення b
Вибір напрямку виконання алгоритму або програми залежно від деяких змінних умов.

Модифікація b

Виконання операцій команди, що змінює, або групи команд, які міняють програму.
a

Визначений процес b

Використання раніше створених і окремо описаних алгоритмів або програм.


0,15a

b 28
Сортування
Упорядкування множини по заданих ознаках

60
Уведення-Вивід
Перетворення даних у форму, придатну для обробки (уведення) або відображення
результатів обробки (вивід).
0,25a

b
Документ
Уведення-Вивід даних, носієм яких служить папір.
R=a 0,5а 0,5а

a R

R
Лінія потоку b
Вказівка послідовності зв'язків між символами.

З'єднувач
Вказівка зв'язку між перерваними лініями потоку, які зв'язують символи.

∅0,5а

Пуск - останов
Початок, кінець, переривання процесу обробки даних або виконання програми.
R=0.25a R

0,5a

b
29
Міжсторінковий з'єднувач
Вказівка зв'язку між роз'єднаними частинами схем алгоритмів і програм, розташованих на
різних аркушах. 0,5а

0,6а

0,2а
Співвідношення геометричних елементів символів
Розмір а повинен вибиратися з ряду 10, 15, 20 мм. Допускається збільшувати розмір а на
число, кратне 5. Розмір b рівний 1.5а.
Напрямок ліній потоку за замовчуванням (без вказівки стрілок) - вправо й униз. У випадку,
якщо потік спрямований уліво або нагору, потрібно вказувати стрілки на лінії потоку. Не
допускається перетинання ліній потоку. Якщо це необхідно, потрібно користуватися
внутрішнсторінковим з'єднувачем.
Завдання
Скласти блок-схему алгоритму розв'язку завдання (передбачається, що даний алгоритм буде
реалізований програмно). Керуватися вимогами ГОСТ 19.701-90.
1) Дано 20 цілих чисел. Визначити, скільки раз зустрічається найбільше значення.
2) Дано 20 дійсних чисел. Визначити, скільки з них більше своїх "сусідів", тобто
попереднього й наступного чисел.
3) Визначити число, одержуване виписуванням у зворотному порядку цифр заданого
натурального числа.
4) Дано 20 речовинних чисел. Знайти порядковий номер того з них, яке ближче всього
до якого-небудь цілого числа.
5) Дано 10 речовинних чисел. Обчислити різницю між максимальним і мінімальним
числами.
6) Дана послідовність із 20-ти цілих чисел. Визначити, зі скількох негативних чисел вона
починається.
7) Визначити, чи є задане натуральне число паліндромом, тобто таким числом,
десятковий запис якого читається однаково ліворуч праворуч і праворуч ліворуч.
8) Дано 20 дійсних чисел. Визначити, чи утворюють вони зростаючу послідовність.
9) Дані ціле n>0 і послідовність із n дійсних чисел, серед яких є хоча б одне негативне
число. Знайти величину найбільшого серед негативних чисел цієї послідовності.
10) Знайти суму цифр заданого натурального числа.
11) Дана непуста послідовність різних натуральних чисел, за якою іде 0. Визначити
порядковий номер найменшого з них.
12) Підрахувати k – кількість цифр у десятковому записі цілого ненегативного числа n.
13) Логічній змінній t привласнити значення true або false, залежно від того, чи є задане
натуральне число k ступенем 3 чи ні.
14) Обчислити: y= sin1 + sin1.1 + sin1.2 + …+ sin2.
15) Дана послідовність із 100 різних цілих чисел. Знайти суму чисел цієї послідовності,
розташованих між максимальним і мінімальним числами (у суму включити обидва
цих числа).
16) Дано 100 дійсних чисел. Роздрукувати їх у зворотному порядку по 6 чисел у рядку.
17) Даний текст із 80 літер. Визначити, чи симетричний він, тобто чи читається він
однаково ліворуч праворуч і праворуч ліворуч.
18) Даний текст, що містить від 1 до 70 букв, за яким іде крапка. Вивести текст у
зворотному порядку.
19) Дана послідовність цілих чисел. Усі позитивні числа цієї послідовності записати в 2-у

30
послідовність.
20) Дана послідовність цілих чисел. Записати квадрати чисел цієї послідовності в 2-у
послідовність.
21) Даний текст із 80 літер. Вивести спочатку всі цифри, що входять у нього, а потім усі
інші літери, зберігаючи при цьому взаємне розташування літер у кожній групі.
22) Дано дві послідовності по 30 цілих чисел у кожній. Знайти найменше серед тих чисел
першої послідовності, які не входять у другу послідовність (уважаючи, що хоча б
одне таке число є).
23) Дана непуста послідовність слів з рядкових латинських букв. Текст завершується
крапкою. Вивести всі букви, які в цій послідовності зустрічаються найчастіше.
24) Надрукувати заданий текст із 100 літер, вилучивши з нього повторні входження
кожної літери.
25) Визначити, скільки різних літер входить у заданий текст, що містить більш 100 літер і
кінчається крапкою (сама крапка в текст не входить).
26) Даний текст із рядкових латинських букв, за яким іде крапка. Вивести за абеткою всі
букви, які входять у цей текст по одному разу.
27) Даний непустий текст із цифр, за якими іде крапка. Надрукувати цифру, що
зустрічається в цьому тексті частіше інших.
28) Дана непуста послідовність слів з рядкових букв кирилиці, яка закінчується крапкою.
Надрукувати за абеткою всі дзвінкі приголосні букви, які входять у текст.
29) Дана непуста послідовність слів з рядкових букв кирилиці; між сусідніми словами -
кома, за останнім словом - крапка. Надрукувати за абеткою всі глухі приголосні
букви, які входять у непарні слова.
30) Дана непуста послідовність слів з рядкових букв кирилиці; між сусідніми словами -
кома, за останнім словом - крапка. Надрукувати за абеткою всі приголосні букви, які
входять у парні слова.

Лабораторна робота № 16

Flow-Форми й діаграми Нассі-Шнейдермана

Ціль роботи: Вивчення й практичне застосування принципів розробки алгоритмів за


допомогою візуальних мов Flow-Форм і діаграм Нассі-Шнейдермана.

Теоретичні відомості
Подання алгоритму програми у вигляді блок-схеми має два недоліки:
- передбачає занадто низький рівень деталізації, що часто приховує суть складних
алгоритмів;
- дозволяє використовувати неструктурні способи передачі керування (goto), причому часто
на схемі алгоритму вони виглядають простіше, чим еквівалентні структурні.
Крім схем, для опису алгоритмів можна використовувати псевдокоди, Flow-Форми й
діаграми Нассі-Шнейдермана. Усі перераховані способи з однієї сторони базуються на тих
же основних структурах, а з іншого сторони, допускають різні рівні деталізації.
Flow-Форми й діаграми Нассі-Шнейдермана належать до візуальних мов специфікації
процесів. Візуальні мови проектування є відносно новою, оригінальною методикою розробки
специфікацій процесу. Вони дозволяють визначати потоки керування за допомогою
спеціальних ієрархічно організованих схем.
Flow-Форми являють собою графічну нотацію опису структурних алгоритмів, яка
ілюструє вкладеність структур. Кожний символ Flow-Форми відповідає керуючій структурі й
зображується у вигляді прямокутника. Для демонстрації вкладеності структур символ Flow-
31
Форми може бути вписаний у відповідну область прямокутника будь-якого іншого символу.
У прямокутниках символів міститься текст природньою мовою або в математичній нотації.
Розмір прямокутника визначається довжиною вписаного в нього тексту й розмірами вкладених
прямокутників. Символи Flow-Форм, що відповідають основним і додатковим керуючим
конструкціям, наведені на рис. 1.
if A case of
A
Then B 1 A1

B 2 A2

C else C n AN

Послідовна обробка Умовний вибір Case-Вибір


For A
While A do B

do
Do B until A B

Цикли
Рисунок1 - Символи Flow-Форм

Подальший розвиток Flow-Форми одержали в діаграмах Нассі-Шнейдермана. На цих


діаграмах символи послідовної обробки й циклу зображуються так само, як і відповідні
символи Flow-Форм. У символах умовного вибору й case-вибору власне умова розташовується
у верхньому трикутнику, обирані варіанти - на нижніх сторонах трикутника, а блоки обробки -
під обираними варіантами (рис.2).

Рисунок 2 – Умовні позначки діаграм Нассі-Шнейдермана для основних конструкцій:


а – проходження, б – розгалуження, в – вибір, г – цикл-поки, д – цикл- до
Візуальні мови проектування підтримуються автоматичною кодогенерацією, дозволяють
здійснювати декомпозицію обчислювальних процесів. Їхній основний недолік - труднощі
модифікації при зміні деталей.

Приклад візуальної специфікації процесів

32
На рис.3 наведений приклад використання Flow-Форм при проектуванні специфікації
процесу, що забезпечує упорядкування певним чином елементів масиву, що і є фрагментом
алгоритму сортування методом "поплавця". На рис.4 розв'язання тієї ж задачі презентовано за
допомогою діаграми Нассі-Шнейдермана.

Рисунок 3 - Приклад Flow-Форми

Рисунок 4 - Діаграма Нассі-Шнейдермана

Завдання

Скласти Flow-Форму й діаграму Насси-Шнейдермана для завдання з лабораторної роботи


№1.

Лабораторна робота №17

Покрокова деталізація (програмування зверху вниз або спадна розробка)

Ціль роботи: практичне освоєння методу спадного проектування програм.

Теоретичні відомості
Покрокова деталізація являє собою простий процес, що припускає первісне вираження
33
логіки модуля в термінах гіпотетичної (умовної) мови дуже високого рівня з наступною
деталізацією кожного речення в термінах мови більш низького рівня, доти, поки, нарешті, не
буде досягнутий рівень використовуваного мови програмування. Чим менше мова містить
деталей, тим більше вона високого рівня. Можна вважати мовою найвищого рівня звичайну
людську мову, а мовою низького рівня — машинну мову.
Протягом усього процесу покрокової деталізації логіка процесу виражається основними
конструкціями структурного програмування.
Достоїнство покрокової деталізації полягає в тому, що вона дозволяє проектувальнику
впорядкувати свої міркування. На кожному кроці він має справу з елементарним завданням.
Розглянемо цей метод на конкретному прикладі, де розв'язання поставленого завдання
виконане мовою Pascal.
ЗАВДАННЯ. Дана матриця розміром 10*10 елементів. Для кожного стовпця серед елементів,
що лежать вище першого, рівного нулю, і значення яких лежать в інтервалі [с, d], знайти
найменший і найбільший елементи і їх номера в рядку. Якщо нульового елемента в стовпці
немає, то обробляється весь стовпець.

План рішення завдання

1. Уведення — вивід.
2. Основний алгоритм (цикл по стовпцях).
3. Обробка стовпця (внутрішній цикл).
4. Обробка елементів матриці.
5. Пошук найбільшого й найменшого елементів у стовпці.
6. Обробка початкових і кінцевих операторів циклів.
7. Оптимізація й шліфування програми.
Уведення - вивід
А[10,10] — вихідна матриця.
C і D — границі інтервалу.
MАХ[10] і MIN[10] - масиви, що містять найбільші й найменші значення кожного стовпця
вихідної матриці.
IMAX[l0] і IMIN[l0] - масиви номерів рядків, у яких зустрічаються знайдені, відповідно,
найбільші й найменші значення в стовпці.
а) Перший крок Деталізація вводу-виводу.

PROGRAMM PRIMER;
VAR
A: ARRAY [1..10,1..10] OF REAL; (* Вихідна матриця *)
C,D: REAL; (* Границі інтервалу *)

34
I,J: INTEGER; (* Номера рядків і стовпців *)
MAX, MIN: ARRAY [1..10] OF REAL; (* Значення найбільших і найменших елем.*)
IMAX, IMIN: ARRAY [1..10] OF INTEGER; (* і їх номера рядків *)
BEGIN
WRITELN ('Уведіть елементи матриці');
FOR I:=l TO 10 DO BEGIN
FOR J:=l TO 10 DO READ(A[I,J]); WRITELN;
END;
WRITE ('Уведіть границі інтервалу'); READ (З, D);
FOR I:=l TO 10 DO WRITELN('MIN=', MIN[I], ',його номер', IMIN[I],';
‘MAX=', MAX[I], '; його номер', IMAX[I]);
END.

б) Другий крок. Деталізація основного алгоритму.

Необхідно виконати те саме для кожного стовпця.

FOR J:=l TO 10 DO
BEGIN
END;

в) Третій крок. Обробка стовпця.

У цьому стовпці необхідно обробити елементи, що лежать вище


першого нульового елемента матриці А.

FOR 1:=1 ТЕ 10 DO WHILE A[I,J] != 0 DO

BEGIN

Обробка елементів матриці

END;

г) Четвертий крок. Обробка елементів матриці А.

IF (З <= A[I, J]) AND (A[I, J]<= D) THEN

BEGIN

Пошук найбільшого елемента в стовпці


Пошук найменшого елемента в стовпці

END;

д) П'ятий крок. Пошук найбільшого й найменшого елементів у


35
стовпці матриці А и їх номера в рядку.

IF A[I, J]>=MAXT THEN BEGIN MAXT:= A[I, J]; IMAXT:= I; END;


IF A[I, J]<= MINT THEN BEGIN MINT:= A[I, J]; IMINT:= I; END;
З'явилися нові змінні: МАХТ і MINT, IMAXT і IMINT, які необхідно включити в описи
змінних із присвоєнням типу REAL і INTEGER відповідно.
е) Шостий крок. Початкові й кінцеві оператори.
Рухаємося зсередини циклів назовні й дивимося, що необхідно для роботи циклів.
Для спрацьовування внутрішнього циклу треба, щоб на першому кроці МАХТ і MINT мали
якісь значення. Також потрібно відповістити на запитання - що робити у випадку
відсутності в стовпці елементів з інтервалу (с, d)? Приймемо, що індекси в цьому випадку
дорівнюють нулю, а значення найбільших і найменших елементів, що попадають у вихідні
масиви, несуттєві.

Отже, спочатку

МАХТ: = З; MINT:= D;
IMАХТ: = 0; IMINT: = 0;
Результати цього циклу треба заслати в J-і елементи результуючих масивів, тому кінцеві
оператори будуть такими:
MAX[J]:= МАХТ; IMAX[J]:= MАХТ;
MIN[J]:= MINT; IMIN[J]:= IMINT;
Для циклу по стовпцях ніяких початкових і кінцевих операторів не потрібно.
ж) Останній крок. Шліфування й оптимізація програми.
Одержавши програму, можна зайнятися її поліпшенням, щоб вона стала коротше або
виконувалася швидше.
Наприклад, якщо використовувати MAX(J), MIN(J), IMAX(J), IMIN(J) замість МАХТ,
MINT, IMAXT, IMINT, те рядок кінцевих операторів не буде потрібний, але програма буде
довше виконуватися (хоча її текст скоротиться), тому що почастішає виконання операції «
Звертання до елемента масиву».

Результуючий алгоритм

36
Результуюча програма

PROGRAMM PRIMER;
VAR
A: ARRAY [1..10, 1..10] OF REAL; (* Вихідна матриця *)
C,D: REAL; (* Границі інтервалу *)
I,J: INTEGER; (* Номера рядків і стовпців матриці А *)
MAX, MIN: ARRAY[1..10] OF REAL; (* Значення найбільших і *)
(* найменших елементів *)
IMAX, IMIN: ARRAY [1..10] OF INTEGER; (* і їх номера рядків *)
MAXT, MINT: REAL; (* Тимчасові змінні, найбільше й *)
(* найменше значення елементів *)
IMAXT, IMINT: INTEGER; (* И їх номера в стовпці *)
BEGIN
WRITELN ('Уведіть елементи матриці:');
FOR I:=l ТЕ 10 DO
BEGIN
FOR J:= 1 TO 10 DO READ(A[I, J]); WRITELN;
END;
WRITE ('Уведіть границі інтервалу'); READLN (З, D);
FOR J:=l TO 10 DO (* Обробка стовпців матриці А *)
37
BEGIN
IMAXT:= 0; IMINT:= 0; MINT:= C; MINT:= D;
(* Обробка елементів стовпця матриці А *)
FOR I:=l TO 10 DO WHILE A[I,J] # 0 DO
BEGIN
IF (C<=A[I, J]) AND (A[I, J]< = D) THEN
(* Елемент матриці належить відрізку CD? *)
BEGIN
IF A[I, J]>= MAXT (* Елемент матриці найбільший? *)
THEN BEGIN MAXT:=A[I, J]; IMAXT:= I; END;
IF [I, J]<= MINT (* Елемент матриці найменший? *)
THEN BEGIN MINT:=A[I, J]; IMINT:= I; END;
END;
END;
MAX[J]:= MAXT; MIN[J]:=MINT;
IMAX[J]:= IMAXT; IMIN[J]:= IMINT;
END;
FOR I:=l TO 10 DO WRITELN('MIN=', MIN[I],', його номер',IMIN[I],
';MAX=', MAX[I], ', його номер', IМАХ|I]);
END.

Завдання

Використовуючи завдання до лабораторної роботи №1, спроектувати програму мовою С


методом покрокової деталізації. Кроки розробки представити у звіті, супроводжуючи їх
блок-схемами. Налагодити програму й одержати результат.

Лабораторна робота №18

Налагодження програм

Ціль роботи: вивчення застосування директив препроцесора С/С++ для налагодження


програм.
Теоретичні відомості
У С/С++ є кілька директив препроцесора, які дають можливість вибірково компілювати
частини вихідного коду вашої програми. Цей процес називається умовною компіляцією.
У будь-якій програмі є умовні гілки виконання коду. Точно так само препроцесор дозволяє
умовно включати ті або інші фрагменти ісходнику програми. Для цього використовується
препроцесорна директива "#if <умовний вираз>".
Допустимо розроблювальна програма містить отладочні друки. Вони повинні бути включені
тільки тоді, коли розроблювач програми займається її налагодженням. Версія, яку програміст
віддає користувачеві, цих друків містити не повинна. Для цього, наприклад, можна в
програмі завести змінну й ставити всі друки під умову:

1 const int debug = 1;


2 ...
3 void func (void)
4 {
5 if (debug)
6 printf ("entering 'func'\n");
7 ...

38
8 }
9 ...
У простих випадках це дійсно буде виходом із ситуації. Якщо змінну debug установити в 0,
то компілятор, бачачи умову "if (debug)", а також те, що змінна має модифікатор const і
дорівнює нулю, швидше за все взагалі вилучить виклик printf(), як мертвий код, у який
програма ніколи не потрапить. Але якщо програма складається з декількох вихідних файлів,
те такий варіант не підійде, тому що змінна повинна бути визначена тільки в одному модулі,
а інші модулі не будуть бачити значення змінної, а тому не зможуть вилучити мертвий код.
При цьому виклик printf() у коді програми залишиться, хоч і буде стояти під умовою, яка
ніколи не буде дійсною. Поки мова йде тільки про отладочні друки, навіть багаторазові, це
може бути прийнятним, тому що 10,,,100 викликів printf() принципово розмір бінарного
файла не збільшать (збільшення може становити одиниці відсотків). Гірше, коли програма
викликає не printf(), а якусь користувацьку функцію, яка потрібна тільки для отладочних
друків.

1 const int debug = 1;


2 ...
3 void debug_print (...)
4 {
5 ...
6 }
7
8 void func (void)
9 {
10 if (debug)
11 debug_print (...);
12 ...
13 }
14 ...
У цьому випадку в код програми потраплять усі такі функції, які ніколи не будуть
запускатися, але будуть займати місце в бінарному файлі. Коли таких функцій багато,
програма може відчутно збільшитися.
Замість отладочного друку може стояти яка завгодно функціональність (наприклад, багато
програмних продуктів поширюються в тому виді, що повна версія робиться платною, а у
вільних (безкоштовних) версіях відключається частина функціональності).
Щоб уникнути цих проблем, непотрібний код треба фізично вирізати із програми. Для цих
цілей зручно використовувати директиву умовної компіляції #if:

1 #define DEBUG 1
2
3 #if DEBUG == 1
4
5 void debug_print (...)
6 {
7 ...
8 }
9
10 #endif
11
12 void func (void)
13 {
14 #if DEBUG == 1
15 debug_print (...);
16 #endif
17
18 ...

39
19 }
20 ...
Ця директива на етапі препроцессірування управляє тим, що потрапить у препроцесорну
видачу, а що ні. Для даного прикладу після препроцесора будемо мати такий текст:

1 void debug_print (...)


2 {
3 ...
4 }
5
6 void func (void)
7 {
8 debug_print (...);
9 ...
10 }
11 ...
Тепер якщо значення макросу DEBUG поміняти на 0, то вийде зовсім інший результат
роботи препроцесора:

1
2 <-- те місце, яке вирізалося на препроцессировании через невиконання
3 умови в директиві #if. Насправді кожний рядок вихідного тексту
4 буде замінена порожнім рядком. Тут вирізався текст функції debug_print
5
6 void func (void)
7 {
8 <-- тут вирізався виклик функції debug_print
9 ...
10 }
11 ...
12
Після препроцессору непотрібний текст фізично вирізався й у компіляцію не попадає. У
випадку роботи з декількома файлами визначення макросу DEBUG слід помістити в один з
файлів *.h, який підключається у всіх вихідних файлах. А якщо ні, то може виявитися так,
що якщо в одному з файлів не включений опис макросу, то він там не буде визначений, а
тому якісь частини коду виявляться не включеними (навіть якщо в *.h файлі значення
макросу виставлене в одиницю). Керування наявністю або відсутністю коду здійснюється
шляхом заміни єдиного символу й не вимагає постійних зусиль по закоментуванню й
розкоментуванню коду.
Аналогічно слід робити, коли із програми потрібно вирізати деяку функціональність.
Припустимо, є програма, яку слід збирати в трьох конфігураціях: lite, medium, full.
Схематично виглядати буде приблизно так:

1 #define BUILD_LITE 1
2 #define BUILD_MEDIUM 2
3 #define BUILD_FULL 3
4
5 /* При складанні повинен бути включений тільки один із цих варіантів */
6 /* #define BUILD BUILD_LITE */
7 /* #define BUILD BUILD_MEDIUM */
8 #define BUILD BUILD_FULL
9 ...
10 #if BUILD >= BUILD_LITE
11 /* Даний код підключиться при будь-якій конфігурації, але для спільності
12 його
13 * треба "підсвітити" макросом BUILD. Так простіше буде пошуком знайти всі
14 місця,
15 * які теоретично залежать від типу зборки

40
16 . */
17 ...
18 #endif
19 ...
20 #if BUILD >= BUILD_MEDIUM
21 /* Даний код підключиться в конфігураціях medium і full */
22 ...
23 #endif
24 ...
25 #if BUILD == BUILD_FULL
26 /* Даний код підключиться в тільки в конфігурації full */
...
#endif
Синтаксис директиви #if простий і збігається з операціями умовного виконання в мові. Слід
пам'ятати, що треба обов'язково використовувати парну директиву #endif, тому що без неї
препроцесор не зможе зрозуміти, у якому місці закінчується директива #if. Варіанти
використання:

1 #if <умова>
2 ...
3 #endif

1 #if <умова>
2 ...
3 #else
4 ...
5 #endif

1 #if <умова>
2 ...
3 #elif <умова>
4 ...
5 #elif <умова>
6 ...
7 #else
8 ...
9 #endif
Потрібно відзначити, що препроцессування виконується окремо від компіляції, а тому в
умові директиви "#if" не можуть використовуватися ніякі змінні із програми. В умові можуть
використовуватися тільки цілочисленні константні значення. Над цими константами можна
виконувати операції порівняння "==", "!=", "<", "<=", ">", ">=", логічні операції "&&", "||",
"!", круглі дужки, а так само конструкція "defined <macro_name>", значення якої істинно,
якщо макрос <macro_name> визначений, а якщо ні, то значення неправильне.
Препроцесорні директиви #if, так само, як і мовні, можуть бути вкладені друг у друга. У
рядках з директивами можна використовувати коментарі.
У якості одного із прикладів використання можна продемонструвати ситуацію: у програмі
була знайдена помилка, але акуратне її виправлення вимагає багато часу, а виправити треба
швидко. Тому тимчасово використовуються «затички»:

1 /* Користувацька помилка N1234. На виправлення піде багато часу, тому під


2 цим макросом поки зроблена «затичка»
3 * для обходу помилки */
4 #define BUG1234
5 ...
6
7 void func1 (void)
8 {
9 ...
41
10 /* «Затичка». У випадку наявності або відсутності налагоджувальних
11 друків
12 * код затички виглядає по різному */
13 #if defined BUG1234
14 #if DEBUG == 1
15 /* Код затички в отладочній зборці */
16 ...
17 #else /* DEBUG */
18 /* Код затички в користувацькій зборці */
19 ...
20 #endif /* DEBUG */
21 #endif /* BUG1234 */
22 ...
23 }
24 ...
25 void func2 (void)
26 {
27 ...
28 /* Тут помилка проявляється тільки в MEDIUM зборці */
29 #if (defined BUG1234) && (BUILD == BUILD_MEDIUM)
30 /* Код затички */
31 ...
32 #endif
33 ...
}
Виправлена в такий спосіб програма тестується й здається користувачеві. Після цього можна
спокійно виправляти помилку. Далі потрібно закоментувати макрос BUG1234 і тестувати в
режимі з викинутими затичками. Важливо, що це управляється всього одним рядком. Після
ретельного тестування потрібно знайти всі місця, де використовувався макрос BUG1234 і
вилучити їх.
У попередньому прикладі введений макрос BUG1234, у якого не було ніякого значення. Таке
використовують у випадках, коли деякий налагоджувальний макрос може приймати тільки
два значення (умовно 0 і 1). У цих випадках замість значень 0 і 1 часто використовують
макрос без значення, а перевірку роблять на підставі того, установлений макрос чи ні - тобто
через директиву #if defined .... (скорочені варіанти "#ifdef <macro_name>" і "ifndef
<macro_name>"). Параметрами цих директив є не логічне вираження, а один-єдиний макрос.
Конструкція #ifdef BUG1234 еквівалентна конструкції #if defined BUG1234, #ifndef BUG1234
еквівалентно #if (! defined BUG1234).

Завдання

Написати програми, використовуючи директиви умовної компіляції.


Програма обробляє одномірний масив і повинна виконуватися двояким способом: якщо
розмір масиву більше 10, то пам'ять для масиву виділити динамічно й до елементів масиву
звертатися через покажчики; якщо розмір масиву не більше 10, то пам'ять для масиву
виділити до початку виконання програми й до елементів масиву звертатися по імені.
Програма обробляє матрицю й повинна виконуватися двояким способом: якщо в програмі є
константи, що визначають кількість рядків і стовпців, то пам'ять для матриці виділити
динамічно й до елементів масиву звертатися через покажчики; якщо хоча б однієї константи
немає, пам'ять для масиву виділити до початку виконання програми й до елементів масиву
звертатися по імені.

Лабораторна робота №19

42
Візуальне програмування

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

Теоретичні відомості
Розробляючи програмне забезпечення (додаток) для користувача, розроблювач витрачає
багато часу на програмування користувацького інтерфейсу.
З появою операційної системи Windows з'явилися й нові інструментальні засоби типу Delphi,
Visual Basic, Access і ін. для створення додатків по методу GUI (Graphical User Interface --
графічний інтерфейс користувача), за допомогою якого основа додатка «візуально
рисується» на екрані.
Тепер можна в короткий термін побудувати користувацький інтерфейс розроблювального
програмного продукту на основі простого механізму створення стандартних панелей
інструментів, командних кнопок і користувацьких меню.
При цьому не потрібно писати програми в класичному розумінні цього слова. Вони будуть
автоматично складені вбудованою мовою програмування під час розробки додатка.
Виконання програми, повністю написаної алгоритмічною мовою високого рівня в
традиційному змісті, починається з першого рядка й протікає через різні її частини згідно із
заданим алгоритмом. Керування додатком користувач, як правило, здійснює через пункти
меню, «гарячі» клавіші, а також через повідомлення, що вводяться їм на запит програми.
Додаток, розроблений по Windows GUI-Технології, працює по-іншому. Його ядром є набір
незалежних частин коду (процедур), що активізуються у відповідь на події, що відбуваються
навколо.
Візуальне програмування виконується в так званому інтегрованому середовищі розробки
(Integrated Development Environment) і припускає наступні дії:
- створення інтерфейсу користувача у вигляді форми, на якій розміщають («малюють»)
елементи керування додатком, наприклад, текстові поля й кнопки керування;
- визначення властивостей для форми й елементів керування, щоб задати, наприклад,
написи, колір і розмір;
- запис відповідного програмного коду (тексту програми) у відповідь на події, щоб оживити
додаток (щоб він працював).
Інтегроване середовище розробки для Windows (IDE for Windows) звичайно складається з
наступних частин:
- заголовок — верхній рядок, що містить також три кнопки Windows: згортання, розгортання
й закриття вікна;
- рядок меню — дозволяє управляти додатком;
- панель інструментів (кнопок) - для прискорення виконання багатьох звичайних дій, що
часто служить альтернативою команд меню;
- робоча область - область вікна, у якій створюються й обробляються всі об'єкти додатка,
вона практично займає все вікно;
- контекстне меню — містить команди для часто виконуваних дій.
Контекстне вікно відкривається після натискання на праву кнопку миші, коли її покажчик
перебуває на об'єкті, з яким у цей момент відбувається робота. Список доступних команд
контекстного меню залежить від того, на якій області середовища розробки було виконано
клацання.
43
Залежно від інструментального середовища розробки додатка додаються інші частини.
Наприклад:
- рядок статусу й підказок — нижній рядок. У ньому виводяться повідомлення про роботу
системи;
- панель елементів керування (Toolbox), яка містить набір інструментів, необхідних під час
розробки додатка для розміщення елементів керування на формі;
- вікно властивостей (Properties), у якому встановлюються властивості для обраної форми
або елемента керування;
- вікно редактора коду, у якому редагується програмний код;
- дизайнер форм — вікно, у якому розробляється форма;
- інші вікна.
Усі вікна являють собою звичайні вікна Windows, що одержують за замовчуванням усі
стандартні характеристики вікна поточної версії Windows. Зокрема, вони успадковують такі
елементи, як границі зі зміною розміру, віконне меню керування (у лівому верхньому куті), а
також кнопки Maximize, Minimize і Exit у правому верхньому куті.
На екрані одночасно можуть перебувати кілька вікон, що представляють різні додатки або
об'єкти. Кожний об'єкт має повний доступ до буфера обміну (clipboard) і до інформації
більшості додатків Windows, що працюють у те саме час. Але тільки з одним вікном може
працювати користувач у цей момент часу. Це вікно буде мати «Активний заголовок вікна»
(active title bar), виділений підвищеної яскравістю. Про нього говорять, що він має фокус.
Сучасні інструментальні засоби розробки додатків для користувача пропонують
розроблювачеві набір майстрів, які допоможуть йому в короткий термін спроектувати
потрібне програмне забезпечення.
Майстер розробки автоматизує процес створення проекту або його частини. Розроблювачеві
проекту залишається тільки відповідати на запитання, що задаються майстром у ході його
створення. У процесі розробки додатка можна в будь-який час повернутися назад або
зупинити (закінчити) роботу майстра. У підсумку буде автоматично сгенерована велика
кількість корисного коду.
Однак, усі вимоги користувача майстер виконати не в змозі. Тому, щоб повністю й остаточно
«оживити» усі автоматично створені майстром можливості, проектувальнику додатка буде
потрібно додати деяку кількість додаткового коду. Майстер розробки додатка являє собою
потужний, але всього лише допоміжний засіб програмування. Він не може повністю
замінити програміста.
Основою створення інтерфейсу додатка є форми (forms).
Форма — це об'єкт, який має властивості, що визначають зовнішній вигляд форми, методи,
що визначають її поведінка, і події, які визначають її взаємодію з користувачем. Режим
форми і її зручний дизайн являє собою дружнє середовище спілкування додатка з
користувачем.
Форма містить елементи керування додатком: поля введення (text boxes), меню, командні
кнопки (command buttons), перемикачі (option buttons), прапорці (check boxes), списки (list
boxes), лінійки прокручування (scroll bars), а також діалогові вікна для вибору файлу або
каталогу. Вона може містити об'єкти OLE (Object Linking and Embedding — зв'язок і
впровадження об'єктів), наприклад, картинки, музику й діаграми.
Елементи керування - це об'єкти, що містяться усередині форми і які забезпечують
інтерактивний режим роботи користувацького додатка. Кожний тип елемента керування
відноситься до певного класу й має свій власний набір властивостей, методів і подій. Одні

44
елементи керування найкраще підходять для введення або відображення тексту, інші - для
керування додатком.
Для відображення або введення тексту у формі застосовуються елементи керування мітка
(Label) і тестове поле (Text Box).
Мітка використовується для відображення на формі тексту, який користувач не може
змінювати. Вона звичайно використовується для оформлення форми. Наприклад, для виводу
заголовків і підказок користувачеві, для опису інших елементів керування та ін.
Відображуваний у мітці текст управляється властивістю Caption, яка може бути встановлена
під час розробки додатка у вікні Properties (Властивостей) або оператором присвоювання під
час виконання програми.
Текстове поле є гнучким елементом керування й застосовується як для одержання даних,
що вводяться користувачем, так і для відображення тексту. Його не слід використовувати
для відображення незмінного користувачем тексту. Його значення можна встановити або
змінити трьома способами: розроблювачем додатка під час проектування у вікні Properties
(Властивостей), оператором присвоювання під час виконання програмного коду або
користувачем під час виконання додатка.
Сучасні інструментальні засоби розробки додатків звичайно містять кілька стандартних
елементів керування, корисних для організації вибору: прапорець (Check boxes), перемикачі
(Option buttons), список (List box) і комбіноване вікно (Combo box) .
Прапорець показує, включена чи ні певна умова й може приймати тільки два значення типу
«так/ні», «правда/ неправда», «1/0» і т.п. Тому що прапорці працюють незалежно друг від
друга, користувач може встановити будь-яке їхнє число одночасне. При виконанні клацання
на прапорці для нього відбувається подія Click (Клацання), яка може бути оброблена в
програмному коді.
Перемикачі надають користувачеві вибір із двох або більш можливостей. Звичайно вони
працюють у групі. При виборі одного, негайно скидаються всі інші перемикачі в групі.
Об'єднання перемикачів у групу означає для користувача, що він може вибрати одну й тільки
одну можливість із заданого набору перемикачів.
Список представляє користувачеві перелік можливих варіантів вибору, відображуваних
звичайно вертикально у вигляді списку. Якщо число елементів списку перевищує
можливості відображення їх у вікні, то з'являється смуга прокручування, за допомогою якої
користувач може переглядати список елементів, прокручивая його із угорі вниз.
Комбіноване вікно сполучає можливості списку й текстового поля. У цьому елементі
керування користувач може робити вибір значення або введенням тексту в поле введення
комбінованого вікна, або вибором елемента з його списку.
Одним з основних способів забезпечення взаємодії користувача з додатком є кнопка
керування (Command Button), натисканням якої користувач може виконувати різноманітні
дії.
Існує багато способів «нажати» кнопку керування:
- клацнути «мишею» на кнопці;
- перемістити фокус на кнопку, натискаючи клавішу <Таb>, і потім вибрати цю кнопку,
нажавши клавішу <Space> або < Enter >;
- нажати клавіші доступу (<Alt> + підкреслена буква>) для обраної кнопки керування;
- установити значення для кнопки в програмі рівним True.
Усі ці дії змушують викликати процедуру обробки події Click (Натискання на кнопку).
Кнопка, що одержує фокус, стає тривимірною. Це досягається малюванням тонкого
пунктирного прямокутника навколо тексту на кнопці, і тонкого прямокутника навколо самої
45
кнопки.
Для поліпшення дизайну форми застосовуються «Графічні елементи керування», які
полегшують роботу із графікою. Це можуть бути зображення, лінії, прямокутники й т.п.
При запуску проекту форми стають вікнами, які й бачить користувач.
Створення інтерфейсу додатка полягає в розміщенні елементів керування у формі, у завданні
для них значень властивостей у вікні Properties (Властивості) і в написанні програм, що
обробляють події типу клацання «миші»; натискання на клавішу, відкриття форми та ін.
Остання дія являє собою традиційне програмування.
Програмні коди розміщаються в процедурах обробки подій (event procedures) або в окремих
модулях і можуть бути викликані на зміну у вікні редактора коду.
Створений додаток працює по наступному алгоритму:
1. Відслідковуються всі вікна й елементи керування для кожного вікна на предмет
визначення всіх подій, що належать до них (рухи або клацання миші, натискання клавіш,
одержання або втрата фокуса й т.п.).
2. Коли подія визначена й для неї не знаходиться вбудована процедура її обробки, то
шукається процедура, написана програмістом для обробки даної події.
3. Якщо така процедура існує, вона виконується, інакше очікується наступна подія.
4. Виконання пункту 1.
Ці дії повторюються циклічно доти, поки додаток не завершить роботу.
Створивши інтерфейс додатка за допомогою форм і елементів керування, необхідно
написати програмний код, який визначає поведінку додатка.
Сучасні інструментальні засоби розробки додатків дозволяють автоматично вносити
оператори, властивості й параметри в текст програми, при наборі якого редактор відображає
список підходящих операторів, прототипів функцій або значень.
При введенні імені елемента керування з'являється список його властивостей, що
розкривається. Така можливість корисна, якщо програміст не знає точно, які властивості
доступні даному елементу керування. Якщо програміст забув формат оператора або функції,
йому буде надана синтаксична допомога - при введенні правильного ключового слова або
правильного імені функції відображається синтаксис оператора або функції.

Завдання

Створити Windows-Додаток із простим інтерфейсом для виконання програми, створеної в


лабораторній роботі №3. Для цього використовувати будь-яке середовище розробки
Windows додатків (наприклад, Visual C++).

Лабораторна робота №20

Види комп'ютерних програм

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

Теоретичні відомості
Лінійний тип програми передбачає написання коду без використання функцій і процедур.
Лінійний тип програми з використанням змінних типу покажчик передбачає додавання в код

46
посилальних змінних.
При модульному програмуванні до основного модуля програми підключаються кілька
допоміжних модулів, у яких реалізовані процедури й функції алгоритму. Наприклад, в
одному модулі реалізується функція додавання нового запису в типізований файл, в іншому
модулі – процедура пошуку запису відповідно до запиту.
У програмах повинне бути реалізоване меню вибору дій: додавання запису, пошук запису,
вихід. При написанні комп'ютерних програм особливу увагу слід приділити роботі з
типізованим файлом, опису змінних типу покажчик. Необхідно виділяти пам'ять для
посилальних змінних на початку програми й видаляти їх по закінченню роботи алгоритму.
Для зручності обробки запиту прочитані з файлу дані можна записати в масив.
Файлом називають спосіб зберігання інформації на фізичному пристрої. Файл — це поняття,
яке може бути застосовно до всього — від файлу на диску до термінала.
У С/C++ відсутні оператори для роботи з файлами. Усі необхідні дії виконуються за
допомогою функцій, включених у стандартну бібліотеку. Вони дозволяють працювати з
різними пристроями, такими, як диски, принтер, комунікаційні канали і т.д. Ці пристрої
сильно відрізняються друг від друга. Однак файлова система перетворить їх у єдиний
абстрактний логічний пристрій, називаний потоком.
Текстовий потік — це послідовність символів. При передачі символів з потоку на екран,
частина з них не виводиться (наприклад, символ повернення каретки, переведення рядка).
Двійковий потік — це послідовність байтів, які однозначно відповідають тому, що
знаходиться на зовнішньому пристрої.
Організація роботи з файлами засобами C
Оголошення файлу
FILE *ідентифікатор;
Приклад
FILE *f;
Відкриття файлу:
fopen(ім'я фізичного файлу, режим доступу)
Режим доступу — рядок, що вказує режим відкриття файлу файлу й тип файлу
Типи файлу: бінарний (b); текстовий (t)

Значення Опис
r Файл відкривається тільки для читання
Файл відкривається тільки для запису. Якщо відповідний фізичний файл існує, він
w
буде перезаписаний
Файл відкривається для запису в кінець ( для дозапису) або створюється, якщо не
a
існує
r+ Файл відкривається для читання й запису.
Файл відкривається для запису й читання. Якщо відповідний фізичний файл існує,
w+
він буде перезаписаний
Файл відкривається для запису в кінець ( для дозапису) або створюється, якщо не
a+
існує
47
Наприклад

f = fopen(s, "wb");
k = fopen("h:\ex.dat", "rb");
Неформатоване файлове введення-вивід
Запис у файл
fwrite(адреса записуваної величини, розмір одного екземпляра, кількість записуваних величин,
ім'я логічного файлу);
Наприклад,
fwrite(&dat, sizeof(int), 1, f);
Читання з файлу
fread(адреса величини, розмір одного екземпляра, кількість зчитуваних величин, ім'я
логічного файлу);
Наприклад,
fread(&dat, sizeof(int), 1, f);
Закриття файлу
fclose(ім'я логічного файлу);
Форматоване файлове введення-вивід
1) Функції fgetc() і fputc() дозволяють відповідно здійснити введення-вивід символу.
2) Функції fgets() і fputs() дозволяють відповідно здійснити введення-вивід рядка.
3) Функції fscanf() і fprintf() дозволяють відповідно здійснити форматоване уведення-вивід і
аналогічні відповідним функціям форматированного введення-виводу, тільки роблять це
стосовно до файлу.
У С/С++ відсутнє визначене поняття «типізований файл». Під цим терміном мається на увазі
файл (звичайно текстовий), структура якого строго визначена й заздалегідь відома. Усі
записи такого файлу мають однакову структуру, що має на увазі їхню однакову обробку.

Завдання

Спроектувати 3 варіанта програми для кожного варіанта - лінійний, лінійний з


використанням змінних типу покажчики, модульний. Розроблювальні програми повинні
працювати з типізованим файлом. Запис типізованого файлу повинний містити не менш
трьох атрибутів (наприклад, для каталогу файлів – ім'я файлу, розмір, дата створення; для
каталогу музики – назва треку, виконавець, час звучання і т.д.). Запит до файлу на пошук
запису формується у відповідності із тематикою завдання (наприклад, знайти запис із
максимальною ціною, знайти запис із мінімальним значенням параметра й т.п.). Заповнення
даними типізованих файлів на наступні теми:
1. Каталог файлів.
2. Каталог фільмів.
3. Каталог музики.
4. Каталог дисків з ПО.
5. Каталог HDD.
6. Каталог автомобілів.
7. Каталог літаків.
8. Каталог вертольотів.

48
9. Каталог телевізорів.
10. Каталог книг.
11. Каталог меблів.
12. Каталог сканерів.
13. Каталог принтерів.
14. Каталог моніторів.
15. Каталог процесорів.
16. Каталог звукових карт.
17. Каталог відеокарт.
18. Каталог модемів.
19. Каталог системних блоків.
20. Каталог UPS.
21. Каталог студентів.
22. Каталог співробітників фірми.
23. Каталог магазинів.
24. Каталог квітів.
25. Каталог телефонів.
26. Каталог кораблів.
27. Каталог канцтоварів.
28. Каталог автобусів.
29. Каталог холодильників.
30. Каталог годинників.

Лабораторна робота №21

Метрика Холстеда

Ціль роботи: вивчення метрики Холстеда для оцінки складності розміру кодів.
Теоретичні відомості
Якість - це повнота властивостей і характеристик продукту, процесу або послуги, які
забезпечують здатність задовольняти заявленим або тим, які маються на увазі, потребам.
Оцінка якості програмного забезпечення проводиться на декількох рівнях, один з яких
призначений для виміру якості за допомогою метрик, кожна з них визначається як
комбінація методу виміру атрибута й шкали виміру значень атрибутів.
При оцінці складності програм, як правило, виділяють три основні групи метрик: метрики
розміру програм, метрики складності потоку керування програм і метрики складності потоку
даних програм.
Оцінки першої групи найбільш прості й тому одержали широке поширення. Традиційною
характеристикою розміру програм є кількість рядків вихідного тексту. Під рядком
розуміється будь-який оператор програми.
Безпосередній вимір розміру програми, незважаючи на свою простоту, дає гарні результати.
Оцінка розміру програми недостатня для ухвалення рішення про її складності, але цілком
застосовна для класифікації програм, що суттєво різняться обсягами. При зменшенні
відмінностей в обсязі програм на перший план висуваються оцінки інших факторів, що
виявляють вплив на складність. Таким чином, оцінка розміру програми є оцінка по
номінальній шкалі, на основі якої визначаються тільки категорії програм без уточнення
оцінки для кожної категорії.
До групи оцінок розміру програм можна віднести метрику Холстеда. За базу прийнятий
підрахунок кількості операторів і операндів, використовуваних у програмі, тобто визначення
розміру програми.
49
Основу метрики Холстеда становлять чотири вимірювані характеристики програми: n1 -
число унікальних операторів програми, включаючи символи-роздільники, імена процедур і
знаки операцій (словник операторів); n2 – число унікальних операндів програми (словник
операндів); N1 – загальне число операторів у програмі N2 – загальне число операндів у
програмі.
Опираючись на ці характеристики, одержувані безпосередньо при аналізі вихідних текстів
програм, Холстед уводить наступні оцінки
n1 — число унікальних операторів програми, включаючи символи-роздільники, імена
процедур і знаки операцій (словник операторів),
n2 — число унікальних операндів програми (словник операндів),
N1 — загальне число операторів у програмі,
N2 — загальне число операндів у програмі,
n1' — теоретичне число унікальних операторів,
n2' — теоретичне число унікальних операндів.
Враховуючи введені позначення, можна визначити:
n=n1+n2 — словник програми,
N=N1+N2 — довжина програми,
n'=n1'+n2' — теоретичний словник програми,
N'= n1*log2(n1) + n2*log2(n2) — теоретична довжина програми (для стилістично коректних
програм відхилення N від N' не перевищує 10%)
V=N*log2n — обсяг програми,
V'=N'*log2n' — теоретичний обсяг програми, де n' — теоретичний словник програми.
L=V'/V — рівень якості програмування, для ідеальної програми L=1
L'= (2 n2)/ (n1*N2) — рівень якості програмування, заснований лише на параметрах реальної
програми без обліку теоретичних параметрів,
EC=V/(L')2 — складність розуміння програми,
D=1/ L' — трудомісткість кодування програми,
y' = V/ D2 — рівень мови вираження
I=V/D — інформаційний зміст програми, дана характеристика дозволяє визначити розумові
витрати на створення програми
E=N' * log2(n/L) — оцінка необхідних інтелектуальних зусиль при розробці програми, що
характеризує число необхідних елементарних розв'язків при написанні програми
Операнд – це деякий об'єкт або величина, оброблювана в програмі, а оператор являє собою
позначення конкретної дії, виконуваного стосовно операнду. У С/С++ додавання до набору
символів, що є вираженням, знака «;» перетворює його в оператор. Так, набір символів «i++»
є вираженням, а конструкція «i++;» уже є оператором. Оператори й операції пов'язані з
об'єктами, над якими вони виконуються. Таким об'єктами є константи, прості змінні, масиви
й структури, а також функції, класи й методи.
Як правило, при проведенні статистичних досліджень текстів програм до словника
операторів відносять наступні елементи:
• імена арифметичних і логічних операцій;
• присвоювання;

50
• умовні й безумовні переходи;
• роздільники;
• дужки (парні);
• імена процедур і функцій;
• вираження типу BEGIN...END, IF...THEN...ELSE, DO...WHILE. Вираження типу
BEGIN...END, IF...THEN...ELSE, DO...WHILE і їм подібні, що здійснюють блокове
угруповання операторів, при цьому розглядаються як єдині оператори (те ж відноситься й до
пар дужок).
Основний вихідний параметр, на якому базуються всі розрахунки метричних характеристик
майбутнього програмного продукту, – кількість імен вхідних і вихідних змінних n2',
представлених у гранично короткому записі (з погляду алгоритмічної складності – стислої).
Наприклад, для завдання одномірного масиву (тобто рядка), яке б не було число його
елементів, потрібно всього два імені:
• покажчик адреси початку масиву;
• кількість елементів у ньому.
Точно так само для завдання двовимірного масиву досить мати три параметри:
• покажчик адреси першого елемента;
• число стовпців;
• число рядків.
При застосуванні метрик Холстеда частково компенсуються недоліки, пов'язані з
можливістю запису однієї й тієї ж функціональності різною кількістю рядків і операторів.
Завдання
Для трьох варіантів програм з лабораторної роботи №6 розрахувати метрику Холстеда. Для
обчислення параметрів метрики Холстеда необхідно підрахувати число використовуваних у
програмі операторів і операндів (загальне число й число різних). Для розрахунків
пропонується використовувати MS Excel. Зрівняти отримані результати, зробити висновки.

Лабораторна робота №22

Метрика Маккейба

Ціль роботи: вивчення метрики Маккейба для оцінки складності потоку керування кодів
комп'ютерних програм.
Теоретичні відомості
Друга група оцінок складності програм – метрики складності потоку керування програм. Як
правило, за допомогою цих оцінок оперують або щільністю керуючих переходів усередині
програм, або взаємозв'язками цих переходів.
І в тому й в іншому випадку стало традиційним представлення програм у вигляді керуючого
орієнтованого графа G(V,E), де V – вершини, відповідні до операторів, а E – дуги, відповідні
до переходів. У дузі (U,V) – вершина V є вихідною, а U – кінцевою. При цьому U
безпосередньо випливає V, а V безпосередньо передує U. Якщо шлях від V до U складається
більш ніж з однієї дуги, тоді U іде за V, а V передує U.
Нехай представлена деяка програма. Для даної програми будується орієнтований граф, що
містить лише один вхід і один вихід, при цьому вершини графа співвідносять із тими
ділянками коду програми, у яких є лише послідовні обчислення, і відсутні оператори
51
розгалуження й циклу, а дуги співвідносять із переходами від блоку до блоку й галузями
виконання програми. Умова при побудові даного графа: кожна вершина досяжна з
початкової, і кінцева вершина досяжна з будь-якої іншої вершини.
Уперше графічне представлення програм було запропоноване Маккейбом. Основною
метрикою складності він пропонує вважати цикломатичну складність графа програми, або,
як ще називають, цикломатичне число Маккейба, що характеризує трудомісткість тестування
програми. Цикломатична складність частини програмного коду — рахункове число лінійно
незалежних маршрутів через програмний код. Якщо вихідний код не містить ніяких точок
розв'язків, таких, як вказівки IF або цикли FOR, то складність дорівнює одиниці, оскільки, є
тільки єдиний маршрут через код.
Графом потоку керування програми називається орієнтований граф, у вузлах якого
перебувають неподільні групи команд, а ребрами з'єднуються такі блоки, які можуть бути
виконані відразу один за одним.
Для обчислення цикломатичного числа Маккейба Z(G) застосовується формула
Z(G) = l-v+2p,
де l – число дуг орієнтованого графа G; v – число вершин; p- число компонентів зв’язності
графа.
Як правило, при обчисленні цикломатичної складності логічні оператори не враховуються.
У процесі автоматизованого обчислення показника цикломатичної складності, як правило,
застосовується спрощений підхід, відповідно до якого побудова графа не здійснюється, а
обчислення показника проводиться на підставі підрахунку числа операторів керуючої логіки
(if, switch і т.д.) і можливої кількості шляхів виконання програми.
Число компонентів зв’язності графа можна розглядати як кількість дуг, які необхідно додати
для перетворення графа в сильнозв’язний. Сильнозв’язним називається граф, будь-які дві
вершини якого взаємно досяжні. Для графів коректних програм, тобто графів, що не мають
недосяжних від точок входу ділянок і “висячих” входу й виходу, сильнозв’язний граф, як
правило, отримується шляхом замикання однієї вершини, що позначає кінець програми на
вершину, що позначає точку входу в цю програму. Так що в коректно написаних програмах
p=1, і тому формула для розрахунків цикломатичної складності здобуває вид:

V(G)= l-v + 2.
По суті Z(G) визначає число лінійно незалежних контурів у сильнозв’язному графі. Інакше
кажучи, цикломатичне число Маккейба показує необхідне число проходів для покриття всіх
контурів сильнозв’язного графа або кількість тестових прогонів програми, необхідних для
вичерпного тестування за критерієм “працює кожна галузь”.
Маккейб виявив, що розумною верхньою границею для цикломатичної складності є 10.
Якщо програмісти переступають цю границю, їм варто або переписати програму, або
розбити її на модулі.

d
c

g f

e
52
Для програми, представленої таким графом, цикломатичне число (при l=10, v=8, р=1)
визначиться як Z(G) = 10-8+2=4.
Таким чином, є сильнозв’язний граф із чотирма лінійно незалежними контурами:
a-b-c-g-e-h-a;
a-b-c-e-h-a;
a-b-d-f-e-h-a;
a-b-d-e-h-a;
Для того, щоб вичерпно протестувати дану програму, потрібно 4 прогони.
Показник цикломатичної складності може бути розрахований для модуля, методу й інших
структурних одиниць програми.
Завдання
Для трьох програм з лабораторної роботи №6 побудувати графи потоку керування й на їхній
основі розрахувати цикломатичне число Маккейба. Порівняти результати, зробити висновки.

Лабораторна робота №23

Метрика граничних значень

Ціль роботи: вивчення метрики граничних значень для оцінки складності потоку керування
кодів комп'ютерних програм.
Теоретичні відомості
Великий інтерес представляє оцінка складності програм по методу граничних значень.
Уведемо кілька додаткових понять, пов'язаних із графом програми.
Нехай G=(V,E) - орієнтований граф програми з єдиною початковою і єдиною кінцевою
вершинами. У цьому графі число вхідних у вершину дуг називається негативним ступенем
вершини, а число вихідних з вершини дуг - позитивним ступенем вершини. Тоді набір вершин
графа можна розбити на дві групи:
• вершини, в яких позитивний ступінь <=1;
• вершини, в яких позитивний ступінь >=2.
Вершини першої групи назвемо приймаючими вершинами, а вершини другої групи -
вершинами відбору.
Для одержання оцінки по методу граничних значень необхідно розбити граф G на
максимальне число підграфів G', що задовольняють наступним умовам:
1) вхід у підграф здійснюється тільки через вершину відбору;
2) кожний підграф включає вершину (називану нижньою границею підграфа), у яку можна
потрапити з будь-якої іншої вершини підграфа. Наприклад, вершина відбору з'єднана
сама із собою дугою петлею, утворює підграф.

а с f 53

b e i
Число вершин, що утворюють такий підграф, дорівнює скоректованій складності вершини
відбору:

Характеристики Вершини відбору


підграфов програм a b c d
Вершини переходу b,c b,d e,f g,h
Скоректована складність вершини
10 2 3 3
графа
b,c,d,
Вершини підграфа e,f,g,
b l,j g,h
h,i,j
Нижня границя підграфа k d i j

Кожна приймаюча вершина має скоректовану складність, рівну 1, крім кінцевої вершини,
скоректована складність якої рівна 0. Скоректовані складності всіх вершин графа G
підсумуються, утворюючи абсолютну граничну складність програми. Після цього
визначається відносна гранична складність програми:

S0=1-(v-1)/Sa,
де S0 – відносна гранична складність програми; Sa – абсолютна гранична складність програми,
v – загальне число вершин графа програми.
Таким чином, відносна складність програми рівна
S0=1-(11/25)=0,56.
Завдання
Для трьох програм з лабораторної роботи №6 і керуючих графів для них, побудованих у
лабораторній роботі №7, розрахувати відносну складність програми методом граничних
значень. Порівняти отримані результати, зробити висновки.

Лабораторна робота №24

Метрики потоку даних програм

Ціль роботи: вивчення метрик потоку даних комп'ютерних програм.

54
Теоретичні відомості
Для оцінки програмного продукту використовують ще одну групу метрик складності
програм – метрику складності потоку даних, тобто використання, конфігурації й розміщення
даних у програмах.
Пара “модуль – глобальна змінна” позначається як (p,r), де p – модуль, що має доступ до
глобальної змінної r. Залежно від наявності в програмі реального звертання до змінної r
формуються два типи пар “модуль – глобальна змінна”: фактичні й можливі. Можливе
звертання до r за допомогою p показує, що область існування r містить у собі p (змінна r
видна в модулі р).
Характеристика Aup говорить про те, скільки раз модулі Up дійсно одержали доступ до
глобальних змінних, а число Pup – скільки раз вони могли б одержати доступ.
Відношення числа фактичних звертань до можливих визначається

Rup=Aup/Pup

Ця формула показує наближену ймовірність посилання довільного модуля на довільну


глобальну змінну. Очевидно, чим вище ця ймовірність, тем вище ймовірність
“несанкціонованої” зміни якої-небудь змінної, що може суттєво ускладнити роботи, пов'язані
з модифікацією програми.
Покажемо розрахунки метрики “модуль – глобальна змінна”. Нехай у програмі є три
глобальні змінні й три функції. Якщо припустити, що кожна функція має доступ до кожної зі
змінних, то ми одержимо дев'ять можливих пар, тобто Pup=9. Далі, нехай перша функція
звертається до однієї змінної, друга – до двох, а третя не звертається ні до однієї змінної.
Тоді Aup=3, Rup=3/9.
Ще одна метрика складності потоку даних – спен.
Визначення спена ґрунтується на локалізації звертання до даних усередині кожної
програмної секції.
Спен – це число тверджень, що містять даний ідентифікатор, між його першою й
останньою появою в тексті програми. Ідентифікатор, що з'явився n раз, має спен, рівний n-1.
Спен визначає кількість контролюючих тверджень, що вводяться в тіло програми при
побудові траси програми по цьому ідентифікатору в процесі тестування й налагодження. При
великому спені ускладнюється тестування й налагодження.
Наступною метрикою складності потоку даних програм є метрика Чепіна. Існує декілька
її модифікацій. Розглянемо більш простий, а з погляду практичного використання – досить
ефективний варіант цієї метрики.
Суть методу полягає в оцінці інформаційної міцності окремо взятого програмного модуля
за допомогою аналізу характеру використання змінних зі списку вводу-виводу.
Уся множина змінних, що становлять список уведення-виводу, розбивається на чотири
функціональні групи:
1. Множина Р – змінні, що вводяться для розрахунків і для забезпечення виводу.
Прикладом може служити використовувана в програмах лексичного аналізатора
змінна, що містить рядок вихідного тексту програми, тобто сама змінна не
модифікується, а тільки містить вихідну інформацію.
2. Множина М – змінні, що модифікуються або створювані усередині програми.

55
3. Множина C – змінні, що беруть участь у керуванні роботою програмного модуля
(керуючі змінні).
4. Множина Т - невикористовувані в програмі (“паразитні”) змінні.
Оскільки кожна змінна може виконувати одночасно кілька функцій, необхідно враховувати її
в кожній відповідній функціональній групі.
Далі вводиться значення метрики Чепіна:
Q = a1P + a2M + a3C + a4T,
де a1, a2, a3, a4 – вагові коефіцієнти.
Вагові коефіцієнти використані для відбиття різного впливу на складність програми кожної
функціональної групи. На думку автора метрики, найбільша вага, рівна трьом, має
функціональна група С, тому що вона впливає на потік керування програми. Вагові
коефіцієнти інших груп розподіляються в такий спосіб: a1=1; a2=2; a4=0.5. Ваговий
коефіцієнт групи T не дорівнює нулю, оскільки “паразитні” змінні не збільшують складності
потоку даних програми, але іноді утрудняють її розуміння. З урахуванням вагових
коефіцієнтів вираження прийме вид:
Q = P + 2M + 3C + 0.5T .
Слід зазначити, що розглянуті метрики складності програми засновані на аналізі вихідних
текстів програм, що забезпечує єдиний підхід до автоматизації їх розрахунків.

Завдання
Для трьох програм з лабораторної роботи №6 розрахувати метрики потоку даних.
Використовувати формули розрахунків для метрики «модуль-глобальна змінна», спена,
метрики Чепіна. Порівняти отримані результати, зробити висновки.

Лабораторна робота №25

Види тестування. Планування тестування

Ціль роботи: вивчити класифікацію видів тестування, закріпити знання шляхом генерації
тестів різних видів, навчитися планувати тестові активності залежно від специфіки
функціональності, що поставляється на тестування.
Теоретичні відомості
Тестування – процес, спрямований на оцінку коректності, повноти і якості розробленого
програмного забезпечення.
Типи тестів по покриттю ( по глибині)
Smoke test – тестування системи для визначення коректної роботи базових функцій програми
в цілому, без поглиблення в деталі. При проведенні тесту визначається придатність
складання для подальшого тестування.
Minimal Acceptance Test (MAT, Positive test): тестування системи або її частини тільки на
валідних даних (дані, які необхідно використовувати для коректної роботи модуля/функції).
Перевіряється правильність роботи всіх функцій і модулів.
Для великих і складних додатків використовується обмежений набір сценаріїв і функцій.
Acceptance Test (AT): повне тестування системи або її частини як на коректних, так і на
некоректних даних/сценаріях. Вид тесту, спрямований на підтвердження того, що додаток
може використовуватися за призначенням при будь-яких умовах.
Тест покриває всі можливі сценарії тестування: перевірку працездатності модулів при
введенні коректних значень; перевірку при введенні некоректних значень; використання

56
форматів даних, відмінних від зазначених у вимогах; перевірку виняткових ситуацій,
повідомлень про помилки; тестування на різних комбінаціях вхідних параметрів; тестування
граничних значень інтервалів і т.д.
Тестові активності (типи тестів по покриттю ( по ширині)):
Defect Validation – перевірка результату виправлення дефектів. Містить у собі перевірку на
відтворюваність дефектів, які були виправлені в новому складанні продукту, а також
перевірку того, що виправлення не вплинуло на функціональність, що раніше працювала.
New Feature Test (NFT, AT of NF) – визначення якості поставленої на тестування нової
функціональності, яка раніше не тестувалася. Містить у собі: проведення повного тесту (АТ)
безпосередньо нової функціональності; тестування нової функціональності на відповідність
документації; перевірку всіляких взаємодій раніше реалізованої функціональності з новими
модулями й функціями.
Regression testing (регресійне тестування) – проводиться з метою оцінки якості раніше
реалізованої функціональності. Містить у собі перевірку стабільності раніше реалізованої
функціональності після внесення змін, наприклад додавання нової функціональності,
виправлення дефектів, оптимізація коду, розгортання додатка на новому оточенні. Може
бути проведене на рівні Smoke, MAT або AT.
Типи тестів по знанню коду
Чорний ящик – тестування системи, функціональне або нефункціональне, без знання
внутрішньої структури й компонентів системи. У тестувальника немає доступу до
внутрішньої структури й коду додатка або в процесі тестування він не звертається до них.
Білий ящик – тестування, засноване на аналізі внутрішньої структури компонентів або
системи. У тестувальника є доступ до внутрішньої структури й коду додатка.
Сірий ящик – комбінація методів білого й чорного ящика, що полягає в тому, що до частини
коду архітектури в тестувальника є, а до частини коду – немає.
Типи тестів по ступеню автоматизації
Ручне – тестування, у якому тест-кейси виконуються тестувальником вручну без
використання засобів автоматизації.
Автоматизоване – набір технік, підходів і інструментальних засобів, що дозволяє
виключити людину з виконання деяких завдань у процесі тестування. Тест-кейси частково
або повністю виконує спеціальний інструментальний засіб.
Типи тестів по ізольованості компонентів
Unit/component (модульне) – тестування окремих компонентів (модулів) ПО.
Integration (інтеграційне) – тестується взаємодія між інтегрованими компонентами або
системами.
System (системне) – тестується працездатність системи в цілому з метою перевірки того, що
вона відповідає встановленим вимогам.
Типи тестів по підготовленості.
Інтуїтивне тестування виконується без підготовки до тестів, без визначення очікуваних
результатів, проектування тестових сценаріїв.
Дослідницьке тестування – метод проектування тестових сценаріїв під час виконання цих
сценаріїв. Тестувальник робить перевірки, продумує їх, придумує нові перевірки, часто
використовує для цього отриману інформацію.
Тестування по документації – тестування по підготовлених тестових сценаріях, посібнику
зі здійснення тестів.
Типи тестів по місці й часу проведення
User Acceptance Testing (UAT) (приймальне тестування) – формальне тестування стосовно
потреб, вимог і бізнес процесів користувача, проведене з метою визначення відповідності
системи критеріям приймання й дати можливість користувачам, замовникам або іншим
авторизованим особам визначити, чи ухвалювати систему.
Alpha Testing (альфа-тестування) – модельоване або дійсне функціональне тестування,
виконується в організації, що розробляє продукт, але не проектною командою (незалежна
команда тестувальників, потенційні користувачі, замовники).
57
Beta Testing (бета-тестування) – експлуатаційне тестування потенційними або існуючими
клієнтами/замовниками на зовнішній стороні (у середовищі, де продукт буде
використовуватися), ніяк не пов'язаними з розроблювачами, з метою визначення, чи дійсно
компонент або система задовольняє вимогам клієнта/замовника й уписується в бізнеси-
процеси. Бета-тестування часто проводиться як форма зовнішнього приймального
тестування готового програмного забезпечення для того, щоб одержати відклик ринку.
Типи тестів по об'єкту тестування
Functional testing (функціональне тестування) – це тестування, засноване на аналізі
специфікації, функціональності компонента або системи. Функціональним можна назвати
будь-який вид тестування, який згідно з вимогами перевіряє правильну роботу.
Safety testing (тестування безпеки) – тестування програмного продукту з метою визначити
його безпеку (здатність програмного продукту при використанні обумовленим образом
залишатися в рамках прийнятного ризику заподіяння шкоди здоров'ю, бізнесу, програмам,
власності або навколишньому середовищу).
Security testing (тестування захищеності) – тестування з метою оцінити захищеність
програмного продукту. Тестування захищеності перевіряє фактичну реакцію захисних
механізмів, вбудованих у систему, на проникнення.
Compatibility testing (тестування сумісності) – для визначення можливості взаємодії
програмного продукту, перевірка працездатності додатка в різних середовищах (браузери і їх
версії, операційні системи, їх типи, версії й розрядність)
Види тестів:
- крос-браузерне тестування (різні браузери або версії браузерів)
- крос-платформне тестування (різні операційні системи або версії операційних систем)
Нефункціональне тестування –перевірка характеристик програми, коли перевіряється не
правильність роботи, а які-небудь властивості (зовнішній вигляд і зручність користування,
швидкість роботи й т.п.).
1. Тестування користувацького інтерфейсу (GUI) – тестування, виконуване шляхом
взаємодії із системою через графічний інтерфейс користувача.
− навігація
− кольори, графіка, оформлення
− зміст виведеної інформації
− поведінка курсору й гарячі клавіші
− відображення різної кількості даних (немає даних, мінімальна й максимальна кількість)
− зміна розмірів вікна або дозволу екрана
2. Тестування зручності використання (Usability Testing) – визначення ступеня
зрозумілості, легкості у вивченні й використанні, привабливості програмного продукту для
користувача за умови використання в заданих умовах експлуатації.
− візуальне оформлення
− навігація
− логічність
3. Тестування доступності (Accessibility testing) – визначає ступінь легкості, з якою
користувачі з обмеженими здатностями можуть використовувати систему або її компоненти.
4. Тестування інтернаціоналізації – тестування здатності продукту працювати в
локалізованих середовищах (здатність змінювати елементи інтерфейсу залежно від довжини
й напрямку тексту, міняти сортування/формати під різні локалі і т.д.).
5. Тестування локалізації (Localization testing) – тестування, проведене з метою перевірити
якість перекладу продукту з однієї мови на іншу.
6. Тестування продуктивності або навантажувальне тестування – процес тестування з
метою визначення продуктивності програмного продукту.
Види тестів:
- навантажувальне тестування (Performance and Load testing) – вид тестування
продуктивності, проведеного з метою оцінки поведінки компонента або системи при

58
зростаючому навантаженні, наприклад кількості паралельних користувачів і/або операцій, а
також визначення, яке навантаження може витримати компонент або система;
- об'ємне тестування (Volume testing) – дозволяє одержати оцінку продуктивності при
збільшенні обсягів даних у базі даних додатку;
- тестування стабільності й надійності (Stability / Reliability testing) – дозволяє перевіряти
працездатність додатка при тривалому (багатогодинному) тестуванні із середнім рівнем
навантаження.
- стресове тестування (Stress testing) – вид тестування продуктивності, що оцінює систему
або компонент на граничних значеннях робочих навантажень або за їхніми межами, або ж у
стані обмежених ресурсів, таких як пам'ять або доступ до сервера.
7. Тестування вимог (Requirements testing) – перевірка вимог на відповідність основним
характеристикам якості.
8. Тестування прототипу (Prototyte testing) – метод виявлення структурних, логічних
помилок і помилок проектування на ранній стадії розвитку продукту до початку фактичної
розробки.
9. Тестування установки (Installability testing) і ліцензування – процес тестування
встановлюваності програмного продукту.
Види тестів:
- формальний тест програми установки додатка (перевірка користувацького інтерфейсу,
навігації, зручності користування, відповідності загальноприйнятим стандартам
оформлення);
- функціональний тест програми установки;
- тестування механізму ліцензування й функцій захисту від піратства;
- перевірка стабільності додатка після установки.
10. Тестування на відмову й відновлення (Failover and Recovery Testing) – емуляція відмов
системи або реально викликувані відмови в керованому оточенні.
Тестування програмного продукту включає наступні етапи:
1. Вивчення й аналіз предмета тестування.
2. Планування тестування.
3. Виконання тестування.
Вивчення й аналіз предмета тестування починається до затвердження специфікації й
триває на стадії розробки (кодування) програмного забезпечення. Кінцевою метою етапу
вивчення й аналізу предмета тестування є одержання відповідей на два питання:
- які функціональності мають бути протестовані,
- як ці функціональності працюють.
Планування тестування відбувається на стадії розробки (кодування) ПО. На цій стадії
перед тестувальником стоїть завдання пошуку компромісу між обсягом тестування, який
можливий у теорії, і обсягом тестування, який можливий на практиці. На даній стадії
необхідно відповістити на запитання: як будемо тестувати? Результатом планування
тестування є тестова документація.
Виконання тестування відбувається на стадії тестування і являє собою практичний пошук
дефектів з використанням тестової документації, складеної раніше.
Для всіх програмних продуктів виконують наступні типи тестів і їх композиції.
Для першого білда рекомендується проводити Smoke+AT готової функціональності:
поверхневе тестування (Smoke Test) виконується для визначення придатності складання для
подальшого тестування; повне тестування системи або її частини як на коректних, так і на
некоректних даних/сценаріях (Acceptance Test, AT) дозволяє виявити дефекти й внести запис
про них у багтрекінгову систему.
Для наступних білдів композиції тестів можуть бути наступними:
- Якщо не була додана нова функціональність, то: DV+MAT. Тобто, виконується перевірка
виправлення дефектів програмістом (Defect Validation, DV), а також перевірка
працездатності іншої функціональності після виправлення дефектів на позитивних сценаріях
59
(Minimal Acceptance Test, MAT).
- Якщо була додана нова функціональність, то: Smoke+DV+NFT+Regression Test. Зокрема,
виконується поверхневе тестування (Smoke Test), перевірка виправлення дефектів
програмістом (Defect Validation, DV), тестування нових функціональностей (New Feature
Testing, NFT), перевірка старих функціональностей, тобто регресійне тестування (Regression
Test).
- Якщо була додана нова функціональність, те можливий також варіант: DV+NFT+Resression
test, тобто без виконання Smoke Test.
Залежно від типу й специфіки додатка (web, desktop, mobile) виконують спеціалізовані тести
(наприклад, кроссбраузерне або кроссплатформне тестування, тестування локалізації й
інтернаціоналізації й ін.).
Завдання
1. Виконати генерацію тестів різних видів для конкретного об'єкта реального миру (приклад
наведений на рисунку). Передбачити не менш 10-15 підходящих тестів різних видів.
2. Спланувати тестові активності для наступних завдань:
2.1 Поставлений на тестування модуль 1, модуль 2, модуль 3.
2.2 Проведені виправлення (fix) для заведених дефектів, доставлена нова функціональність –
модуль 4.
2.3 Замовник вирішив розширювати ринки збуту й просить здійснити підтримку для
Великобританії (крім уже існуючої для України).
2.4 Замовник прагне переконатися, що ПЗ витримає навантаження в 2000 користувачів.

60
61
Список додаткової літератури для використання під час виконання
лабораторних робіт

1. Р.Вайнер, Л.Пинсон «C++ зсередини». Пер. З англ. - Київ: «Диасофт», 1993.-304с.


2. Б.Страуструп «Мова програмування C++» - Москва: «И.В.К.Софт», 1991.-315с.
3. С.Дьюхарст, К.Старк «Програмування на C++»- Київ: «Диасофт», 1993.-272с.
4. И.М.Двоеглазов «Мова програмування C++, довідковий посібник» - Київ:
«Евроиндекс», 1993

62

You might also like