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

Я хочу создать функцию, которая выполняет функцию, переданную параметром для набора данных. Как передать функцию в качестве параметра в C?


person andrewrk    schedule 13.08.2008    source источник
comment
Если вы используете функции / массивы в качестве переменных, ВСЕГДА используйте typedef.   -  person Mooing Duck    schedule 14.01.2014
comment
Мы называем это указателем на функцию.   -  person AminM    schedule 01.04.2014
comment
Имя функции должно быть понтером к функции. Большинство людей, изучающих C, рано или поздно покрывают qsort, что именно это делает?   -  person mckenzm    schedule 03.07.2015
comment
@MooingDuck Я не согласен с вашим предложением, и в вашем предложении нет аргументов в пользу вывода. Я лично предпочитаю никогда использовать typedef для указателей функций, и я думаю, что это делает код более понятным и легким для чтения.   -  person andrewrk    schedule 17.12.2015
comment
@andrewrk: Вы предпочитаете void funcA(void(*funcB)(int)) и void (*funcA())() typedef void funcB(); void funcA(funcB) и funcB funcA()? Я не вижу положительных моментов.   -  person Mooing Duck    schedule 17.12.2015
comment
О, я только что заметил, что в комментарии массивы указаны как переменные. Да, это неправильно. Я имел в виду указатели на массивы как переменные.   -  person Mooing Duck    schedule 17.12.2015
comment
Функция, переданная в качестве параметра, также может иметь параметр (ы). Может ли кто-нибудь показать мне пример, КАК получить доступ к этим параметрам функции? ?   -  person Jan Hus    schedule 18.04.2020
comment
См. Примеры того, как это делается в стандартных библиотечных функциях qsort и bsearch.   -  person Bob Jarvis - Reinstate Monica    schedule 15.02.2021


Ответы (8)


Декларация

Прототип функции, которая принимает параметр функции, выглядит следующим образом:

void func ( void (*f)(int) );

Это означает, что параметр f будет указателем на функцию, которая имеет тип возврата void и принимает единственный параметр int. Следующая функция (print) является примером функции, которую можно передать func в качестве параметра, поскольку это правильный тип:

void print ( int x ) {
  printf("%d\n", x);
}

Вызов функции

При вызове функции с параметром функции переданное значение должно быть указателем на функцию. Используйте для этого имя функции (без скобок):

func(print);

вызовет func, передав ему функцию печати.

Тело функции

Как и любой параметр, func теперь может использовать имя параметра в теле функции для доступа к значению параметра. Допустим, func применит переданную функцию к числам 0-4. Сначала рассмотрим, как будет выглядеть цикл при прямом вызове print:

for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
  print(ctr);
}

Поскольку в объявлении параметра func указано, что f - это имя указателя на желаемую функцию, мы сначала напомним, что если f является указателем, то *f - это то, на что указывает f (т.е. функция print в данном случае). В результате просто замените каждое вхождение print в приведенном выше цикле на *f:

void func ( void (*f)(int) ) {
  for ( int ctr = 0 ; ctr < 5 ; ctr++ ) {
    (*f)(ctr);
  }
}

Источник

