Варіанти індивідуальних завдань. Вивчення можливостей середовища Microsoft Visual C++

Мета роботи

 

Вивчення можливостей середовища Microsoft Visual C++. Створення, трансляція та налагодження програм. Отримання практичних навичок роботи з масивами та функціями в мові С++.

 

2.2 Організація самостійної роботи студентів

Під час підготовки до виконання лабораторної роботи необхідно вивчити індивідуальне завдання (п. 1.3), виконати розробку алгоритму вирішення задачі та підготовити текст програми щодо реалізації розробленого алгоритму, підготовити відповідні розділи звіту та вивчити відповідний теоретичний матеріал, який викладено у лекціях: „Основи мови С++, структура програми, типи змінних”, „Операції мови С++: арифметичні, збільшення, зменшення, логічні”, „Керуючі конструкції мови С++” та у наступних підручниках і навчальних посібниках

При вивченні теоретичного матеріалу необхідно усвідомити, що будь-яка програма (проект) на Сі складається з файлів. Файли транслюються Сі-компілятором незалежно одне від одного, а потім поєднуються програмою -побудовником задач. За наслідками цього створюється файл із програмою, готовою до виконання. Файли, що містять тексти Сі-програми, називаються вихідними.

У мові Сі вихідні файли бувають двох типів:

· заголовні, або h-файли;

· файли реалізації, або Сі-файли.

Назви заголовних файлів мають розширення ".h". Назви файлів реалізації мають розширення ".c" для мови Сі й ".cpp", ".cxx" або ".cc" для мови C++. Заголовні файли містять тільки описи. Файли реалізації, або Cі-файли, містять тексти функцій і визначення глобальних змінних. Говорячи спрощено, Сі-файли містять самі програми, а h-файли - лише інформацію про програми.

Подання вихідних текстів у вигляді заголовних файлів і файлів реалізації необхідно для створення великих проектів, що мають модульну структуру. Заголовні файли використовуються для передачі інформації між модулями. Файли реалізації – це окремі модулі, які розробляються та транслюються незалежно одне від одного та поєднуються при створенні виконуваної програми. Файли реалізації підключають заголовні файли за допомогою директиви препроцесора #include. Самі заголовні файли також можуть використовувати інші заголовні файли.

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

Розглянемо приклад програми, яка виводить на екран фразу "Hello, World".

#include <stdio.h> int main() { printf("Hello, World\n"); return 0; }

 

Перший рядок підключає заголовний файл із описом стандартних функцій введення даних Сі-бібліотеки. У файлі описаний прототип функції printf (друк за форматом). Виконання програми починається з функції main, яка повертає після закінченні роботи ціле число (рядок return 0;), що трактується операційною системою, як код завершення завдання. Число нуль означає успішне виконання задачі, але програміст може самостійно визначати коди завершення.

При розгляді типів змінних у C++ варто розрізняти поняття базового типу та конструкції, що дозволяє будувати нові типи на основі вже побудованих. Базових типів зовсім небагато – це цілі та дійсні числа, які розрізняються за діапазонами можливих значень (або по довжині в байтах) та логічний тип даних. До конструкцій відноситься масив, покажчик та структура, а також клас.

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

 

int a[10];char c[256];double d[1000];

 

У першому рядку описаний масив цілих чисел з 10 елементів. Необхідно пам’ятати, що нумерація в Сі завжди починається з нуля, тому індекси елементів масиву змінюються в межах від 0 до 9. У другому рядку описаний масив символів з 256 елементів (індекси в межах 0...255), у третьому – масив дійсних чисел з 1000 елементів (індекси в межах 0...999). Для доступу до елемента масиву вказується ім'я масиву та індекс елемента у квадратних дужках, наприклад: a[10], c[255], d[123].

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

int *a, *b, c, d;char *e;void *f;

 

У першому рядку описані покажчики a і b на тип int та прості змінні c та d типу int (c і d – не є покажчиками!). З покажчиками можливі наступні дві дії:

1. Присвоїти покажчику адресу деякої змінної. Для цього використовується операція узяття адреси, що позначається амперсендом &. Наприклад, рядок a = &c; дозволяє покажчику a присвоїти значення адреси змінної c;

