- •Лабораторна робота №8 Тема: рекурсивні функції, вказівники на функції в алгоритмічній мові с
- •1. Мета роботи
- •2. Теоретичні відомості
- •2.1. Рекурсивні функції
- •2.2. Приклади програм з використанням рекурсії
- •2.3. Вказівники на функції. Масиви вказівників на функції
- •2.4. Вказівники типу near і far
- •3. Контрольні запитання
- •4. Лабораторне завдання
- •6. Список літератури
- •Завдання до лабораторної роботи
2.2. Приклади програм з використанням рекурсії
/* Приклад: не оголошуючи масиву, ввести групу даних і вивести їх у зворотному порядку. */
#include <iostream.h>
//Рекурсивна функція
void rec()
{ int i;
cin >> i;
if(i!=0) rec ();
else cout << "Вивід чисел: " << endl;
cout << i;
} •
void main(}
{
cout << "Введіть числа. Ознака закінчення вводу - 0 : " << endl;
rec ();
}
/* Результат виконання програми:
Введіть числа . Ознака закінчення вводу -0 : 3 2 4 1 5 6 7 9 8 0
Вивід чисел: 0 8 9 7 6 5 1 4 2 3 */
/* Приклад: не оголошуючи масиву довгих цілих чисел, ввести групу даних, вивести їх у зворотному порядку разом з їхніми порядковими номерами. */
#include <iostream.h>
void rec (int num) /* Рекурсивна функція */
{ int i;
cin >> i;
if (i!=0) rec(num + l) ;
else cout << "Вивід чисел:" << endl;
cout << ",Число, "<< i << ", порядковий .номер " << num << "\n";
}
void main ()
{cout << “Введіть числа. Ознака закінчення вводу - 0: " << endl;
гес (0) ;
}
/* Результат виконання програми:
Введіть числа. Ознака закінчення вводу - 0: 1 2 3 4 5 6 7 8 9 0
Вивід чисел:
Число 0, порядковий номер 10
Число 9, порядковий номер 9
Число 8, порядковий номер 8
Число 7, порядковий номер 7
Число 6, порядковий номер 6
Число 5, порядковий номер 5
Число 4, порядковий номер 4
Число 3, порядковий номер 3
Число 2, порядковий номер 2
Число 1, порядковий номер 1 */
/* Приклад: використовуючи рекурсивний виклик функції main(), обчислити суму елементів масиву цілого типу. Масив не оголошувати. Останній -елемент масиву - 0. У файлу з вихідним модулем повинне бути розширення .с */
#include<stdio.h>
#include <iostream.h>
int main()
{ int i;
cin >> i ;
if(i) { i+=main (); cout << " s= " << i; }
return i; ' -
}
/* Другий варіант: сума обчислюється у функції, результат виводиться в main () */
int fun()
{
int i;
scanf (“%d”,&i);
if (i) i+ = fun ();
return i;
}
void main ()
{ printf (“%d”, fun());
}
2.3. Вказівники на функції. Масиви вказівників на функції
У мові С сама функція не може бути значенням змінної, але можна визначити вказівник на функцію. З ним уже можна оперувати, як зі змінною: передавати його іншим функціям, поміщати в масиви й т.д. Оголошення виду:
int ( *fl ) ( );
говорить про те, що fl - вказівник на функцію, що повертає ціле значення. Перша пара дужок необхідна, без них
int *f 1 ( ); /* це не вказівник на функцію */
означало б, що fl -функція, що повертає вказівник на ціле значення. Після оголошення вказівник а на функцію в програмі можна використовувати оператори виду:
y = ( *fl ) ( . . .); або y = f1(...);
Вказівник на функцію - такий тип змінної, котрій можна присвоювати адреси точки входу у функцію, тобто адресу першої виконуваної команди. Ця змінна надалі може використовуватися для виклику функції замість її імені. Визначення вказівника на функцію має наступний загальний вид:
тип_результату (* ім'я вказівника на функцію) (список типів параметрів) ;
Наприклад:
double (*fd) ( int, int );
/* fd - покажчик на функцію, що повертає результат типу double і приймає два параметри типу int. */
int ( *find ) ( int, float * ) ;
/ find - вказівник на функцію. Функція повертає результат типу int і приймає два параметри: число типу int і вказівник на число типу float.*/
char* ( * comp ) ( const char *sl, const char *s2 ) ;
/* comp - вказівник на функцію, що повертає результат вказівник на char і приймаючу як параметри два вказівники на char.*/
При визначенні вказівника на функцію дозволяється одночасно із вказівкою типу параметрів задавати і їхні імена.
Нехай є прототипи функцій піднесення числа типу int у третю, п'яту і сьому степінь і які повертають результат типу int (звичайно, все це легко реалізувати й в одній функції):
int f3 ( int );
int f5 ( int ) ;
int f7 ( int );
і є вказівник на функцію наведеного вище виду:
int ( *fst ) ( int );
Тоді оператор fst = f3; присвоїть вказівнику fst адресу входу у функцію f3, fst = f5; - адресу входу у функцію f5 і, відповідно, fst = f7; - адресу входу у функцію f7. Після цього викликати кожну з функцій (f3, f5, f7) можна будь-яким оператором, записаним нижче (наприклад, f3):
f3 ( a ); // звертання до функції, використовуючи її ім'я.
(*fst ) ( а ); // виклик функції через вказівник.
fst( a ); // виклик функції також через вказівник.
Останній варіант також правильний, тому що f3, fst - це адреси входу у функцію. Однак виклик ( *fst ) ( а ) явно показує, що використовується вказівник на функцію, а не викликається функція з ім'ям fst, якщо бачити тільки один оператор fst ( a );.
У ряді задач, що використовують математичні методи, вказівник на функцію необхідно передавати як параметр у функцію, що реалізує відповідний метод. Наприклад, для обчислення значення інтеграла треба знати ім'я функції, у якій обчислюється значення підінтегрального виразу; для пошуку екстремума деякої цільової функції треба знати ім'я цієї функції і т.п.
Для ілюстрації розглянемо простий приклад. Нехай потрібно обчислити наступні вирази:
y = 4 * 4 * 4 + 7 * 7 * 7 і . y = 3*3*3*3*3 + 9*9*9*9*9.
За умовою задачі, сума елементів обчислюється в окремій функції, з якої повинна викликатися і функція піднесення числа до степеня.
#include <stdio.h>
#include <conio.h>
int f 3 ( int x )
// Піднесення числа в третю степінь
{ return x * x * x;
}
int f5( int x )
// Піднесення числа в п'яту степінь
int х2 = х * х;
return х2 * х2 * х;
}•
void main( )
{
int ( *fst ) ( int );
// Вказівник на функцію
int sum (int , int, int ( *fst ) ( int ) );
// Прототип функції sum
fst = f3;
// fst адреса входу в f3
printf(" 4 ^ 3 + 7 ^ 3 = %d \n ", sum( 4, 7, fst ) );
fst = f5;
// fst = адреса входу у f5
printf(" 3 ^ 5 + 9 ^ 5 = %d\n ,", sum( 3, 9, fst ) ) ;
}
int sum( int m, int n, int ( *fp ) ( int ) )
// Реалізація функції sum
{ int yl, y2;
yl = ( *fp ) ( m );
// Виклик або f3, або f5
y2 = ( *fp ) ( n );
// Виклик або f3, або f5
return y+y2;
}
/*Є й інші варіанти реалізації функції sum. */
int sum( int m, int n, int ( *fst ) ( int ) )
{ return fst( m ) + fst( n ) ;
}
Вказівники на функцію широко застосовуються в програмуванні:
- багато бібліотечних функцій як аргумент одержують вказівник на функцію;
- використання вказівників на функцію як аргументів дозволяє розробляти універсальні функції, наприклад функції чисельного рішення рівнянь, чисельного інтегрування й диференціювання;
- масиви вказівників на функції використовуються для організації меню.
Вказівники на функції можуть бути компонентами структур.
Як і звичайні змінні, вказівники на функції можуть об’єднуватися в масиви. Наприклад, визначити й проініціалізувати масив вказівників на функції можна в такий спосіб:
/* Опис прототипів функцій, точки входів яких будуть елементами масиву вказівників на функції. */
float funcl( float );
float func2 ( float ) ;
float func3( float );
float func4( float ) ;
float func5( float ) ;
// Масив вказівників на функції
float ( *fparray [5] ) (float ) = { funcl, func2, func3, func4, func5 } ;
Доступ до елементів масиву fparray виконується, як до звичайних елементів масиву.
Наприклад:
float х = 1;
cout << fparray[0] ( х) ;
// або cout << (*fparray[0] ) ( х) ;
При організації меню можна використовувати вказівники на масиви функцій. Кожному пункту меню ставиться у відповідність його реалізуюча функція, вказівник на яку поміщається в масив. Після того як користувач зробив вибір, за відповідним індексом вибирається функція, що реалізує цей пункт меню.
Одним з прикладів, де можуть використовуватись вказівники на функції, є обчислення коренів трансцендентних рівнянь. Розглянемо цей випадок на прикладі найпростішого методу половинного ділення.
Нехай відомо, що на відрізку
[a,b]
рівняння
має один корінь, тобто в точках a
і b
значення функції мають різні знаки.
Якщо | а - b |<e (де е - точність обчислень),
то
[а,
b] можна вважати коренем, наприклад х =
а (необхідна точність досягнута). Інакше
за наближене значення кореня візьмемо
середину відрізка
.
Якщо
,
то корінь знайдений. Інакше вибираємо
той з відрізків ([a
,
],
[
,
b]), на кінцях якого значення функції
мають різні знаки, і застосовуємо до
нього ті ж міркування.
// Функція вимагає під’єднання бібліотечного модуля <math.h>
int hdiv(double ( *f ) ( double),double a ,double b, double *x, double e)
{
double y; int n=0; /* n - кількість ітерацій */
while ( fabs ( a-b ) > e )
{
*x - ( a + b ) / 2; n++;
y = f ( *x );
if( y == 0 ) break;
if( f(a) *y < 0) b = *x;
else a = *x;
}
*x = ( a + b ) / 2; return n;
}
Розглянемо наступний приклад з використанням вказівників на функції.
/* Приклад. Визначити масив вказівників на функції. Вводити цифру, яка визначатиме, яку функцію необхідно виконати: 0 – знайти мінімальне число, 1 – знайти максимальне число, 2- обрахувати суму, 3- обрахувати різницю, 4 – добуток, 5 – частку, 9 – завершити роботу. Виконати відповідну функцію, використовуючи вказівник на неї */
#include <stdio.h>
#include <conio.h>
void main (void)
{
int min (int, int); /* - minimum is found */
int max (int, int); /* - maximum is found */
int plus(int, int); /* - sum is found */
int minus (int, int); /* difference is found */
int mul (int, int); /* - product is found */
int div (int, int); /* - quotient is found */
// array of pointers
int (*y[10]) (int, int) = { min, max, plus, minus, mul, div};
// names of operations
char *str[ ] = {“min”, “max”, “plus”, “minus”, “mul”, “div”};
int m, n; char i;
while (l)
{
puts (“Input the operation: \n”);
puts (“0 – to find minimum \n”);
puts (“1 – to find maximum \n”);
puts (“2 – to find sum \n”);
puts (“3 – to find difference \n”);
puts (“4 – to find product \n”);
puts (“5 – to find quotient \n”);
puts (“9 – exit \n”);
scanf(“%d”, &i);
if ( i == 9 ) return;
if ( i < 0 || i > 5 )
{
puts (“Incorrect operation code \n”); continue;
}
puts (“Input the operands (m, n): \n”);
scanf(“%d%d”, &m, &n);
// Call the function by the pointer – element of array
printf (“%s= %d \n”, str[i],(*y[i]) (m, n) );
// or in the following way
printf (“%s= %d \n”, str[i], y[i], (m, n) );
}
}
// description of functions
int min (int m, int n) {return ( m < n ) ? m: n; }
int max (int m, int n) {return ( m > n ) ? m: n; }
int plus (int m, int n) {return m + n ; }
int minus (int m, int n) {return m - n; }
int mul (int m, int n) {return m * n; }
int div (int m, int n)
{
if ( n) return m / n;
return 32767; /* n value is equal to 0 */
}
