Как передать функцию в функцию c
Перейти к содержимому

Как передать функцию в функцию c

  • автор:

Как передать функцию в функцию c

В языке Си указатель на функцию может передаваться в другую функцию в качестве параметра. Например:

#include int sum(int x, int y) < return x+y; >int subtract(int x, int y) < return x-y; >int operation(int (*op)(int, int), int a, int b) < return op(a, b); >int main(void)

Здесь в функции operation первый параметр — указатель int (*op)(int, int) представляет функцию, которая возвращает значение типа int и принимает два параметра типа int . Результатом функции является вызов той функции, на которую указывает указатель.

Определению указателя соответствуют две функции: sum и subtract, поэтому их адрес можно передать в вызов функции operation: operation(sum, a, b); .

Другой пример — функция, которая может принимать в качестве параметра некоторое условие:

#include int isEven(int x) < return x%2==0; >int isPositive(int x) < return x>0; > void action(int (*condition)(int), int numbers[], int n) < for(int i=0; i> > int main(void) < int nums[] = ; int n = sizeof(nums)/sizeof(nums[0]); printf("Even numbers: "); action(isEven, nums, n); printf("\nPositive numbers: "); action(isPositive, nums, n); return 0; >

Первый параметр функции action — указатель int (*condition)(int) представляет функцию, которая принимает целое число и в зависимости от того, соответствует оно условию или нет, возвращает 1 (если соответствует) или 0. На момент определения функции action точное условие может быть неизвестно.

В текущей программе условия представлены двумя функциями. Функция isEven() возвращает 1, если число четное, и 0, если число нечетное. А функция isPositive() возвращает 1, если число положительное, и 0, если отрицательное.

При вызове функции action() в нее можно передать нужное условие: action(isEven, nums, n); . В итоге программа выведет на экран числа из массива nums, которые соответствуют переданному условию:

Even numbers: -4 -2 0 2 4 Positive numbers: 1 2 3 4 5

Но применение указателей на функцию в качестве параметров ухудшает читабельность определения функции. В этом случае можно определить псевдоним для типа функции:

#include 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 operation(binary_op op, int a, int b) < return op(a, b); >int main(void) < printf("result = %d \n", operation(sum, 10, 5)); // result = 15 printf("result = %d \n", operation(subtract, 10, 5)); // result = 5 return 0; >

В данном случае для функций, которые имеют два параметра типа int и возращают значение типа int, определен псевдоним binary_op .

typedef int (binary_op)(int, int);

И далее мы можем использовать этот псевдоним как тип для определения параметров:

int operation(binary_op op, int a, int b)

Как передать функцию в функцию c

В языке С++ данные в подпрограмму можно передавать тремя способами: по значению, по адресу и по ссылке. В языке С допустимы только два способа: по значению и по адресу.

Передача данных по значению

Этот способ передачи данных в подпрограмму является основным и действует по-умолчанию. Фактический параметр вычисляется в вызывающей функции и его значение передаётся на место формального параметра в вызываемой функции. На этом связь между фактическим и формальным параметрами прекращается.

В качестве фактического параметра можно использовать константу, переменную или более сложное выражение. Передача данных по значению пригодна только для простых данных, которые являются входными параметрами. Если параметр является выходным данным или массивом, то передача его в функцию по значению не приемлема.

Пример 1 . Вычислить сумму ряда с заданной точностью ε =10 -5 :

Для вычисления суммы ряда используем функцию. В неё передадим по значению x и eps . Результат вернём через имя функции оператором return .

Возможный вариант реализации программы:

using namespace std;

double fsum(double x, double eps);

double x, s, eps = 1.0e-5;

double fsum(double x, double eps)

double s = x, p = x, i, t = x * x;

for(i = 3; fabs(p) > eps; i += 2)

p = -p * t / (i * (i — 1));

Не сложно убедиться, что всё нормально работает. Но что делать, когда выходных параметров два или более? Через имя функции можно вернуть только один объект, остальные придётся возвращать через список. Позволит ли этот способ (передача по значению) вернуть через список параметров изменённые значения? Нет, не позволит. Давайте проверим это на несложном примере.

Пример 2 . Даны два числа, хранящиеся в переменных a и b . Используя подпрограмму, выполнить обмен содержимого ячеек этих переменных.

Данные передадим по значению. Они будут в этой задаче и входными, и выходными данными. Для контроля изменения содержимого ячеек памяти будем выводить на экран монитора промежуточные данные.

Возможный вариант реализации программы:

using namespace std;

void Obmen(double x, double y);

double a = 2.5, b = 3.1;

void Obmen(double x, double y)

Результаты выполнения программы:

Do Obmen: a=2.5 b=3.1