2. Одержати об'єкт, адреса якого записана у покажчику; для цього використовується операція зірочка «*», що записується перед покажчиком. Наприклад, рядок d = *a; присвоює змінній d значення цілочисельної змінної, адреса якої записана в a.

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

· для угруповання можна використовувати круглі дужки, наприклад, опис int *(x[10]); означає "масив з 10 елементів типу покажчик на int";

· при відсутності дужок пріоритети конструкцій опису розподілені в такий спосіб:

o операція * визначення покажчика має найнижчий пріоритет. Наприклад, опис int *x[10]; означає "масив з 10 елементів типу покажчик на int". Тут до ім'я змінної x спочатку застосовується операція визначення масиву [] (квадратні дужки), оскільки вона має більше високий пріоритет, чим зірочка. Потім до отриманого масиву застосовується операція визначення покажчика. У результаті виходить "масив покажчиків", а не покажчик на масив! Якщо нам потрібно визначити покажчик на масив, то варто використовувати круглі дужки при описі: int (*x)[10]; Тут до ім'я x спочатку застосовується операція * визначення покажчика;

o операції визначення масиву [] (квадратні дужки після ім'я) та визначення функції (круглі дужки після ім'я) мають однаковий пріоритет, більше високий, ніж зірочка. Приклади:

- int f(); Описано прототип функції f без аргументів, що повертає значення типу int;

- int (*f())[10]; Описано прототип функції f без аргументів, що повертає значення типу покажчик на масив з 10 елементів типу int;

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

void (*a[100])(int x);

Описується змінна a. До неї спочатку застосовується операція опису масиву з 100 елементів, далі – визначення покажчика, далі – функція від одного цілочисельного аргументу x типу int, нарешті – визначення типу, що повертається, void. Опис читається в такий спосіб:

1. a - це

2. Масив з 100 елементів типу

3. Покажчик на

4. Функцію з одним аргументом x типу int, що повертає значення типу

5. void.

Нижче розставлені номери операцій у порядку їхнього застосування в описі змінної a:

 

void (* a [100])(int x);5) 3) 1) 2) 4)

Спеціального типу даних рядок у Сі немає. Рядки представляються масивами символів (а символи – їхніми числовими кодами). Останнім символом масиву, що представляє рядок, повинен бути символ з нульовим кодом. Приклад:

 

char str[10];str[0] = 'e'; str[1] = '2';str[2] = 'e'; str[3] = '4';str[4] = 0;

 

Описано масив str з 10 символів, що може представляти рядок довжиною не більше 9, оскільки один елемент повинен бути зарезервований для завершального нуля. Далі в масив str записується рядок "e2e4". Рядок завершується нульовим символом. Усього запис рядка використовує 5 перших елементів масиву str з індексами 0...4. Останні 5 елементів масиву не використовуються. Масив можна ініціалізувати безпосередньо при описі, наприклад:

char t[] = "abc";

 

Тут не вказується у квадратних дужках розмір масиву t, компілятор його обчислює сам. Після операції присвоювання записана строкова константа "abc", що заноситься в масив t. У результаті компілятор створює масив t із чотирьох елементів, оскільки на рядок приділяється 4 байти, включаючи завершальний нуль. Строкові константи записуються у подвійних апострофах, на відміну від символьних, які записуються в одинарні.

Вирази у Сі складаються зі змінних або констант, до яких застосовуються різні операції. Для вказівки порядку операцій можна використовувати круглі дужки.

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

Втім, ці незвичні операції можна не використовувати, заміняючи їх традиційними.

До чотирьох звичайних арифметичних операцій додавання +, відрахування – , множення * та ділення / у Сі додана операція знаходження остачі від ділення першого цілого числа на друге, котра позначається символом відсотка %. Пріоритет в операції обчислення остачі % такий же, як і в ділення або множення. Відзначимо, що операція % перестановочна з операцією зміни знака (унарним мінусом), наприклад, за наслідками виконання двох рядків

