Jak przekazać funkcję jako parametr w C?


615

Chcę utworzyć funkcję, która wykonuje funkcję przekazywaną przez parametr na zestawie danych. Jak przekazać funkcję jako parametr w C?


12
Jeśli używasz funkcji / tablic jako zmiennych, ZAWSZE używaj typedef.
Mooing Duck

3
Nazywamy to Wskaźnikiem funkcji
AminM

Nazwa funkcji powinna być ponter do funkcji. Większość osób uczących się C prędzej czy później obejmuje qsort, co to dokładnie robi?
mckenzm

5
@MooingDuck Nie zgadzam się z twoją sugestią, a twojej sugestii brakuje uzasadnienia na poparcie wniosku. Osobiście wolę nigdy nie używać typedef do wskaźników funkcji i myślę, że dzięki temu kod jest bardziej przejrzysty i łatwiejszy do odczytania.
andrewrk

2
@andrewrk: Wolisz void funcA(void(*funcB)(int))i void (*funcA())()do typedef void funcB(); void funcA(funcB)i funcB funcA()? Nie widzę plusów.
Mooing Duck,

Odpowiedzi:


746

Deklaracja

Prototyp funkcji, która przyjmuje parametr funkcji, wygląda następująco:

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

Oznacza to, że parametr fbędzie wskaźnikiem do funkcji, która ma voidtyp zwracany i która przyjmuje pojedynczy intparametr. Następująca funkcja ( print) jest przykładem funkcji, do której można przekazać funcjako parametr, ponieważ jest to właściwy typ:

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

Wywołanie funkcji

Podczas wywoływania funkcji z parametrem funkcji przekazywana wartość musi być wskaźnikiem funkcji. Użyj do tego nazwy funkcji (bez nawiasów):

func(print);

zadzwoni func, przekazując mu funkcję drukowania.

Funkcja Body

Jak w przypadku każdego parametru, funcmożna teraz używać nazwy parametru w treści funkcji, aby uzyskać dostęp do wartości parametru. Powiedzmy, że funczastosuje funkcję, która zostanie przekazana do liczb 0-4. Najpierw zastanów się, jak pętla mogłaby wyglądać tak, aby wywołać print:

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

Ponieważ funcdeklaracja parametru mówi, że fjest to nazwa wskaźnika do żądanej funkcji, najpierw przypominamy sobie, że jeśli fjest wskaźnikiem, *fto rzecz, która fwskazuje (tj. Funkcja printw tym przypadku). W rezultacie po prostu zamień każde wystąpienie wydruku w powyższej pętli na *f:

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

Źródło


40
W pierwszym i ostatnim przykładzie kodu * nie jest obowiązkowe. Zarówno definicja parametru funkcji, jak i fwywołanie funkcji mogą trwać ftak samo, jak bez *. Dobrym pomysłem może być zrobienie tego tak, jak robisz, aby było oczywiste, że parametr f jest wskaźnikiem funkcji. Ale dość często szkodzi to czytelności.
Gauthier

5
Przykłady patrz [c99, 6.9.1§14]. Oba są oczywiście poprawne, chciałem tylko wspomnieć o alternatywie.
Gauthier

6
Naprawdę? Najwyżej oceniana odpowiedź nie odnosi się ani do używania typedefwskaźników funkcji? Przepraszam, muszę głosować w dół.
Jonathon Reinhart

3
@JonathonReinhart, jakie byłyby zalety appracha „typedef”? Ta wersja wygląda jednak na znacznie czystszą i brakuje w niej również dodatkowych instrukcji. właściwie starter tutaj.
Abhinav Gauniyal

3
@JonathonReinhart dobrze zauważony; należy wyraźnie zauważyć, że wskaźnik typedefs zaciemnia kod i dlatego nie powinien być używany.
MM

129