Function Obmen start:

Function Obmen end:

Posle Obmen: a=2.5 b=3.1

Вывод на экран значений переменных показывает, что данные в функцию переданы правильно, перестановка в функции произведена, но это ни как не отразилось на значениях исходных переменных a и b после выхода из функции Obmen() .

Этот пример наглядно показывает, что через параметры, передаваемые по значению, нельзя вернуть результаты работы функции .

Передача данных по адресу

По адресу в функцию всегда передаются массивы (рассмотрим это в следующих темах). Для массива это вообще единственный способ передачи данных в языках С/С++. Так же по адресу можно передать те простые объекты, которые являются выходными данными (или входными и выходными одновременно).

В случае передачи данных по адресу фактический параметр может быть только переменной (константа или выражение не имеют адреса!).

Вернёмся к предыдущему примеру.

Пример 3 . Даны два числа, хранящиеся в переменных a и b . Используя подпрограмму, выполнить обмен содержимого ячеек этих переменных.

Данные передадим по адресу. Они будут в этой задаче и входными, и выходными данными. Для контроля изменения содержимого ячеек памяти будем выводить на экран монитора промежуточные данные.

Возможный вариант реализации программы:

using namespace std;

void Obmen(double *x, double *y);

double a = 2.5, b = 3.1;

void Obmen(double *x, double *y)

Результаты выполнения программы:

Do Obmen: a=2.5 b=3.1

Function Obmen start:

Function Obmen end:

Posle Obmen: a=3.1 b=2.5

Печать на экране монитора наглядно показывает, что обмен произведён, и исходные переменные теперь имеют новые значения, т.е. передача данных по адресу действительно позволяет вернуть в вызывающую функцию результат работы вызываемой подпрограммы.

Как это работает? Рассмотрим данный вопрос подробнее, используя пример с обменом данных. Для наглядности приведём рисунок:

В вызывающей функции (в нашем случае — в main() ) вычисляются адреса объектов, передаваемых по адресу ( у нас — адреса переменных a и b . Пусть это будут числа 1000 и 1008 ), и затем эти адреса копируются в ячейки памяти — указатели, память под которые выделена в функции Obmen() (это x и y ). Зная адрес переменной, например, адрес переменной a , который теперь хранится в указателе x , можно, пользуясь операцией разыменование, не только прочитать, но и изменить значение исходной переменной.

Ни какой реальной передачи данных (в смысле копирования) из подпрограммы Obmen() назад в main() не делается. Мы на самом деле через указатели работаем с исходными объектами! Поэтому после выхода из функции Obmen() имеем изменённые переменные a и b (если быть точнее, переменные изменятся ещё до выхода из функции,то есть в момент перестановки в самой функции Obmen() ).

Передача данных по ссылке

Это ещё один из способов вернуть результат работы функции через список параметров. Напомним, что применяется только для С++. В языке С такого варианта нет.

При передаче данных по ссылке в функцию, куда передаются данные, создаются синонимы исходных объектов. Поэтому работа в подпрограмме ведётся именно с исходными объектами. Если в подпрограмме ссылочная переменная изменит значение, то это сразу отразится на исходной переменной.

В вызывающей функции параметр, передаваемый по ссылке, может быть только простой переменной любого известного типа.

Вернёмся снова к примеру обмена, только данные передадим по ссылке.

Пример 4 . Даны два числа, хранящиеся в переменных a и b . Используя подпрограмму, выполнить обмен содержимого ячеек этих переменных.

Возможный вариант реализации программы:

using namespace std;

double a = 2.5, b = 3.1;

Как передать функцию в функцию c

Указатель на функцию фактически представляет некоторый тип, и функция также может иметь параметр, который представляет тип указателя на функцию. Таким образом, мы можем через параметр на функцию передавать в одну функцию другую. То есть функция может быть аргументом другой функции.

#include int add(int, int); int subtract(int, int); int operation(int(*)(int, int), int, int); // первый параметр — указатель на функцию int main() < int a; int b; int result = operation(add, a, b); std::cout int add(int x, int y) < return x + y; >int subtract(int x, int y) < return x - y; >int operation(int(*op)(int, int), int a, int b)

В данном случае первый параметр функции operation — int (*op)(int, int) — представляет указатель на функцию, которая возвращает значение типа int и принимает два параметра типа int . Результатом функции является вызов той функции, на которую указывает указатель.

Определению указателя соответствуют две функции: add и subtract, поэтому их адрес можно передать в вызов функции operation: operation(add, a, b); .

Результат работы программы:

result: 16 result: 4

