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?
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:
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.
typedefvoid(*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:
Pour une fonction qui renvoie un entier et prend un caractère, vous feriez
typedefint(*functiontype2)(char);
et l'utiliser
int dosomethingwithchar(char a){return1;}
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!
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)>
où boolest le type de retour ici d'une fonction à un argument dont le premier argument est de type int.
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:
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>intIncMultInt(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;}));return0;}
typedef
.void funcA(void(*funcB)(int))
etvoid (*funcA())()
àtypedef void funcB(); void funcA(funcB)
etfuncB funcA()
? Je ne vois pas l'avantage.Réponses:
Déclaration
Un prototype de fonction qui prend un paramètre de fonction ressemble à ceci:
Cela indique que le paramètre
f
sera un pointeur vers une fonction qui a unvoid
type de retour et qui prend un seulint
paramètre. La fonction suivante (print
) est un exemple de fonction qui peut être transmise enfunc
tant que paramètre car c'est le type approprié: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:
appellerait
func
, en lui passant la fonction d'impression.Corps de fonction
Comme pour tout paramètre,
func
peut désormais utiliser le nom du paramètre dans le corps de la fonction pour accéder à la valeur du paramètre. Disons quefunc
cela appliquera la fonction, elle est passée aux nombres 0-4. Considérez d'abord à quoi ressemblerait la boucle pour appeler directement print:Puisque
func
la déclaration des paramètres de dit quef
c'est le nom d'un pointeur vers la fonction désirée, nous rappelons d'abord que sif
est un pointeur alors*f
c'est la chose quif
pointe (c'est-à-dire la fonctionprint
dans ce cas). Par conséquent, remplacez simplement chaque occurrence de print dans la boucle ci-dessus par*f
:La source
la source
f
appel de fonction peuvent prendref
comme 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é.typedef
pointeur de fonction? Désolé, je dois voter contre.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.
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:
Pour une fonction qui renvoie un entier et prend un caractère, vous feriez
et l'utiliser
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!
est tellement plus agréable que ce qui précède.
la source
typedef
; c'est plus simple et ne nécessite aucune bibliothèque supplémentaire.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,
où
bool
est le type de retour ici d'une fonction à un argument dont le premier argument est de typeint
.J'ai inclus un exemple de programme ci-dessous:
Parfois, cependant, il est plus pratique d'utiliser une fonction de modèle:
la source
Passer l' adresse d'une fonction comme paramètre à une autre fonction comme indiqué ci-dessous
Nous pouvons également passer la fonction comme paramètre en utilisant le pointeur de fonction
la source
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:
est équivalent à ceci:
la source
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é.
la source
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.
la source
IncMultInt
?