person Niyaz    schedule 13.08.2008
comment
В вашем первом и последнем примерах кода * не является обязательным. И определение параметра функции, и вызов функции f могут принимать f, как и без *. Было бы неплохо сделать это так же, как и вы, чтобы было очевидно, что параметр f является указателем на функцию. Но читабельность часто ухудшается. - person Gauthier; 22.02.2012
comment
См. Примеры в [c99, 6.9.1§14]. Оба, конечно, правы, я просто хотел упомянуть альтернативу. - person Gauthier; 22.02.2012
comment
Действительно? В самом популярном ответе нет ни одной ссылки на использование typedef для указателей на функции? Извините, я должен проголосовать против. - person Jonathon Reinhart; 01.10.2014
comment
@JonathonReinhart, какие преимущества дает оценка typedef? Эта версия выглядит намного чище, и в ней также отсутствуют дополнительные инструкции. в значительной степени стартер здесь. - person Abhinav Gauniyal; 22.02.2015
comment
@JonathonReinhart хорошо заметен; Следует явно отметить, что определения типов указателей скрывают код и поэтому не должны использоваться. - person M.M; 24.11.2015
comment
@ M.M Не согласен. Рассмотрим API на основе обратных вызовов, где обратные вызовы регистрируются и хранятся в контекстной структуре. Если вы используете typedef для указателей функций и хотите изменить их прототип, вам нужно только изменить typedef указателя на функцию, а не изменять его везде. Это исходит от парня, который не согласен с чрезмерным определением типа (а именно с структурами). - person Jonathon Reinhart; 24.11.2015
comment
@JonathonReinhart, в этом случае я бы напечатал тип функции. - person M.M; 24.11.2015
comment
@ M.M Это стандартный C? Я играл с ним раньше, но подумал, что это расширение GNU. - person Jonathon Reinhart; 24.11.2015
comment
@JonathonReinhart Да, это было в ANSI C - person M.M; 24.11.2015
comment
Глядя на (stackoverflow.com/questions/4574985/) , единственная разница - это включение * при объявлении указателя на функцию, правильно? Потому что вы не можете определить функцию из определения типа функции. Существует много информации об указателях функций, определяющих типы, но не о типах функций. - person Jonathon Reinhart; 24.11.2015
comment
Что делать, если вы хотите передать указатель на функцию, которая возвращает указатель в качестве аргумента. Я пробовал это: - person azmath; 10.03.2016
comment
Есть ли что-нибудь против ссылок на функции, чтобы у нас не было сложного синтаксиса? В прошлый раз, когда я проверял, они работали отлично .. - person lorro; 26.07.2016
comment
@Gauthier - звездочка не обязательна из-за неявного разыменования? - person M.Ionut; 14.03.2020
comment
@ M.Ioan Я не уверен в деталях реализации, но обычно считаю, что идентификатор функции - это указатель на память (на начало функции). Рассмотрим следующую пару скобок (пустую или нет) как оператор, означающий запуск кода в том месте, на которое указывает предыдущий идентификатор. Я не уверен, как обычно реализуются указатели на функции, но если функции на самом деле являются указателями, я был бы удивлен, если бы указатель функции был указателем на такой указатель, а не точно таким же. - person Gauthier; 14.03.2020
comment
Может кто-нибудь сказать мне, что означают круглые скобки (* f) в void func (void (* f) (int)), пожалуйста? Почему, если я их удалю, код не работает? - person Gennaro Arguzzi; 28.05.2020
comment
@GennaroArguzzi Precedence. Без круглых скобок * привязывается к void вместо f, что означает, что вы объявляете f как функцию, которая возвращает указатель на void, а не как указатель на функцию, которая возвращает void. - person Ove; 28.02.2021

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

typedef void (*functiontype)();

Объявляет функцию, которая возвращает void и не принимает аргументов. Чтобы создать указатель на функцию этого типа, вы можете:

void dosomething() { }

functiontype func = &dosomething;
func();

Для функции, которая возвращает int и принимает char, вы бы сделали

typedef int (*functiontype2)(char);

и использовать это

int dosomethingwithchar(char a) { return 1; }

functiontype2 func2 = &dosomethingwithchar
int result = func2('a');

Существуют библиотеки, которые могут помочь превратить указатели функций в удобные для чтения типы. Библиотека boost function великолепна и стоит того усилие!

boost::function<int (char a)> functiontype2;

намного лучше, чем выше.

person roo    schedule 13.08.2008
comment
Если вы хотите превратить указатель на функцию в тип, вам не нужна библиотека boost. Просто используйте typedef; это проще и не требует дополнительных библиотек. - person wizzwizz4; 09.04.2016
comment
Разве не Boost библиотека C ++? Почему вы упоминаете об этом в вопросе на C? - person 12431234123412341234123; 02.10.2020

Начиная с C ++ 11, вы можете использовать функциональную библиотеку, чтобы сделать это в лаконичная и универсальная мода. Синтаксис, например,

std::function<bool (int)>

где bool - это тип возвращаемого значения функции с одним аргументом, первый аргумент которой имеет тип int.

Я включил пример программы ниже:

// g++ test.cpp --std=c++11
#include <functional>

double Combiner(double a, double b, std::function<double (double,double)> func){
  return func(a,b);
}

double Add(double a, double b){
  return a+b;
}

double Mult(double a, double b){
  return a*b;
}

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}

Однако иногда удобнее использовать функцию-шаблон:

// g++ test.cpp --std=c++11

template<class T>
double Combiner(double a, double b, T func){
  return func(a,b);
}

double Add(double a, double b){
  return a+b;
}