Функция, передаваемая другой функции в качестве аргумента, называется функцией обратного вызова или коллбек (callback). А функция, которая принимает другую функцию в качестве аргумента, является функцией высшего порядка. Таким образом, в примере выше функция operation представляет функцию высокого порядка, а функции add и subtract — функции обратного вызова.

Рассмотрим другой пример — определим функцию, которая может принимать в качестве параметра некоторое условие и вычислять все элементы массива, которые соответствуют этому условию:

#include // функции, которые представляют условия bool isEven(int); // если число четное bool isPositive(int); // если число положительное // Функция для определения элементов массива, которые соответствуют некоторому условию // функция принимает условие — bool(*)(int) // массив — int[] // размер массива — unsigned void action(bool(*)(int), int[], unsigned); int main() < int numbers[]< -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 >; const unsigned n < std::size(numbers)>; // находим длину массива std::cout bool isEven(int x) < return x % 2 == 0; >bool isPositive(int x) < return x >0; > void action(bool(*condition)(int), int numbers[], unsigned n) < // перебираем массив for (unsigned i<>; i < n; i++) < // если число (numbers[i] соответствует условию condition if (condition(numbers[i])) < std::cout > std::cout

Функция action в качестве первого параметра принимает некоторую функцию, которая задает условие, которому должны соответствовать элементы массива. Это условие представляет указатель bool (*condition)(int) . То есть это некоторая функцию, которая принимает целое число и в зависимости от того, соответствует оно условию или нет, возвращает значение типа bool ( true , если число из массива соответствует условию, и false , если не соответствует). На момент определения функции action точное условие может быть неизвестно.

В текущей программе условия представлены двумя функциями. Функция isEven() возвращает true, если число четное, и false, если число нечетное. А функция isPositive() возвращает true, если число положительное, и false, если отрицательное.

Второй параметр функции action — массив чисел int, для которых вызываем условие. А третий параметр — размер массива. Если число соотвествует условию, то выводим это число на консоль

void action(bool(*condition)(int), int numbers[], unsigned n) < // перебираем массив for (unsigned i<>; i < n; i++) < // если число (numbers[i] соответствует условию condition if (condition(numbers[i]))

При вызове функции action() в нее можно передать нужное условие:

action(isEven, nums, n); action(isPositive, numbers, n);

В итоге программа выведет на экран числа из массива nums, которые соответствуют переданному условию:

Even numbers: -4 -2 0 2 4 Positive numbers: 1 2 3 4 5

Как в С++ передать функцию в качестве параметра?

@avp, А почему сначала функция вызывается и выводится её сообщение, а потом уже остальное, хотя функция не на первом месте стоит? То есть почему сначала выводится это «xx-args: a+a, 3», а потом это «(*x)()=xx() = 6». Пытался посмотреть как это реализовано в стандартной библиотеке, но что-то я не нашел iostream в MVC 2010.

22 фев 2013 в 19:56

@mzarb, если заменить cout на printf() , то будет понятно. int xx (char *s, int a) < printf ("xx-args: %s, %d\n", s, a); return a+a; >и в main() x = xx; printf («(*x)()=xx() = %d\n», x(«a+a»,3)); в main в printf() передается результат вызова x(). При вызове x() отрабатывает printf() в ней, а она возвращает число 6. Поэтому сначала напечатается printf («xx-args: %s, %d\n», «a+a», 3); а уже потом printf («(*x)()=xx() = %d\n», 6); С cout аналогично, сначала вычисляются операнды (или как они там называются), а потом их значения выводятся в поток.

22 фев 2013 в 20:21

typedef int (*func)(int a, int b); int call_func(int a, int b) < return a + b; >void function(int a, int b, func f) < int sum = f(a, b); std::cout  
)" data-controller="se-share-sheet" data-se-share-sheet-title="Поделиться ссылкой на ответ" data-se-share-sheet-subtitle="" data-se-share-sheet-post-type="answer" data-se-share-sheet-social="facebook twitter " data-se-share-sheet-location="2" data-se-share-sheet-license-url="https%3a%2f%2fcreativecommons.org%2flicenses%2fby-sa%2f3.0%2f" data-se-share-sheet-license-name="CC BY-SA 3.0" data-s-popover-placement="bottom-start">Поделиться
)" title="">Улучшить ответ
)">изменён 18 июл 2012 в 20:01
ответ дан 18 июл 2012 в 18:36
Добавить комментарий |
7

С помощью boost::function так. Или std::function в С++11 так. Способ с голыми указателями также применим, но у функциональных объектов возможностей гораздо больше.

#include #include typedef boost::function MyFunc; void bar(MyFunc const& func) < func("blabla"); >int f1(std::string const& str) < std::cout int f2(std::string const& str) < std::cout int main()

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *