Wie übergibt man eine Funktion als Parameter in C?

616

Ich möchte eine Funktion erstellen, die eine Funktion ausführt, die von einem Parameter für einen Datensatz übergeben wird. Wie übergibt man eine Funktion als Parameter in C?

andrewrk
quelle
12
Wenn Sie Funktionen / Arrays als Variablen verwenden, verwenden Sie IMMER typedef.
Mooing Duck
3
Wir nennen es Funktionszeiger
AminM
Der Funktionsname sollte ein Ponter der Funktion sein. Die meisten Leute, die C lernen, decken früher oder später qsort ab, was genau das tut?
McKenzm
5
@MooingDuck Ich stimme Ihrem Vorschlag nicht zu, und Ihrem Vorschlag fehlen Argumente, um die Schlussfolgerung zu stützen. Ich persönlich bevorzuge es, niemals typedef für Funktionszeiger zu verwenden, und ich denke, dies macht den Code klarer und leichter lesbar.
Andrewrk
2
@andrewrk: Du bevorzugst void funcA(void(*funcB)(int))und void (*funcA())()zu typedef void funcB(); void funcA(funcB)und funcB funcA()? Ich sehe den Kopf nicht.
Mooing Duck

Antworten:

747

Erklärung

Ein Prototyp für eine Funktion, die einen Funktionsparameter verwendet, sieht folgendermaßen aus:

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

Dies besagt, dass der Parameter fein Zeiger auf eine Funktion ist, die einen voidRückgabetyp hat und einen einzelnen intParameter akzeptiert. Die folgende Funktion ( print) ist ein Beispiel für eine Funktion, die funcals Parameter übergeben werden kann, da es sich um den richtigen Typ handelt:

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

Funktionsaufruf

Beim Aufruf einer Funktion mit einem Funktionsparameter muss der übergebene Wert ein Zeiger auf eine Funktion sein. Verwenden Sie dazu den Namen der Funktion (ohne Klammern):

func(print);

würde aufrufen funcund die Druckfunktion an ihn übergeben.

Funktionskörper

Wie bei jedem Parameter funckann jetzt der Name des Parameters im Funktionskörper verwendet werden, um auf den Wert des Parameters zuzugreifen. Angenommen, dies funcwendet die Funktion an, die an die Zahlen 0-4 übergeben wird. Überlegen Sie zunächst, wie die Schleife aussehen würde, um print direkt aufzurufen:

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

Da funcdie Parameterdeklaration besagt, dass dies fder Name für einen Zeiger auf die gewünschte Funktion ist, erinnern wir uns zunächst daran, dass, wenn fes sich um einen Zeiger *fhandelt, auf was gezeigt wird f(dh printin diesem Fall auf die Funktion ). Ersetzen Sie daher einfach jedes Auftreten von Druck in der obigen Schleife durch *f:

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

Quelle

Niyaz
quelle
40
In Ihrem ersten und letzten Codebeispiel ist das * nicht obligatorisch. Sowohl die Funktionsparameterdefinition als auch der fFunktionsaufruf können fgenauso wie ohne * ausgeführt werden. Es könnte jedoch eine gute Idee sein, dies so zu tun, wie Sie es tun, um deutlich zu machen, dass der Parameter f ein Funktionszeiger ist. Aber es schadet ziemlich oft der Lesbarkeit.
Gauthier
5
Beispiele finden Sie in [c99, 6.9.1§14]. Beides ist natürlich richtig, ich wollte nur die Alternative erwähnen.
Gauthier
6
"Ja wirklich?" Die am besten bewertete Antwort enthält keinen einzigen Verweis auf die Verwendung eines typedeffor-Funktionszeigers? Entschuldigung, ich muss abstimmen.
Jonathon Reinhart
3
@ JonathonReinhart, was wären die Vorteile von 'typedef' apprach? Diese Version sieht jedoch viel sauberer aus und es fehlen auch zusätzliche Aussagen. so ziemlich Starter hier.
Abhinav Gauniyal
3
@ JonathonReinhart gut entdeckt; Es sollte ausdrücklich darauf hingewiesen werden, dass Zeigertypedefs den Code verschleiern und daher nicht verwendet werden sollten.
MM
129