double Mult(double a, double b){
  return a*b;
}

int main(){
  Combiner(12,13,Add);
  Combiner(12,13,Mult);
}
person Richard    schedule 20.07.2015
comment
Вопрос в C; C ++ здесь не применяется. - person Super Cat; 30.10.2015
comment
Вопрос для C ++ (stackoverflow.com/questions/6339970/) упоминается здесь, поэтому я думаю, что этот ответ уместен. - person mr_T; 24.03.2016
comment
Возможно, здесь не следует упоминать вопрос о C ++, потому что этот вопрос касается C. - person Michael Fulton; 19.04.2018
comment
Этот вопрос впервые возник, когда я искал вызов функции С ++ в качестве параметра, поэтому этот ответ здесь хорошо служит своей цели. - person ufoxDan; 08.11.2019

Передайте адрес функции в качестве параметра другой функции, как показано ниже.

#include <stdio.h>

void print();
void execute(void());

int main()
{
    execute(print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void f()) // receive address of print
{
    f();
}

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

#include <stdio.h>

void print();
void execute(void (*f)());

int main()
{
    execute(&print); // sends address of print
    return 0;
}

void print()
{
    printf("Hello!");
}

void execute(void (*f)()) // receive address of print
{
    f();
}
person Yogeesh H T    schedule 23.11.2015
comment
Очень хороший ответ, с целыми блоками кода (вместо того, чтобы нарезать все до непонятного беспорядка). Не могли бы вы подробнее рассказать о различиях обоих методов? - person Rafael Eyng; 29.01.2020
comment
Поправьте меня, если я ошибаюсь, имена функций AFAIK являются указателями, как и имена массивов, поэтому в первом примере вы передаете объект функции, а компилятор выполняет неявное преобразование, во втором примере вы напрямую передаете указатель функции, который является явным преобразованием. - person Sekomer; 27.06.2021

Функции могут быть "переданы" как указатели на функции, согласно ISO C11 6.7.6.3p8: "Объявление параметра как '' функция, возвращающая тип '' должно быть изменено на '' указатель на функцию, возвращающую тип '' < / em>, как в 6.3.2.1. ". Например, это:

void foo(int bar(int, int));

эквивалентно этому:

void foo(int (*bar)(int, int));
person doppelheathen    schedule 27.11.2018
comment
Из какого документа вы цитируете? Есть ссылка на это? - person Rafael Eyng; 30.01.2020
comment
Я цитирую стандарт ISO C11. - person doppelheathen; 05.02.2020

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

person saint_groceon    schedule 13.08.2008

Я собираюсь объяснить это на простом примере кода, который принимает функцию compare в качестве параметра другой функции sorting. Допустим, у меня есть функция пузырьковой сортировки, которая принимает настраиваемую функцию сравнения и использует ее вместо фиксированного оператора if.

Функция сравнения

bool compare(int a, int b) {
    return a > b;
}

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

Функция пузырьковой сортировки

void bubble_sort(int arr[], int n, bool (&cmp)(int a, int b)) {

    for (int i = 0;i < n - 1;i++) {
        for (int j = 0;j < (n - 1 - i);j++) {
            
            if (cmp(arr[j], arr[j + 1])) {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
}

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

int main()
{
    int i, n = 10, key = 11;
    int arr[10] = { 20, 22, 18, 8, 12, 3, 6, 12, 11, 15 };

    bubble_sort(arr, n, compare);
    cout<<"Sorted Order"<<endl;
    for (int i = 0;i < n;i++) {
        cout << arr[i] << " ";
    }
}

Выход:

Sorted Order
3 6 8 11 12 12 15 18 20 22
person Hemanth Kollipara    schedule 31.01.2021

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

#include <stdio.h>

int IncMultInt(int a, int b)
{
    a++;
    return a * b;
}

int main(int argc, char *argv[])

{
    int a = 5;
    int b = 7;

    printf("%d * %d = %d\n", a, b, IncMultInt(a, b));

    b = 9;

    // Create some local code with it's own local variable
    printf("%d * %d = %d\n", a, b,  ( { int _a = a+1; _a * b; } ) );

    return 0;
}
person Walter    schedule 15.03.2019
comment
Вы просто вызываете функцию. Как бы вы передали какую-нибудь другую функцию вместо IncMultInt? - person Tejas Pendse; 28.11.2019