Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Глинський С++.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.26 Mб
Скачать

Тема 9. Функції (1)

  1. Типи функцій. Мова програмування С++ дає можливість реалізувати кон­цепцію структурного аналізу алгоритмів.

Структурний аналіз - попередє опрацювання складної задачі чи гро­міздкого алгоритму та поділі його на окремі простіші час­тий. У С++ ці частини реалізуються за допомогою функцій. Окремі функції об’єднують у спільну програму. У відкомпільованому вигляді така програма утворює модуль.

Розрізняють:

  • головну функцію main(), що обов’язково входить до кожної програми і повертає ціле значення. Визначення void main() {/*... */} ніколи не було в C++, так само як і не було навіть в CEven if your compiler accepts "void main()" avoid it, or risk being considered ignorant by C and C++ programmers. http://www.stroustrup.com/bs_faq2.html#void-main

Навіть якщо ваш компілятор приймає “void main()”, уникайте цього, бо програмісти С і C++ вважатимуть вас неосвіченим.

  • стандартні функції мови описані (визначені) у бібліотеках. (зокрема, математичні функції sin(), соs() з бібліотеки math.h).

  • функція користувача - це поіменована група команд, яка оголошена у файлі заголовків (або в основній програмі) та описана у модулі (в основній програмі). До функції можна звернутись (викликати) з будь-якого місця програми необхід­ну кількість разів.

Прості програми не потребують використання функцій користувача.

2. Оголошення функцій користувача. Кожну функцію користувача перед першим викликом передусім необхідно ого­лосити (задекларувати, створити прототип, сигнатуру(набір характеристик)). За стандартом ISO/ANSI прототипи функцій оголошують у спеціальних файлах заголовків. У програму ці файли при­єднують за допомогою директиви #include, наприклад:

#include <conio.h>

#include "MyHeader.h"

Прототипи стандартних функцій розміщені у папці IN­CLUDE. Синтаксис мови дає змогу розмістити прототип також і в основній програмі. Функцію користувача оголошують так:

<тип функції> <назва функції> (<список формальних параметрів>);

де

  • тип функції - це тип даного, який функція повертає в ос­новну програму. Тип функції можна не зазначати. За замов­чуванням функція повертає у програму дане цілого типу int. Функцію, яка не повертає у програму жодного результату, оголошують з типом void. Для функції, яка не залежить від жодних параметрів, у круглих дужках записують службове слово void.

  • назву функції надає користувач за правилом створення ідентифікаторів.

  • у списку формальних параметрів через кому записують змінні, зазначаючи їхні типи. Тип необхідно зазначати для кожної змінної окремо. Імена змінних можна опускати. Якщо функція не набуває жодних значень, то список формальних параметрів може бути відсутній. Круглі дужки опускати не можна.

Приклад 1. Розглянемо сигнатури функцій

float Suma(int kil, float cina);

void drib (float, float);

kod(int k1, int k2); double loto(void);

У цьому випадку оголошена функція Suma типу float, яка залежить від двох змінних: перша змінна цілого типу int, дру­га - типу float.

Функція drib залежить від двох змінних дійс­ного типу float і не повертає у програму жодного значення.

Ці­ла функція kod залежить від двох змінних цілого типу, дійсна функція loto типу double не залежить від жодних параметрів.

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

float Suma(int kil, float cina = 2.5); void drib (float v = 1.2, float n = 3); kod(int k1, int k2 = 5);

3. Опис функцій користувача. Опис функції складається із заголовка без «;» і тіла функції, записаного у фі­гурних дужках, а саме:

<тип функції> <назва функції>(<список формальних параметрів>)

{

<тіло функції>;

return (<назва змінної 1>);

}

У тілі функції записують команди, які задають дію функ­ції. Результат виконання функції повертається в основну програму (у точку виклику) за допомогою змінної 1 командою return. Тип змінної 1 має збігатися з типом функції. У тілі функцій типу void команду return не зазначають. У команді return круглі дужки можна не писати.

