В языке Си одна функция может возвращать указатель на другую функцию. Это может быть актуально, если имеется ограниченное количество вариантов - выполняемых функций, и надо выбрать одну из них. Но при этом набор вариантов и выбор из них определяется в промежуточной функции.
Например, нам надо выбрать и выполнить одну из трех арифметических операций
#include <stdio.h> int sum(int x, int y) { return x + y; } int subtract(int x, int y) { return x - y; } int multiply(int x, int y) { return x * y; } // int choice - выбранный пункт int (*select(int choice))(int, int) { // возвращаем нужную функцию switch (choice) { case 2: return subtract; case 3: return multiply; default: return sum; } } int main(void) { int (*operation)(int, int); // указатель на выбранную функцию operation = select(1); // получаем указатель на функцию sum - сложение int result = operation(6, 4); // выполняем функцию printf("result: %d \n", result); // result: 10 return 0; }
В данной программе мы предполагаем, что пользователь должен выбрать для выполнения одну из трех функций: sum, subtract, multiply, каждая из которых представляет определенное действие.
Все выбираемые функции имеют один и тот же прототип вида:
int operation(int, int);
То есть возвращают значение типа int
и принимают два параметра типа int
.
Сам выбор происходит в функции select()
. Она возвращает указатель на функцию - по сути выбранную функцию. Посмотрим на ее заголовок:
int (*select(int choice))(int, int)
Если рассматривать по компонентам, то заголовок выглядит следующим образом:
тип_указателя_на_функцию (*select(параметры_функции))(параметры_указателя_на_функцию)
То есть сначала идет возвращаемый тип функции, на который возвращается указатель (в данном случае int
)
Далее в скобках идет название самой функции и ее параметры - (*select(int choice))
. То есть функция select
, которая возвращает указатель на функцию,
принимает один параметр - choice - условный номер арифметической функции.
Зтаем идут типы параметров функции, указатель на которую возвращается: (int, int)
То есть в итоге функция select()
имеет один параметр choice
, который представляет тип int
, и возвращает указатель на функцию,
которая имеет прототип int action(int, int)
.
В самой функции select()
в зависимости от значения параметра choice
возвращаем определенную функцию:
switch (choice) { case 2: return subtract; case 3: return multiply; default: return sum; }
В функции main
сначала определяем указатель, который соответствует прототипу арифметических функций sum, subtract, multiply:
int (*operation)(int, int);
Далее в этот указатель получаем результат из функции select
, передав в нее номер функции:
operation = select(1);
По номеру 1 функция select возвращает функцию sum
. Соответственно переменная-указатель operation
будет хранить адрес функции sum.
Далее вызываем функцию, на которую указавает указатель, передвая ей два числа для ее параметров:
int result = operation(6, 4); // выполняем функцию
Подобным образом можно получить и другие функции:
int main(void) { int (*operation)(int, int) = select(2); // получаем указатель на функцию subtract printf("result: %d \n", operation(6, 4)); // result: 2 operation = select(3); // получаем указатель на функцию multiply printf("result: %d \n", operation(6, 4)); // result: 24 return 0; }
Поскольку определение функции, которая возвращает указатель на функцию, не самое читабельное, то, чтобы упростить код и повысить читабельность, можно определить псевдоним типа функции:
#include <stdio.h> typedef int (binary_op)(int, int); int sum(int x, int y) { return x + y;} int subtract(int x, int y) { return x - y;} int multiply(int x, int y) { return x * y;} int operation(binary_op op, int a, int b){ return op(a, b);} // int choice - выбранный пункт binary_op* select(int choice) { // возвращаем нужную функцию switch (choice) { case 2: return &subtract; case 3: return &multiply; default: return ∑ } } int main(void) { binary_op* operation = select(1); // получаем указатель на функцию sum - сложение int result = operation(6, 4); // выполняем функцию printf("result: %d \n", result); // result: 10 return 0; }
В данном случае сначала определяем псевдоним binary_op для функции, которая принимает два параметра типа int
и возвращает значение типа int
:
typedef int (binary_op)(int, int);
Затем мы можем использовать этот тип (или указатель этого типа) в качестве типа результата функции:
binary_op* select(int choice) { .................. }
В функции main получаем указатель на возвращенную функцию в пепеменную operation и вызываем ее:
binary_op* operation = select(1); int result = operation(6, 4);