- •Процедурно-орієнтоване програмування Організація підпрограм Поняття підпрограми
- •Організація підпрограм
- •Механізм передачі параметрів
- •Принцип локалізації
- •Концепція розподілу пам’яті
- •Способи передачі параметрів
- •Вказівники на функції
- •Розширені можливості використання підпрограм Функції з аргументами за замовчуванням
- •Перевантаження функцій
- •Шаблони функцій
- •Вбудовані функції
- •Рекурсія
Організація підпрограм
Підпрограми поділяються на стандартні і визначені користувачем. Перші входять до складу мови програмування і викликаються для виконання за строго фіксованими іменами. Другі розробляються самим користувачем.
Всі стандартні засоби зібрані у спеціальні набори — стандартні бібліотеки, які, зазвичай, повинні бути підключені в тексті програми. Наприклад, у програмах на мові Рascal відповідні бібліотеки слід підключити у розділі uses; у мовах С/С++ для цього служить директива препроцесора include.
З практичної точки зору знання стандартних підпрограм є корисним і необхідним, адже застосовувати готові деталі набагато легше, ніж створювати їх самому. Проте запрограмувати розв’язання «всіх можливих підзадач» — утопія, тому програмістам доводиться постійно створювати власні підпрограми.
Реалізація підпрограм може різнитися в залежності від мови програмування і залежить, в основному, від можливостей повернення ними значень. Синтаксис же деяких мов програмування не залежить від того, повертаються значення підпрограмами чи не повертаються. Наприклад, Maple не звертає на це увагу; у мовах С/C++ вважається, що значення повертається підпрограмою завжди, тільки інколи воно може мати так званий пустий тип (void); в Pascal розрізняють підпрограми-функції, що завжди повертають значення і завжди тільки одне, та підпрограми-процедури, що можуть не повертати значень.
Досить часто результатом роботи підпрограми має бути не одне, а декілька різних значень. В такому випадку використовуються різноманітні спеціалізовані методи повернення комплексу значень. Найпростіший із них - використання параметрів-змінних, найефективніший - утворення блоку результатів і повернення адреси цього блоку, як результату роботи програми. В Maple використовується саме останній метод, тоді як Pascal і C/C++ — використовують і перший, і другий методи.
Розглянемо більш детально реалізацію підпрограм у мовах С/С++. У цих мовах підпрограми існують лише у вигляді функцій. Функції забезпечують зручний спосіб окремого оформлення деяких обчислень з можливістю їх подальшого використання по імені (без турботи про те, як виконана реалізація). РБНФ визначення функції у С/С++:
опис_функції = заголовок тіло_функції;
заголовок = тип_результату ім'я_функції“ (“ [список_параметрів] “)“;
тіло_функції = “ {“ {оголошення_змінної |визначення_змінної |оператор} “}“.
Як видно, визначення функції задає тип значення, що повертається функцією, ім'я функції, типи і число формальних параметрів, а також оголошення (визначення) змінних і оператори, що називаються тілом функції, і визначають дію функції. Допускається використання функцій, які не мають параметрів, однак дужки у цьому випадку все рівно ставляться.
Формат визначення функції представляється наступним чином:
тип_результату ім'я_функції ( [список_параметрів])
{ тіло_функції
}
Повернення значення із функції виконується оператором return наступного формату:
return [вираз].
Тип значення, що повертається функцією, повинен відповідати типу результату в оголошенні цієї функції.
Якщо внаслідок використання конструкції розгалуження виконання функції може завершуватися різними операторами, то операторів повернення результату має бути декілька. Наприклад,
int MAX (int a, int b) //обчислення більшого із двох чисел
{ if (a>b) return a;
else return b;
}
Функція може не повертати жодного значення. Дія таких функцій може полягати, наприклад, у зміні значень деяких змінних, виведенні на друк деяких текстів тощо. Тоді тип результату функції задається ключовим словом void, що вказує на відсутність такого значення. Наприклад,
void PRINT( )
{ cout<<“Нellow!”<< endl;
}
У цьому випадку керування із функції повертається:
по досягненні кінця тіла функції (символа «}»);
при виконанні пустого оператора return.
Активізація функції здійснюється оператором виклику. Його РБНФ:
оператор_виклику_функції = ім'я_ функції “(“ [список_аргументів] “)“.
Оператор виклику може бути як окремим оператором програми, так і операндом виразу. Це залежить від того, чи повертає функція значення у точку виклику: якщо функція не повертає значення у точку виклику, то вона викликається окремим оператором програми, інакше – як операнд виразу.
Розглянемо наступну задачу: для заданих дійсних значень х і у обчислити v = mах(9.5, u), де u = mах(х+у, х*у).
Фрагмент програми розв’язання цієї задачі без використання підпрограм можна представити наступним чином:
// variant 1
int main()
{ float x, y, // вхідні дані
u, v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
if (x+y > x*y) u=x+y; else u=x*y;
if (9.5>u) v=9.5; else v=u;
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
Як видно, в даній програмі фігурують два умовні оператори, кожен з яких призначений для вирішення по суті справи однієї і тієї ж часткової задачі: знаходження більшого з двох заданих дійсних значень і присвоєння деякій змінній отриманого результату. Тому алгоритм розв’язання цієї підзадачі доцільно оголосити у вигляді функції.
Щоб підкреслити схожість двох згаданих вище умовних операторів, запишемо програму трохи інакше:
// variant 2
int main()
{ float x, y, // вхідні дані
n1, n2, m; // допоміжні дані
u, v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
n1=x+y; n2=x*y;
if (n1>n2) m=n1; else m=n2;
u=m;
n1=9.5; n2=u;
if (n1>n2) m=n1; else m=n2;
v=m;
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
У цій програмі умовні оператори в точності збігаються один з одним, тому можна вважати, що один і той же умовний оператор присутній в програмі двічі. Щоб уникнути двократного виписування цього оператора оголосимо його функцією:
void F_MAX();
{ if (n1>n2) m=n1;
else m=n2;
}
Для активації цієї функції в потрібному місці програми достатньо записати там оператор виклику даної функції, що складається лише з її імені, - F_MAX. З використанням такої функції вихідна програма прийме вигляд:
// variant 3
// ======== визначення максимального значення ========
void F_MAX()
{ if (n1>n2) m=n1;
else m=n2;
}
//========== головна функція =============================
int main()
{ float x, y, // вхідні дані
n1, n2, m; // допоміжні дані
u, v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
n1=x+y; n2=x*y;
F_MAX();
u=m;
n1=9.5; n2=u;
F_MAX();
v=m;
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
Враховуючи, що функція F_MAX у будь-якому випадку має повертати результат, перепишемо її наступним чином:
float F_MAX();
{ if (n1>n2) return n1;
else return n2;
}
Тоді вихідна програма прийме наступний вигляд:
// variant 4
// ======== визначення максимального значення ========
float F_MAX ()
{ if (n1>n2) return n1;
else return n2;
}
//========== головна функція =============================
int main()
{ float x, y, // вхідні дані
n1, n2; // допоміжні дані
u, v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
n1=x+y; n2=x*y;
u = F_MAX ();
n1= 9.5; n2=u;
v = F_MAX ();
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
Оскільки в даному випадку фрагмент тексту програми, оголошений функцією, невеликий, то істотного виграшу у розмірі загального тексту програми ми не отримали. Проте, якби цей фрагмент був чималим, то виграш був би очевидний. Але навіть і в даному випадку основна частина програми — її головна функція — стала компактнішою і наочнішою у порівнянні із попередніми варіантами програми.