Зауваження 1. Функцію можна описувати і на початку про­грами. У такому випадку декларувати її не потрібно.

Приклад 2. Функції Suma і drib, оголошені у прикла­ді 1, у деякій програмі можна описати так:

float Suma(int kil, float cina) // Заголовок функції

{

float s = kil * cina;

return (s); // Значення, яке повертає функція в основну програму

}

//

void dilennya (float v, float n)

{

cout <<“\n dilennya = " << v / n;

}

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

Приклад 3. Сигнатуру функції, яка обчислює периметр k-кутної правильної фігури зі стороною r, можна оголосити так: float perymetr (int k = 4, float r = 2.5);

а описати власне функцію так: float perymetr (int k, float r)

{

float p;

p = k * r;

return (p);

}

  1. Виклик функцій користувача. До функції користувача звертаються з розділу команд основної програми (функції main()) або з іншої функції. Виклик функцій можна виконати двояко: або командою виклику, або з виразів так:

<назва функції>(<список фактичних параметрів>)

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

Приклад 4. Розглянемо сигнатуру з прикладу 3. float perymetr (int k = 4, float r = 2.5);

До цієї функції можна звернутись одним із способів:

perymetr (7, 2.8); perymetr (8); perymetr 0;

У першому випадку змінній k буде присвоєне значення 7, а змінній r - 2,8; у другому випадку k = 8, r = 2,5; в третьо­му k = 7, r = 2,5.

У списку фактичних параметрів не можна пропускати змінні з середини списку, тобто не можна функцію perymetr () ви­кликати так: perymetr (1.65);, оскільки тут пропущено ініціаліза­цію першої змінної k.

Задача 1 (про товарний чек). Використовуючи функцію користувача, скласти програму для виведення на екран товар­ного чека для купівлі ті штук товару за ціною тп грнивень за одну штуку. Ціну та кількість купленого товару ввести самос­тійно у режимі діалогу.

#include <iostream> // Товарний чек

#include <conio.h>

using namespace std;

float Suma(int, float); // Оголошення функції Suma

int main() {

float m; int n;

cout << "Введіть ціну за 1 шт. \n"; cin >> m;

cout << "Введіть кількість купленого товару \n"; cin >> n;

cout<< "\t ТОВАРНИЙ ЧЕК \n";

cout <<"Ціна за 1 шт. -"<< m << " грн \n";

cout <<"За "<< n << " штук необхідно сплатити ";

cout<< Suma(n, m) << " грн\n"; // Виклик функції Suma

cout << "\t Дякуємо за покупку!";

getch();

}

float Suma(int kil, float cina) // Опис функції Suma

{

float s;

s = kil * cina;

return s;

}

5. Посилання. До даних можна звертатись за допомогою імен або посилань.

Посилання слугує для надання ще одного імені (псевдоніма, синоніма, аліасу) даному. Посилання утво­рюють так:

<тип даного> &<назва посилання> = <назва змінної>;

Наприклад, float &cina = suma;

У цьому випадку посилання cina і змінна suma вказувати­муть на одну і ту саму адресу в пам’яті комп’ютера. Для посилань не резервується додаткова оперативна пам’ять.

У C++ можна утворювати посилання на дані, але не на їх­ні типи. Значення посилання ініціалізуються відразу під час того оголошення, тобто на етапі компіляції. У нашому прикладі посилання cina проініціалізоване змінною suma. Отже, якщо suma = 11, то і значення посилання cina також буде 11.

Під час зміни значення посилання змінюється значення змінної, на яке це посилання вказує. Отже, якщо у програмі записати команду cina = 15.7, то змінна suma також набуде зна­чення 15.7.

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

Щоб передати посилання чи вказівники у функцію як параметри і не змінити значення фактичних параметрів, тре­ба в оголошенні функції до кожного параметра дописати ключове слово const.

