Указатели на функции как параметры позволяют создавать функции, реализующие тот или иной метод обработки другой функции, которая заранее не определена. Например, можно определить функцию для вычисления определенного интеграла. Подынтегральная функция может быть передана в функцию вычисления интеграла с помощью параметра-указателя. Пример функции для вычисления определенного интеграла с помощью формулы прямоугольников:
double rectangle(double (* pf)(double), double a, double b)
{
int N=20; int i;
double h,s=0.0;
h=(b-a)/N;
for (i=0; i<N; i++)
s+=pf(a+h/2+i*h);
return h*s;
}
Параметры функции rectangle(): pf - указатель на функцию с параметром типа double, возвращающую значение типа double. Это указатель на функцию, вычисляющую значение подынтегральной функции при заданном значении аргумента. Параметры a, b - пределы интегрирования. Число интервалов разбиения отрезка интегрирования фиксировано: N=20. Пусть текст функции под именем rect.c сохранен в каталоге пользователя.
Предложим, что функция rectangle() должна быть использована для вычисления приближенных значений интегралов.
|
|
Программа для решения этой задачи может иметь следующий вид:
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "locale.h"
#include "math.h"
#include "rect.c" /* Включение определения функции rectangle() */
double ratio(double x) /* Подынтегральная функция */
{
double z; /* Вспомогательная переменная */
z=x*x+1;
return x/(z*z);
}
double cos4_2(double v) /* Подынтегральная функция */
{
double w; /* Вспомогательная переменная */
w=cos(v);
return 4*w*w;
}
int _tmain(int argc, _TCHAR* argv[])
{
setlocale(LC_ALL,"Russian");
double a,b,c;
a=-1.0;
b=2.0;
c=rectangle(ratio,a,b);
printf("\n Первый интеграл: %f",c);
printf("\n Второй интеграл: %f", rectangle(cos4_2,0.0,0.5));
_getch();
return 0;
}
Результат выполнения программы:
Первый интеграл: 0.149847
Второй интеграл: 1.841559
Директива #include "rect.c" включает на этапе препроцессорной обработки в программу определение функции rectangle(). Предполагается, как упомянуто выше, что текст этого определения находится в файле rect.c.
Определения функций ratio() и cos4_2(), позволяющих вычислять значения подынтегральных выражений по заданному значению аргумента, ничем не примечательны. Их прототипы соответствуют требованиям спецификации первого параметра функции rectangle():
double имя (double)
В основной программе main() функция rectangle() вызывается дважды с разными значениями параметров. Для разнообразия вызовы выполнены по-разному, но каждый раз первый параметр - это имя конкретной функции, т.е. константный указатель на функцию.
4. Указатель на функцию как возвращаемое функцией значение
При организации меню в тех случаях, когда количество вариантов и соответствующее количество действий определяются не в точке исполнения, а в "промежуточной" функции, удобно возвращать из этой промежуточной функции адрес той конкретной функции, которая должна быть выполнена. Этот адрес можно возвращать в виде значения указателя на функцию.
|
|
Рассмотрим программу, демонстрирующую особенности такой организации меню.
В программе определены три функции: первые две функции f1 () и f2() с прототипом вида
int f (void);
(пустой список параметров, возвращается значение типа int) и третья функция menu() с прототипом
int (*menu(void)) (void);
которая возвращает значение указателя на функции с пустыми списками параметров, возвращающие значения типа int.
При выполнении функции menu() пользователю дается возможность выбора из двух пунктов меню. Пунктам меню соответствуют определенные выше функции f1 () и f2(), указатель на одну из которых является возвращаемым значением. При неверном выборе номера пункта возвращаемое значение становится равным NULL.
В основной программе определен указатель r, который может принимать значения адресов функций fl() иf2(). В бесконечном цикле выполняются обращения к функции menu(),и если результат равен NULL, то программа печатает "The End" и завершает выполнение. В противном случае вызов
t=(*r) ();
обеспечивает исполнение той из функций f1 () или f2(), адрес которой является значением указателя r. Текст программы:
#include "stdafx.h"
#include "stdio.h"
#include "conio.h"
#include "locale.h"
int f1(void)
{
printf(" The first actions: ");
return 1;
}
int f2(void)
{
printf(" The second actions: ");
return 2;
}
int (* menu(void))(void)
{
int choice; /* Номер пункта меню */
/* Массив указателей на функции: */
int (* menu_items[])() = {f1, f2};
printf("\n Pick the menu item (1 or 2): ");
scanf("%d",&choice);
if (choice < 3 && choice > 0)
return menu_items[choice - 1];
else
return NULL;
}
int _tmain(int argc, _TCHAR* argv[])
{
int (*r) (void); /* Указатель на функции.*/
int t;
while (1)
{/* Обращение к меню: */
r=menu();
if (r == NULL)
{
printf("\nThe End!");
_getch();
return 0;
}
/* Вызов выбранной функции */
t=(*r) ();
printf("\tt= %d",t);
}
_getch();
return 0;
}
Результаты выполнения программы:
Pick the menu item (1 or 2): 2
The second actions: t=2
Pick the menu item (1 or 2): 1
The first actions: t=l
Pick the menu item (1 or 2): 24
The End!
В функции menu() определен массив menu_items[ ] указателей на функции. В качестве инициализирующих значений в списке использованы имена функций f1() и f2():
int(* menu_items[ ]) () = {fl, f2};
Такое определение массива указателей на функции по меньшей мере не очень наглядно. Упростить такие определения можно с помощью вспомогательных обозначений (имен), вводимых спецификатором typedef.Например, то же самое определение массива указателей можно ввести так:
typedef int (*Menu_action) (void);
Menu_action menu_items [ ] = {fl, f2};
Здесь typedef вводит обозначение Menu_action для типа "указатель на функции с пустым списком параметров, возвращающие значения типа int".