- •Процедурно-орієнтоване програмування Організація підпрограм Поняття підпрограми
- •Організація підпрограм
- •Механізм передачі параметрів
- •Принцип локалізації
- •Концепція розподілу пам’яті
- •Способи передачі параметрів
- •Вказівники на функції
- •Розширені можливості використання підпрограм Функції з аргументами за замовчуванням
- •Перевантаження функцій
- •Шаблони функцій
- •Вбудовані функції
- •Рекурсія
Механізм передачі параметрів
Представлена вище функція F_MAX не дуже зручна для використання, оскільки її призначення зафіксоване дуже жорстко. Зокрема, вхідними даними для неї можуть служити лише значення змінних n1 і n2. Тому перед кожним звертанням до функції доводиться заздалегідь присвоювати цим змінним ті значення, з яких потрібно вибирати більше.
Загалом же, підпрограми використовуються для багаторазового виконання стереотипних дій над різними даними. Така можливість досягається за рахунок використання механізму передачі параметрів, суть якого полягає в тому, що при кожному виклику підпрограми їй можуть передаватися параметри – вхідні значення, з якими будуть виконуватися задані в підпрограмі дії. Значення цих параметрів конкретизуються при кожному звертанні до підпрограми, тобто кожен раз вони можуть бути різними. Отже, одна й та сама підпрограма кожен раз може видавати різний результат, незважаючи на те, що дії в ній завжди виконуються ті ж самі.
Слід зазначити, що параметри використовуються не тільки як інструмент для передачі в підпрограму вихідних даних, а й для повернення у зовнішній блок (основну програму, підпрограму) отриманих результатів.
Оскільки параметри можуть бути кожен раз різними, виникає проблема уніфікованого опису дій по обробці цих параметрів у тілі підпрограми. Для вирішення цієї задачі параметри підпрограми позначаються абстрактними іменами в термінах яких і описуються відповідні дії. Ідентифікатори, що виступають в якості цих абстрактних імен, називають формальними параметрами, оскільки вони представляють не якісь конкретні значення, а значення "взагалі".
Формальні параметри, якщо вони є, явно вказуються у заголовку підпрограми і тим самим упорядковуються по їх перерахуванню. З урахуванням вищезазначеного формат заголовка функції у С/С++ можна представити наступним чином:
тип_результату ім'я_функції ([список_ формальних_параметрів]).
Конкретизація дій підпрограми здійснюється за допомогою аргументів або фактичних параметрів - конкретних значень або змінних, які повинна обробити підпрограма, і які в її тілі були позначені формальними параметрами. Вказуються фактичні параметри, якщо вони є, в операторі виклику відповідної підпрограми. У С/С++ оператор виклику функції має наступний формат:
ім'я_ функції[(список_фактичних_параметрів)].
Список фактичних параметрів в операторі виклику підпрограми повинен відповідати списку формальних параметрів у заголовку підпрограми. Ця відповідність повинна бути взаємно-однозначною по кількості, порядку і типу параметрів у результаті їхнього перебору зліва направо. Тобто кількість, порядок слідування і тип формальних і фактичних параметрів повинні відповідати однин одному, тобто збігатися.
Таким чином, фактичні параметри - це конкретні значення або змінні, які повинна обробити підпрограма; формальні параметри - це абстрактні імена, в термінах яких описуються дії по обробці фактичних параметрів в тілі підпрограми.
При активізації підпрограми фактичні параметри, зазначені в команді виклику, заміщують відповідні їм формальні параметри, чим і забезпечується передача даних в підпрограму. Саме механізм заміни (підстановки) і дозволяє виконувати підпрограму з різними аргументами. Схематично це можна представити наступним чином:
Опис формальних параметрів у заголовку підпрограми являє собою їх перелік з вказанням для кожного такого параметра імені типу того значення, яке представляється цим формальним параметром. РБНФ відповідного поняття для мов С/С++:
список_формальних_параметрів = тип ім'я {, тип ім'я}.
Наприклад,
float FUNС(float p1, float p2).
У С/С++ типи формальних параметрів можуть описуватися в заголовку функції або після нього, але до тіла функції:
-
float FUNС (float p1, float p2)
{ …
}
float FUNС (p1, p2)
float p1; float p2;
{ …
}
Оператори тіла функції розглядають список формальних параметрів як своєрідне розширення опису змінних: змінні з цього списку можуть використовуватися у будь-яких виразах усередині функції.
Для прикладу, змінимо функцію F_MAX, представивши її вхідні дані n1 і n2 у вигляді параметрів. Для цього введемо формальні параметри (ідентифікатори, які не використовуються в тілі функції для інших цілей), наприклад p1 та p2, і запишемо тіло функції з їх використанням:
float F_MAX (float p1, float p2)
{ if (p1>p2) return p1;
else return p2;
}
Після цього головну функцію вихідної програми можна записати так:
// variant 5
int main()
{ float x, y, // вхідні дані
u, v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
u= F_MAX (x+y, x*y);
v= F_MAX (9.5, u);
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
Як видно, тепер в основній програмі відпала необхідність вводити у вживання змінні n1 і n2, а перед зверненням до функції - присвоювати цим змінним відповідні значення. Завдяки цьому головна функція основної програми стала ще більш компактною і наочною.
Згідно з правилами мови програмування в тілі підпрограми звернення до інших підпрограм може з'явитися лише тоді, коли ця підпрограма вже описана раніше. Але, наприклад, у випадку непрямої рекурсії, підпрограми, описані на одному рівні вкладеності, можуть мати взаємні виклики одна одної, наприклад,
float Func1 (float x, float y)
{ Func2 (1, 2);
}
float Func2 (int a, int b);
{ Func1 (0.5, 0.99);
}
У цьому випадку при компіляції функції Func1 компілятор не може правильно обробити виклик функції Func2, оскільки ця функція описується нижче по тексту і інформація про неї ще не відома. Якщо поміняти місцями ці дві функції, то відбувається аналогічна ситуація з компіляцією виклику Func1 у функції Func2.
Вирішенням цієї проблеми є механізм випереджувальних описів підпрограм, який підтримується багатьма мовами програмування. Даний механізм передбачає, що спочатку задається попередній опис підпрограми, який містить необхідну для ідентифікації підпрограми інформацію (зазвичай, заголовок підпрограми з вказанням кількості і типу її параметрів); повний же опис підпрограми розміщується в тексті програми далі. У мові Pascal випереджувальний опис підпрограм реалізується за допомогою директиви forwаrd, у C/C++ для цього служать так звані прототипи функцій.
Прототип функції – це її попереднє оголошення шляхом вказання тільки заголовка функції, який містить тип значення, що повертає функція, її ім’я та список типів параметрів. Компілятор ігнорує ідентифікатори параметрів у прототипі функції, тому їх можна не вказувати. Наприклад,
float F_MAX (float, float).
Частину прототипу функції, яка включає її ім’я та типи параметрів називають сигнатурою функції.
Прототипи у С/С++ можна задавати для будь-якої функції користувача, тобто, не обов’язково при взаємних викликах функцій.
Якщо прототип функції не заданий, а зустрівся виклик функції, то з аналізу форми даного виклику будується неявний прототип. При цьому тип результату створеного прототипу - int, а список типів і число параметрів функції формується на підставі типів і числа фактичних параметрів, що використовуються при даному виклику. Функцію, що має прототип, можна визначати далі по тексту в будь-якому місці програми.
Прототип функції як окремий оператор слід записувати на початку програми, тим самим означуючи складові частини самої програми. Для прикладу, змінемо нашу тестову програму, офоримши її з використання прототипу функції F_MAX.
// variant 6
float F_MAX (float, float); // прототип функції
//========== головна функція =============================
int main()
{ float x,y, // вхідні дані
u,v; // результати обчислень
cout <<“Input x, y:”;
cin >> x >> y;
u= F_MAX (x+y, x*y);
v= F_MAX (9.5, u);
cout<<“u=“<<u<<“ v=“<<v<<endl;
system(“pause“);
}
// ======== визначення максимального значення ========
float F_MAX (float p1, float p2)
{ if (p1>p2) return p1;
else return p2;
}
