Як ви передаєте функцію як параметр у C?


616

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


12
Якщо ви використовуєте функції / масиви як змінні, ВИНАГИ використовуйте typedef.
Mooing Duck

3
Ми називаємо це покажчик функцій
AminM

Назва функції має бути понтером функції. Більшість людей, які навчаються C, охоплюють qsort рано чи пізно, що робить саме це?
mckenzm

5
@MooingDuck Я не згоден з вашою пропозицією, і у вашій пропозиції немає міркувань, щоб підтвердити висновок. Я особисто вважаю за краще ніколи не використовувати typedef у функціональних вказівниках, і я думаю, що це робить код яснішим та легшим для читання.
andrewrk

2
@andrewrk: Ви віддаєте перевагу void funcA(void(*funcB)(int))і void (*funcA())()до typedef void funcB(); void funcA(funcB)і funcB funcA()? Я не бачу перевернутого боку.
Mooing Duck

Відповіді:


747

Декларація

Прототип функції, яка приймає параметр функції, виглядає наступним чином:

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. Розглянемо спочатку, як виглядатиме цикл для виклику друку безпосередньо:

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

Оскільки funcв оголошенні параметра ' p ' сказано, що fце ім'я для вказівника на потрібну функцію, ми спочатку нагадаємо, що, якщо fвказівник, то *fце те, на що fвказує (тобто функція printв даному випадку). Як результат, просто замініть кожну появу друку в циклі вище на *f:

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

Джерело


40
У вашому першому та останньому прикладах коду * не є обов'язковим. Як визначення параметра функції, так і fвиклик функції можуть приймати так fсамо, як і без *. Це може бути хорошою ідеєю, як це зробити, хоча ви зробите зрозумілим, що параметр f - покажчик функції. Але боляче читати це дуже часто.
Готьє

5
Див. [C99, 6.9.1§14] приклади. Обидва вірні, звичайно, я просто хотів згадати альтернативу.
Готьє

6
Дійсно? Відповідь з найвищим рейтингом не містить жодної посилання на використання typedefпокажчиків функцій? Вибачте, доведеться проголосувати.
Джонатан Райнхарт

3
@JonathonReinhart, які переваги матиме apprach 'typedef'? Ця версія виглядає набагато чистіше, але також не вистачає зайвих заяв. тут дуже багато стартерів.
Абхінав Гауніял

3
@JonathonReinhart добре помічений; слід чітко зауважити, що вказівники typedefs замовчують код і тому не повинні використовуватися.
ММ

129

Це питання вже має відповідь для визначення покажчиків функцій, однак вони можуть стати дуже безладним, особливо якщо ви збираєтеся передавати їх навколо своєї програми. Щоб уникнути цієї неприємності, я б рекомендував ввести покажчик функції в щось більш читабельне. Наприклад.

typedef void (*functiontype)();

Оголошує функцію, яка повертає недійсним і не приймає аргументів. Щоб створити вказівник функції на цей тип, ви можете виконати:

void dosomething() { }

functiontype func = &dosomething;
func();

Для функції, яка повертає int і приймає знак, який ви зробили б

typedef int (*functiontype2)(char);

і використовувати його

int dosomethingwithchar(char a) { return 1; }

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

Є бібліотеки, які можуть допомогти перетворити покажчики функцій на приємні для читання типи. Бібліотека функцій підсилення велика і варто вартих зусиль!

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

настільки приємніше, ніж вище.


4
Якщо ви хочете "перетворити покажчик функції на тип", вам не потрібна бібліотека підвищення. Просто використовуйте typedef; це простіше і не вимагає зайвих бібліотек.
wizzwizz4

72

Оскільки 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);
}

43
Питання стосується С; C ++ тут ​​не застосовується.
Супер Кіт

16
Тут зазначається питання щодо C ++ ( stackoverflow.com/questions/6339970/… ), тому я думаю, що ця відповідь є на місці.
mr_T

8
Можливо, питання про C ++ тут ​​не слід згадувати, оскільки це питання є C.
Майкл Фултон,

5
Це запитання виникло спочатку, коли я шукав "c ++ виклик функції як параметр", тому ця відповідь добре відповідає своєму призначенню тут незалежно.
ufoxDan

25

Передайте адресу функції як параметра іншій функції, як показано нижче

#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();
}

1
Дуже приємна відповідь, з цілими блоками коду (замість того, щоб розбивати все в незрозумілий безлад). Не могли б ви детальніше розкрити відмінності обох методів?
Рафаель Ейн

5

Функції можна "передавати" як покажчики функцій відповідно до ISO C11 6.7.6.3p8: " Оголошення параметра як" тип повернення функції "має бути налаштовано на" вказівник на функцію повернення типу " , як в 6.3 .2.1. ". Наприклад, це:

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

еквівалентно цьому:

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

З якого документа ви цитуєте? Будь-яке посилання на нього?
Рафаель Ейн

1
Я цитую зі стандарту ISO C11.
doppelheathen


-2

Це насправді не функція, але це локалізований фрагмент коду. Звичайно, код не передає лише результат. Він не працюватиме, якщо його передаватимуть диспетчеру подій, який буде запущено пізніше (оскільки результат обчислюється зараз, а не тоді, коли подія відбувається) Але він локалізує ваш код в одному місці, якщо це все, що ви намагаєтесь зробити.

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

Ви просто викликаєте функцію. Як би ви передали якусь іншу функцію замість IncMultInt?
Техас Пендсе
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.