Diese Frage enthält bereits die Antwort zum Definieren von Funktionszeigern. Sie können jedoch sehr unübersichtlich werden, insbesondere wenn Sie sie in Ihrer Anwendung weitergeben. Um diese Unannehmlichkeit zu vermeiden, würde ich empfehlen, dass Sie den Funktionszeiger in etwas Lesbareres eingeben. Zum Beispiel.

typedef void (*functiontype)();

Deklariert eine Funktion, die void zurückgibt und keine Argumente akzeptiert. So erstellen Sie einen Funktionszeiger auf diesen Typ:

void dosomething() { }

functiontype func = &dosomething;
func();

Für eine Funktion, die ein int zurückgibt und ein Zeichen annimmt, würden Sie dies tun

typedef int (*functiontype2)(char);

und es zu benutzen

int dosomethingwithchar(char a) { return 1; }

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

Es gibt Bibliotheken, die helfen können, Funktionszeiger in gut lesbare Typen umzuwandeln. Die Boost-Funktionsbibliothek ist großartig und die Mühe wert!

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

ist so viel schöner als die oben genannten.

roo
quelle
4
Wenn Sie "einen Funktionszeiger in einen Typ verwandeln" möchten, benötigen Sie die Boost-Bibliothek nicht. Verwenden Sie einfach typedef; Es ist einfacher und erfordert keine zusätzlichen Bibliotheken.
wizzwizz4
72

Seit C ++ 11 können Sie die Funktionsbibliothek verwenden , um dies kurz und allgemein zu tun. Die Syntax lautet z.

std::function<bool (int)>

wo boolist der Rückgabetyp hier eine einargumentigen Funktion , deren ersten Argument vom Typ int.

Ich habe unten ein Beispielprogramm eingefügt:

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

Manchmal ist es jedoch bequemer, eine Vorlagenfunktion zu verwenden:

// 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);
}
Richard
quelle
43
Die Frage ist über C; C ++ gilt hier nicht.
Super Cat
16
Auf die Frage für C ++ ( stackoverflow.com/questions/6339970/… ) wird hier verwiesen, daher denke ich, dass diese Antwort vorhanden ist.
mr_T
8
Vielleicht sollte die Frage für C ++ hier nicht erwähnt werden, weil diese Frage für C. ist
Michael Fulton
5
Diese Frage tauchte zuerst auf, als ich nach "c ++ - Funktionsaufruf als Parameter" suchte, daher erfüllt diese Antwort hier unabhängig ihren Zweck.
UfoxDan
25

Übergeben Sie die Adresse einer Funktion als Parameter an eine andere Funktion, wie unten gezeigt

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

Wir können die Funktion auch als Parameter mit dem Funktionszeiger übergeben

#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();
}
Yogeesh HT
quelle
1
Sehr schöne Antwort mit ganzen Codeblöcken (anstatt alles in ein unverständliches Chaos zu zerlegen). Könnten Sie die Unterschiede beider Techniken näher erläutern?
Rafael Eyng
5

Funktionen können gemäß ISO C11 6.7.6.3p8 als Funktionszeiger "übergeben" werden: " Eine Deklaration eines Parameters als" Funktionsrückgabetyp "muss wie in 6.3 als " Zeiger auf Funktionsrückgabetyp "angepasst werden .2.1. ". Zum Beispiel:

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

ist gleichbedeutend damit:

void foo(int (*bar)(int, int));
Doppelheiden
quelle
Aus welchem ​​Dokument zitieren Sie? Irgendein Link dazu?
Rafael Eyng
1
Ich zitiere aus dem ISO C11-Standard.
Doppelheathen
4

Sie müssen einen Funktionszeiger übergeben . Die Syntax ist etwas umständlich, aber sie ist sehr leistungsfähig, sobald Sie sich damit vertraut gemacht haben.

saint_groceon
quelle
-2

Es ist nicht wirklich eine Funktion, aber es ist ein lokalisierter Code. Natürlich gibt es den Code nicht nur als Ergebnis weiter. Es funktioniert nicht, wenn es an einen Ereignis-Dispatcher übergeben wird, um zu einem späteren Zeitpunkt ausgeführt zu werden (da das Ergebnis jetzt berechnet wird und nicht, wenn das Ereignis eintritt). Aber es lokalisiert Ihren Code an einem Ort, wenn das alles ist, was Sie versuchen zu tun.

#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;
}
Walter
quelle
Sie rufen nur eine Funktion auf. Wie würden Sie eine andere Funktion anstelle von übergeben IncMultInt?
Tejas Pendse