To pytanie ma już odpowiedź na zdefiniowanie wskaźników funkcji, jednak mogą stać się bardzo nieuporządkowane, szczególnie jeśli zamierzasz przekazywać je wokół aplikacji. Aby uniknąć tej nieprzyjemności, polecam wpisanie wskaźnika funkcji w coś bardziej czytelnego. Na przykład.

typedef void (*functiontype)();

Deklaruje funkcję, która zwraca void i nie przyjmuje żadnych argumentów. Aby utworzyć wskaźnik funkcji do tego typu, możesz teraz:

void dosomething() { }

functiontype func = &dosomething;
func();

Dla funkcji, która zwraca wartość int i przyjmuje znak char, zrobiłbyś to

typedef int (*functiontype2)(char);

i z niego korzystać

int dosomethingwithchar(char a) { return 1; }

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

Istnieją biblioteki, które mogą pomóc w przekształceniu wskaźników funkcji w ładne, czytelne typy. Funkcja Boost biblioteka jest wielki i jest warte wysiłku!

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

jest o wiele ładniejszy niż wyżej.


4
Jeśli chcesz „zmienić wskaźnik funkcji na typ”, nie potrzebujesz biblioteki doładowań. Po prostu użyj typedef; jest prostszy i nie wymaga żadnych dodatkowych bibliotek.
wizzwizz4

72

Od wersji C ++ 11 możesz użyć biblioteki funkcjonalnej, aby zrobić to w zwięzły i ogólny sposób. Składnia jest np.

std::function<bool (int)>

gdzie booljest tutaj typ zwracany funkcji jednoparametrowej, której pierwszy argument jest typu int.

Poniżej zamieściłem przykładowy program:

// 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);
}

Czasami jednak wygodniej jest użyć funkcji szablonu:

// 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
Pytanie dotyczy C; C ++ nie ma tutaj zastosowania.
Super Cat

16
Pytanie do C ++ ( stackoverflow.com/questions/6339970/... ) jest tutaj przytoczone, więc myślę, że ta odpowiedź jest na miejscu.
mr_T

8
Być może nie należy tu odnosić się do pytania o C ++, ponieważ to pytanie brzmi C.
Michael Fulton

5
To pytanie pojawiło się po raz pierwszy, gdy szukałem „wywołania funkcji c ++ jako parametru”, więc ta odpowiedź dobrze spełnia swoje zadanie tutaj.
ufoxDan

25

Przekaż adres funkcji jako parametru do innej funkcji, jak pokazano poniżej

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

Możemy również przekazać funkcję jako parametr za pomocą wskaźnika funkcji

#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
Bardzo ładna odpowiedź, z całymi blokami kodu (zamiast krojenia wszystkiego w niezrozumiały bałagan). Czy mógłbyś rozwinąć różnice między obiema technikami?
Rafael Eyng

5

Funkcje można „przekazać” jako wskaźniki funkcji, zgodnie z ISO C11 6.7.6.3p8: „ Deklarację parametru jako„ typu zwracającego funkcję ”należy ustawić na„ wskaźnik do typu zwracającego funkcję ” , jak w 6.3 .2.1. ”. Na przykład:

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

jest równoważne z tym:

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

Z jakiego dokumentu cytujesz? Jakieś łącze do tego?
Rafael Eyng

1
Cytuję ze standardu ISO C11.
doppelheathen


-2

To nie jest tak naprawdę funkcja, ale jest zlokalizowanym fragmentem kodu. Oczywiście nie przekazuje kodu tylko wyniku. Nie zadziała, jeśli zostanie przekazany do programu rozsyłającego zdarzenia, który zostanie uruchomiony w późniejszym czasie (ponieważ wynik jest obliczany teraz, a nie w momencie wystąpienia zdarzenia). Ale lokalizuje kod w jednym miejscu, jeśli to wszystko, co próbujesz zrobić.

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

Po prostu wywołujesz funkcję. Jak przekazałbyś jakąś inną funkcję zamiast IncMultInt?
Tejas Pendse
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.