Наприклад, int sort(const int *р, const int *g);

У C++ посиланням може бути не тільки змінна чи стала, а і функція

<тиn> & <назва функції>(<список формальних параметрів>)

Така функція повертає синонім імені комірки, у яку зане­сено результат (посилання на змінну певного типу). Функція-посилання має двояке призначення. По-перше, як і звичайна функція, вона може повертати значення в основну програму. По-друге, їй самій можна надавати значень, що є унікальним випадком у програмуванні.

Приклад 5. Оголосимо змінну та вказівник на дійсний тип float *prt, u; та опишемо функцію float &ltem(float* а, int і) {return *(а + і);}

Виділимо ділянку пам’яті для зберігання значень десяти дійсних чисел prt = new float[10]; Викликати цю функцію можна звичайно: u = ltem(prt, 3); Тоді змінній u буде надано значення четвертого елемента. Уведемо значення п’ятого числа так: ltem(prt, 4) = 10.

Функції ltem() надаємо значення, тобто у ділянку пам’яті буде занесено число 10.

Задача 2 (про гуртову покупку). Нехай знижки на гуртов­і залежать від суми, на яку зроблено замовлення, а саме: під час купівлі товару на суму до 200 грн. покупець має сплатити 100% від вартості товару, на суму від 200 до 500 грн. йому надається знижка 10%, а на суму понад 500 грн. - 20%. Ввести з клавіатури суму, на яку зроблено замовлення, і вивести суму, яку необхідно сплатити з урахування системи знижок. Використати функцію користувача, аргументами якої є посилання на змінні.

#include <iostream> // Гуртове замовлення

#include <cstdlib>

using namespace std;

void Suma(float &t); // Оголошення функції Suma

int main() {

float S; float &pS = S;

cout <<"Введіть суму замовлення \n"; cin>> S;

cout<< "\t Вам необхідно сплатити \n";

Suma(pS); // Виклик функції

cout << S << " грн.";

system("PAUSE");

return EXIT_SUCCESS;

}

// ---------------------------------------------------

void Suma(float &t) // Опис функції Suma

{ // Зміна значення посилання t

if (t >= 200.0 && t < 500) t *= 0.9;

if (t >= 500.0) t *= 0.8;

}

У цьому випадку функція Suma() повертає у програму зна­чення через посилання pS.

6. Рекурсія. Рекурсією називається алгоритмічна конст­рукція, де підпрограма викликає сама себе. Рекурсія дає змо­гу записувати циклічний алгоритм, не застосовуючи команд циклу.

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

Використання рекурсії збільшує час виконання програми і часто вимагає значного обсягу пам'яті для зберігання копій підпрограми на рекурсивном спуску. Тому на практиці заміняють рекурсивні алгоритми на ітеративні.

Рекурсія дозволяє створювати код з незмінними змінними, що

  • робить код більш читабельним,

  • захищає від помилок типу «дії виконані в невірному порядку», «використано не-ініціалізована змінна» і інших аналогічних,

  • полегшує організацію контролю коректності вхідних даних,

  • дозволяє читати код з будь-якого місця, не переглядаючи його весь, відстежуючи всі зміни змінної,

  • полегшує налагодження.

У деяких випадках, рекурсія дозволяє більш ясно сформулювати ідею алгоритму; наприклад, це відноситься до алгоритму швидкого сортування - quicksort - який рекурсівен за своєю природою.

Але, звичайно, використовувати рекурсію слід розумно. Потрібно враховувати те, що часто (не завжди) вона веде до великих витрат стека і деякого зниження продуктивності.

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

У будь-якому випадку, існує безліч мов, в яких рекурсія - єдиний засіб, для виконання багатьох дій. До таких мов відносяться не тільки «екзотичні» функціональні мови, такі як Haskell, а й засоби більш широкого застосування: XSLT-процесори, макро-процесор m4 (основа системи autoconf і багатьох систем конфігурації), і інші засоби. Таке «тяжіння» до рекурсії - не наслідок обмеженості цих мов; навпаки, ці мови спеціально оптимізовані для використання рекурсії; вони активно використовують факт незмінності змінних і здатні виконати безліч оптимізацій, позбавляючи програміста від багатьох турбот.

