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

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

Как передать функцию в функцию 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

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

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

#include 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) < return op(a, b); >int main(void)

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

Определению указателя соответствуют две функции: add и subtract, поэтому их адрес можно передать в вызов функции operation: operation(add, 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 int add(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 add; >> int main(void) < int (*operation)(int, int); // указатель на выбранную функцию operation = select(1); // получаем указатель на функцию add - сложение int result = operation(6, 4); // выполняем функцию printf("result: %d \n", result); // result: 10 return 0; >

В данной программе мы предполагаем, что пользователь должен выбрать для выполнения одну из трех функций: add, subtract, multiply, каждая из которых представляет определенное действие.

Все выбираемые функции имеют один и тот же прототип вида:

int action(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)

В функции main сначала определяем указатель, который соответствует прототипу арифметических функций add, subtract, multiply:

int (*operation)(int, int);

Далее в этот указатель получаем результат из функции select , передав в нее номер функции:

operation = select(1);

По номеру 1 функция select возвращает функцию add . Соответственно переменная-указатель operation будет хранить адрес функции add.

Далее вызываем функцию, на которую указавает указатель, передвая ей два числа для ее параметров:

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; >

Для хранения всех действий в функции select определен массив указателей на функции actions:

int (*actions[])() = ;

С помощью введенного с клавиатуры числа определяем номер нужного действия, которое надо выполнить. Если номер меньше 1 или больше 3, то возвращается константа NULL.

В главной функции main() в бесконечном цикле вызываем функцию select, получая в качестве результата указатель на функцию:

action = select();

И если указатель не равен NULL, то после этого мы сможем вызвать функцию по указателю и получить ее результат:

actionNumber = action();

Консольный вывод работы программы:

Select action (1, 2, 3): 1 Action 1 selected action 1 Select action (1, 2, 3): 3 Action 3 selected action 3 Select action (1, 2, 3): 4 End

Arduino.ru

Код примерно такой. Мне это нужно для того, что бы я мог кроме ppp() подставлять какую либо иную функцию.

void loop() < ooo(); >int Millis(int16_t stoping) < static uint32_t Time = 0; int result; static int16_t Delay = stoping; if ((millis() - Time) >Delay) < ppp(); Time = millis(); return result; >> void ppp() < digitalWrite(ledFP[1],!digitalRead(ledFP[1])); >void ooo()

  • Войдите на сайт для отправки комментариев

Пнд, 23/09/2019 — 17:46
Зарегистрирован: 26.05.2017

google function pointer as parameter

  • Войдите на сайт для отправки комментариев

Пнд, 23/09/2019 — 20:28
Зарегистрирован: 03.07.2016

Та блин я давно уже тему наваял , а вы б**я только проснулись #4 С 18.04.2015 видно спите.

  • Войдите на сайт для отправки комментариев

Пнд, 23/09/2019 — 21:24

ЕвгенийП аватар

Зарегистрирован: 25.05.2015

Опишите в списке аргументов указатель на функцию и просто передавайте её имя.

  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 09:46
Зарегистрирован: 18.04.2015
ЕвгенийП пишет:

Опишите в списке аргументов указатель на функцию и просто передавайте её имя.

Спасибо, я так и предполагал. Сейчас изучаю этот вопрос.

  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 09:49
Зарегистрирован: 18.04.2015
qwone пишет:

Та блин я давно уже тему наваял , а вы б**я только проснулись #4 С 18.04.2015 видно спите.

Да. Но только теперь начал плотно изучать. Можете, пожалуйста, ткнуть носом?

  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 10:00
Зарегистрирован: 18.04.2015
qwone пишет:

Нашел такой Ваш пример:

void func() < // это просто функция Serial.println("qwqewqewq"); >void (*pFun)() = &func;// это указатель на функцию void (*&pA)() = pFun;// это ссылка на указатель функции void setup() < Serial.begin(9600); pA(); >void loop()

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

Извините, если написал вопрос на грани бреда, но я еще учусь. )))

  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 10:14

DetSimen аватар

Зарегистрирован: 25.01.2017

using pvfCallback = void (*)(void); // это тип. указатель на функцию без параметров возвр-ую void // pvf - необязательная мнемоника типа. pointer to void func void MyFunc(pvfCallback MyCallback) < // функция, принимающая параметром указатель на другую функцию if (MyCallback != NULL) MyCallback(); >void Callback(void) < // функция, которая будет вызвана из MyFunc >void loop(void)< MyFunc(Callback); // вызываем MyFunc, передавая указатель на Callback. >
  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 10:16

DetSimen аватар

Зарегистрирован: 25.01.2017

тебе не на форумах надо знания получать, а прочесть для начала Библию Си от Кернигана. Таких вопросов бы не было ( ну, если ты не вхлам тупой, канеш ).

  • Войдите на сайт для отправки комментариев

Втр, 24/09/2019 — 10:57

ЕвгенийП аватар

Зарегистрирован: 25.05.2015
itehno пишет:

для чего нужен указатель и ссылка на указатель? Упростить не получится? Не совсем понятно, зачем такие сложности?

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

itehno пишет:
как передать функцию в качестве параметра в один из аргументов другой функции?

Никак. Можно передать указатель на функцию, а саму функцию — никак потому, что функция в С++ не является объектом первого класса (если Вам от такого объяснения станет легче).

  • Войдите на сайт для отправки комментариев

Пнд, 31/07/2023 — 22:18
Зарегистрирован: 17.01.2019

Доброго времени суток. Дабы не множить темы подскажите как передать Serial в качестве аргумента?
Клас работает с сериал. Для универсальности хочу чтоб он работал как с хардварным так и софтварным serial.

void setup() Serial.begeen(9600);
myClass(Serial);
softwareSerial mySerial(1,2);
mySerial.begeen(9600);
myClass(mySerial);
>
Написал по памяти, для пояснения сути. Дело в том, что Serial и mySerial являються разными типами данных. И в случае если тип явно не указывать tamplate tampname. то userSerial в классе не будет иметь методов (получаю такую ошибку).

  • Войдите на сайт для отправки комментариев

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

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