Операція порівняння порівнює два вирази. За наслідками виробляється логічне значення – true або false залежно від значень виразів. Приклади:

bool res;int x, y;res = (x == y); // true, якщо x дорівнює y, інакше falseres = (x == x); // завжди trueres = (2 < 1); // завжди false

 

Операції порівняння в Сі позначаються в такий спосіб:

== дорівнює, != не дорівнює,> більше, >= більше або дорівнює,< менше, <= менше або дорівнює.

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

& побітове логічне додавання ("і")| побітове логічне множення ("або")~ побітове логічне заперечення ("не")^ побітове додавання по модулі 2 (виключне "або")

 

(Необхідно пам'ятати, що логічні операції множення та додавання записуються за допомогою подвійних знаків && або ||, а побітові – за допомогою одинарних.)

Керуючі конструкції дозволяють організовувати цикли та розгалуження в програмах. У Си всього кілька конструкцій, причому половину з них можна не використовувати (вони реалізуються через інші).

Оператор if ("якщо") дозволяє організувати розгалуження в програмі. Він має дві форми: оператор "якщо" і оператор "якщо...інакше". Оператор "якщо" має вигляд:

if (умова) дія;

оператор "якщо...інакше" має вигляд

 

if (умова) дія1;else дія2;

Як умову можна використовувати будь-який вираз логічного або цілого типу. Нагадаємо, що при використанні цілочисельного виразу значенню "істина" відповідає будь-яке ненульове значення. При виконанні оператора "якщо" спочатку обчислюється умовний вираз після if. Якщо воно дійсне, то виконується дія, якщо воно не дійсне, то нічого не відбувається. Наприклад, у наступному фрагменті в змінну m записується максимальне зі значень змінних x та y:

 

double x, y, m;. . .m = x;if (y > x) m = y;

 

При виконанні оператора "якщо...інакше" у випадку, коли умова істинна, виконується дія, що записана після if; у противному випадку виконується дія після else. Наприклад, попередній фрагмент запишеться в такий спосіб:

double x, y, m;. . .if (x > y) m = x;else m = y;

 

Коли треба виконати кілька дій залежно від істинності умови, необхідно використовувати фігурні дужки, поєднуючи декілька операторів у блок, наприклад:

 

double x, y, d;. . .if (d > 1.0) { x /= d; y /= d;}

Тут змінні x та y діляться на d тільки в тому випадку, коли значення d більше одиниці.

Кілька умовних операторів типу "якщо...інакше" можна записувати послідовно (тобто дія після else можна знову записати умовний оператор). У результаті реалізується вибір з декількох можливостей. Конструкція вибору використовується в програмуванні дуже часто. Приклад: є дійсна змінна x, необхідно записати у дійсну змінну y значення функції sign(x):

 

sign(x) = -1, при x < 0sign(x) = 1, при x > 0sign(x) = 0, при x = 0

Це робиться з використанням конструкції вибору:

 

double x, s;. . .if (x < 0.0) { s = (-1.0);}else if (x > 0.0) { s = 1.0;}else { s = 0.0;}

 

При виконанні цього фрагмента спершу перевіряється умова x < 0.0. Якщо вона дійсна, то виконується оператор s = (-1.0); інакше перевіряється друга умова x < 0.0. У випадку його істинності виконується оператор s = 1.0, інакше виконується оператор s = 0.0. Фігурні дужки тут додані для поліпшення структурності тексту програми.

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

Конструкція циклу "доки" відповідає циклу while:

 

while (умова) дія;

 

Цикл while називають циклом із передумовою, оскільки умова перевіряється перед виконанням тіла циклу. Цикл while виконується в такий спосіб: спочатку перевіряється умова. Якщо вона дійсна, то виконується дія. Потім знову перевіряється умова; якщо вона дійсна, то знову повторюється дія, і так нескінченно. Цикл завершується, коли умова стає помилковою. Приклад:

int n, p;. . .p = 1;while (2*p <= n) p *= 2;

 

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

Якщо необхідно перервати виконання циклу, то необхідно використовувати оператор

 

break;

Оператор застосовується усередині тіла циклу у фігурних дужках. Приклад: потрібно знайти корінь цілочисельної функції f(x), дійсної для цілочисельних аргументів.

int f(int x); // Опис прототипу функції. . .int x;. . .// Шукаємо корінь функції f(x)x = 0;while (true) { if (f(x) == 0) { break; // Знайшли корінь } // Переходимо до наступного цілого значення x // у порядку 0, -1, 1, -2, 2, -3, 3, ... if (x >= 0) { x = (-x - 1); } else { x = (-x); }}// Твердження: f(x) == 0

 

Тут використовується нескінченний цикл "while (true)". Вихід із циклу здійснюється за допомогою оператора "break".

Іноді потрібно пропустити виконання тіла циклу при яких-небудь значеннях змінних у циклі, переходячи до наступного набору значень і чергової ітерації. Для цього використовується оператор:

continue;

 

Оператор continue, так само, як і break, використовується лише в тому випадку, коли тіло циклу складається більш ніж з одного оператора та вставлено у фігурні дужки. Його варто розуміти як перехід на фігурну дужку, що закриває тіло циклу. Приклад: нехай заданий n+1 крапка на дійсній прямій , i = 0, 1,..., n; крапки будуть називатися вузлами інтерполяції. Елементарний інтерполяційний багаточлен Лагранжа – це багаточлен ступеня n, що приймає нульові значення у всіх вузлах , крім . В k-ом вузлі багаточлен приймає значення 1. Багаточлен обчислюється по наступній формулі:

 

Нехай потрібно обчислити значення елементарного інтерполяційного багаточлена при значенні x = t. Це робиться за допомогою наступного фрагмента програми:

 

double x[100]; // Вузли інтерполяції (не більше 100)int n; // Кількість вузлів інтерполяціїint k; // Номер вузлаdouble t; // Крапка, у якій обчислюється значенняdouble L; // Значення багаточлена L_k(x) у tint i;. . .L = 1.0; // Початкове значення добуткуi = 0;while (i <= n) { if (i == k) { ++i; // До наступного вузла continue; // Пропустити k-й множник } // Обчислення добутку L *= (t - x[i]) / (x[k] - x[i]); ++i; // До наступного вузла}// Відповідь у змінної L

 

Тут оператор continue використовується для того, щоб пропустити обчислення при i = k.

Циклу for має вигляд:

for (ініціалізація; умова продовження; ітератор) тіло циклу;

Ініціалізація виконується один раз перед першою перевіркою умови продовження та першим виконанням тіла циклу. Умова продовження перевіряється перед кожним виконанням тіла циклу. Якщо умова істинна, то виконується тіло циклу, інакше цикл завершується. Ітератор виконується після кожного виконання тіла циклу (перед наступною перевіркою умови продовження).

Оскільки умова продовження перевіряється перед виконанням тіла циклу, цикл for є, подібно циклу while, циклом із передумовою. Якщо умова продовження ніколи не виконується, то тіло циклу не виконується жодного разу, що добре як з погляду надійності програми, так і з погляду простоти та естетики (оскільки не потрібно окремо розглядати виняткові випадки).

Розглянемо приклад підсумовування масиву з використанням циклу for:

double a[100]; // Масив a містить не більше 100 эл-тівint n; // Реальна довжина масиву a (n <= 100)double sum; // Змінна для суми эл-тів масивуint i; // Змінна циклу. . .sum = 0.0;for (i = 0; i < n; ++i) { sum += a[i]; // Збільшуємо суму на a[i]}

 

Тут цілочисельна змінна i використовується як змінна циклу. В операторі ініціалізації змінної i присвоюється значення 0. Умовою продовження циклу є умова i<n. Ітератор ++i збільшує змінну i на одиницю. Таким чином, змінна i послідовно приймає значення 0, 1, 2,..., n-1. Для кожного значення i виконується тіло циклу.

При вивченні керуючих конструкцій необхідно ознайомитися із логічним представлення цих конструкцій у вигляді алгоритмів з використанням графічної мови, що описана у Єдиній системі програмної документації (ЄСПД).

Варіанти індивідуальних завдань