Типовий приклад використання рекурсії - обчислення факторіала числа. Нагадаємо, що факторіал числа - це добу­ток цілих чисел від одиниці до певного заданого числа.

Отже, п! = n (п - 1) • (n - 2) • ... •1. Рекурсивна властивість факто­ріала: n! = n• (n-1)!.

Приклад 4. Скласти функцію для обчислення n!, викорис­товуючи рекурсію, можна так: long factorial (int n)

{long fact;

if (n > 1) fact = n * factorial(n -1);

else fact = 1; return fact;}

Зауваження 2. Оскільки навіть для невеликих чисел значення факторіала є досить великим, то у цьому прикладі для функції factorial задано тип long. Це дає змогу обчислити факторіали чисел від 1 до 16. Для визначення факторіалів чисел більших від 16 необхідно використати алгоритми „довгої арифметики”.

Обчислимо функцію factorial(5) = 5 • factorial(4) = 5 • 4 • factorial(3) = 5 •4• 3 factorial(2) = 5•4•3•2•factorial(l) = 5• 4•3•2•1 = 120. Переконайтеся, що factorial (13) = 1932053504.

Приклад 6. Рекурсивна функція обчислення суми цілих чисел від а до b має вигляд

int Suma (int a, int b){

int S;

if (a == b) S = a;

else S = b + Suma(a, b -1); return S;}

Зауваження 3. Застосовуючи рекурсію, потрібно правильно складати стоп-умови, які забезпечують закінчення циклічних обчислень.

Вправи

З презентації

  1. Скласти програму для обчислення факторіала числа. Знайдіть факторіали 4, 7, 10.

  2. Запишіть функцію для обчислення добутку цілих чисел з діапазону від а до b. Знайдіть добуток чисел, діапазон увести з клавіатури.

  3. Обчислити суму перших n цілих додатних чисел.

  4. Перевірте, чи є уведено число паліндромом. Наприклад, 12321, 2442 – паліндроми

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

  2. Задані два натуральних числа. Визначити в якому з них більше цифр, організувавши функцію для підрахунку кількості цифр в будь-якому натуральному числі.

  1. Придумайте призначення, складіть і запишіть функцію kod(int k1, int k2), сигнатура якої оголошена у прикладі 1.

  2. Придумайте призначення та опишіть дві - три функції різних типів.

  3. Модифікуйте програму Товарний чек на випадок купівлі двох видів товару за різною ціною. Товарний чек виведіть у файл.

  4. Модифікуйте програму Товарний чек на випадок купівлі h видів то­вару за різною ціною, якщо кількість товарів наперед відома.

Вказівка: використайте команду циклу for.

  1. Модифікуйте програму Товарний чек на випадок купівлі k видів товару за різною ціною, якщо кількість товарів наперед невідома.

Вказівка. Використайте команду циклу while з параметром завершен­ня циклу.

  1. Виведіть у файл факторіали чисел від 1 до 10.

  2. Модифікуйте програму Гуртове замовлення, використовуючи вказів­ники.

  3. Модифікуйте програму Гуртове замовлення, використовуючи вка­зівники з ключовим словом const.

  4. Розв’яжіть задачу № 7 вашого варіанта, використавши дві функції користувача для знаходження суми та добутку. Результати виведіть

а) на екран;

б) у файл.

  1. Розв’яжіть задачу № 7 вашого варіанта, використавши дві функції користувача. Аргументами цих функцій мають бути посилання на дійсні змінні.

  2. Розв’яжіть задачу № 7 вашого варіанта, використовуючи рекур­сивні функції (тобто без використання команди циклу).

1.7