Comment passez-vous une fonction comme paramètre en C?

616

Je veux créer une fonction qui exécute une fonction passée par paramètre sur un ensemble de données. Comment passez-vous une fonction comme paramètre en C?

andrewrk
la source
12
Si vous utilisez des fonctions / tableaux comme variables, utilisez TOUJOURS typedef.
Mooing Duck
3
Nous l'appelons pointeur de fonction
AminM
Le nom de la fonction doit être un ponteur pour la fonction. La plupart des gens qui apprennent C couvrent qsort tôt ou tard, qui fait exactement cela?
mckenzm
5
@MooingDuck Je ne suis pas d'accord avec votre suggestion, et votre suggestion manque de raisonnement pour étayer la conclusion. Personnellement, je préfère ne jamais utiliser typedef sur les pointeurs de fonction, et je pense que cela rend le code plus clair et plus facile à lire.
andrewrk
2
@andrewrk: Vous préférez void funcA(void(*funcB)(int))et void (*funcA())()à typedef void funcB(); void funcA(funcB)et funcB funcA()? Je ne vois pas l'avantage.
Mooing Duck

Réponses:

747

Déclaration

Un prototype de fonction qui prend un paramètre de fonction ressemble à ceci:

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

Cela indique que le paramètre fsera un pointeur vers une fonction qui a un voidtype de retour et qui prend un seul intparamètre. La fonction suivante ( print) est un exemple de fonction qui peut être transmise en functant que paramètre car c'est le type approprié:

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

Appel de fonction

Lors de l'appel d'une fonction avec un paramètre de fonction, la valeur transmise doit être un pointeur vers une fonction. Utilisez le nom de la fonction (sans parenthèses) pour cela:

func(print);

appellerait func, en lui passant la fonction d'impression.

Corps de fonction

Comme pour tout paramètre, funcpeut désormais utiliser le nom du paramètre dans le corps de la fonction pour accéder à la valeur du paramètre. Disons que funccela appliquera la fonction, elle est passée aux nombres 0-4. Considérez d'abord à quoi ressemblerait la boucle pour appeler directement print:

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

Puisque funcla déclaration des paramètres de dit que fc'est le nom d'un pointeur vers la fonction désirée, nous rappelons d'abord que si fest un pointeur alors *fc'est la chose qui fpointe (c'est-à-dire la fonction printdans ce cas). Par conséquent, remplacez simplement chaque occurrence de print dans la boucle ci-dessus par *f:

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

La source

Niyaz
la source
40
Dans vos premier et dernier exemples de code, le * n'est pas obligatoire. La définition du paramètre de fonction et l' fappel de fonction peuvent prendre fcomme tel sans *. Il peut être judicieux de le faire comme vous le faites pour rendre évident que le paramètre f est un pointeur de fonction. Mais cela nuit assez souvent à la lisibilité.
Gauthier
5
Voir [c99, 6.9.1§14] pour des exemples. Les deux sont corrects bien sûr, je voulais juste mentionner l'alternative.
Gauthier
6
Vraiment? La réponse la mieux notée ne fait-elle pas une seule référence à l'utilisation d'un typedefpointeur de fonction? Désolé, je dois voter contre.
Jonathon Reinhart
3
@JonathonReinhart, quels seraient les avantages d'une approbation 'typedef'? Cependant, cette version semble beaucoup plus propre et manque de déclarations supplémentaires. à peu près ici.
Abhinav Gauniyal
3
@JonathonReinhart bien repéré; il convient de noter explicitement que les types de pointeurs masquent le code et ne doivent donc pas être utilisés.
MM
128

Cette question a déjà la réponse pour définir des pointeurs de fonction, mais ils peuvent devenir très compliqués, surtout si vous allez les faire circuler dans votre application. Pour éviter ce désagrément, je vous recommande de taper le pointeur de fonction en quelque chose de plus lisible. Par exemple.

typedef void (*functiontype)();

Déclare une fonction qui renvoie void et ne prend aucun argument. Pour créer un pointeur de fonction sur ce type, vous pouvez maintenant:

void dosomething() { }

functiontype func = &dosomething;
func();

Pour une fonction qui renvoie un entier et prend un caractère, vous feriez

typedef int (*functiontype2)(char);

et l'utiliser

int dosomethingwithchar(char a) { return 1; }

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

Il existe des bibliothèques qui peuvent vous aider à transformer les pointeurs de fonction en de bons types lisibles. La bibliothèque de fonctions boost est géniale et vaut bien l'effort!

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

est tellement plus agréable que ce qui précède.

roo
la source
4
Si vous voulez "transformer un pointeur de fonction en type", vous n'avez pas besoin de la bibliothèque boost. Utilisez simplement typedef; c'est plus simple et ne nécessite aucune bibliothèque supplémentaire.
wizzwizz4
73

Depuis C ++ 11, vous pouvez utiliser la bibliothèque fonctionnelle pour le faire de manière succincte et générique. La syntaxe est, par exemple,

std::function<bool (int)>

boolest le type de retour ici d'une fonction à un argument dont le premier argument est de type int.

J'ai inclus un exemple de programme ci-dessous:

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

Parfois, cependant, il est plus pratique d'utiliser une fonction de modèle:

// 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
la source
43
La question concerne C; C ++ ne s'applique pas ici.
Super Cat
16
La question pour C ++ ( stackoverflow.com/questions/6339970/… ) est mentionnée ici, donc je pense que cette réponse est en place.
mr_T
8
Peut-être que la question pour C ++ ne devrait pas être mentionnée ici parce que cette question pour est C.
Michael Fulton
5
Cette question est apparue en premier lorsque j'ai recherché "l'appel de fonction c ++ comme paramètre", donc cette réponse remplit bien son rôle ici.
ufoxDan
25

Passer l' adresse d'une fonction comme paramètre à une autre fonction comme indiqué ci-dessous

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

Nous pouvons également passer la fonction comme paramètre en utilisant le pointeur de fonction

#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
la source
1
Très belle réponse, avec des blocs entiers de code (au lieu de tout découper en un gâchis incompréhensible). Pourriez-vous nous expliquer les différences des deux techniques?
Rafael Eyng
5

Les fonctions peuvent être "passées" en tant que pointeurs de fonction, conformément à ISO C11 6.7.6.3p8: " Une déclaration d'un paramètre comme '' fonction renvoyant le type '' doit être ajustée sur '' pointeur vers la fonction renvoyant le type '' , comme en 6.3 .2.1. ". Par exemple, ceci:

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

est équivalent à ceci:

void foo(int (*bar)(int, int));
doppelheathen
la source
De quel document parlez-vous? Un lien vers celui-ci?
Rafael Eyng
1
Je cite la norme ISO C11.
doppelheathen
4

Vous devez passer un pointeur de fonction . La syntaxe est un peu lourde, mais elle est vraiment puissante une fois que vous vous y êtes familiarisé.

saint_groceon
la source
-2

Ce n'est pas vraiment une fonction, mais c'est un morceau de code localisé. Bien sûr, il ne transmet pas le code uniquement le résultat. Cela ne fonctionnera pas s'il est transmis à un répartiteur d'événements pour être exécuté ultérieurement (car le résultat est calculé maintenant et non pas lorsque l'événement se produit). Mais il localise votre code en un seul endroit si c'est tout ce que vous essayez de faire.

#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
la source
Vous invoquez simplement une fonction. Comment passeriez-vous une autre fonction à la place de IncMultInt?
Tejas Pendse