Come si passa una funzione come parametro in C?


616

Voglio creare una funzione che esegue una funzione passata per parametro su un set di dati. Come si passa una funzione come parametro in C?


12
Se si utilizzano funzioni / matrici come variabili, utilizzare SEMPRE typedef.
Mooing Duck,

3
Lo chiamiamo puntatore a funzione
AminM

Il nome della funzione dovrebbe essere un ponter per la funzione. La maggior parte delle persone che imparano C coprono qsort prima o poi che fa esattamente questo?
mckenzm,

5
@MooingDuck Non sono d'accordo con il tuo suggerimento e il tuo suggerimento manca di ragionamento per supportare la conclusione. Personalmente preferisco non usare mai typedef sui puntatori a funzione e penso che renda il codice più chiaro e più facile da leggere.
andrewrk,

2
@andrewrk: Preferisci void funcA(void(*funcB)(int))e void (*funcA())()a typedef void funcB(); void funcA(funcB)e funcB funcA()? Non vedo il lato positivo.
Mooing Duck il

Risposte:


747

Dichiarazione

Un prototipo per una funzione che accetta un parametro di funzione è simile al seguente:

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

Ciò indica che il parametro fsarà un puntatore a una funzione che ha un voidtipo restituito e che accetta un singolo intparametro. La seguente funzione ( print) è un esempio di una funzione che può essere passata funccome parametro perché è il tipo corretto:

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

Chiamata di funzione

Quando si chiama una funzione con un parametro di funzione, il valore passato deve essere un puntatore a una funzione. Utilizzare il nome della funzione (senza parentesi) per questo:

func(print);

chiamerebbe func, passando ad essa la funzione di stampa.

Corpo funzione

Come con qualsiasi parametro, funcora è possibile utilizzare il nome del parametro nel corpo della funzione per accedere al valore del parametro. Diciamo che funcapplicherà la funzione che viene passata ai numeri 0-4. Considera, innanzitutto, come dovrebbe essere il loop per chiamare print direttamente:

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

Dato che funcla dichiarazione dei parametri dice che fè il nome di un puntatore alla funzione desiderata, ricordiamo innanzitutto che se fè un puntatore allora *fè la cosa che fpunta (cioè la funzione printin questo caso). Di conseguenza, basta sostituire ogni occorrenza di stampa nel ciclo sopra con *f:

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

fonte


40
Nel tuo primo e ultimo esempio di codice, * non è obbligatorio. Sia la definizione del parametro della ffunzione che la chiamata della funzione possono assumere fesattamente come sono senza *. Potrebbe essere una buona idea farlo come fai tu, per rendere ovvio che il parametro f è un puntatore a funzione. Ma fa male la leggibilità abbastanza spesso.
Gauthier,

5
Vedere [c99, 6.9.1§14] per esempi. Entrambi sono corretti, ovviamente, volevo solo menzionare l'alternativa.
Gauthier,

6
Veramente? La risposta più votata non fa un singolo riferimento all'utilizzo typedefdei puntatori a per la funzione? Scusate, ho un voto negativo.
Jonathon Reinhart,

3
@JonathonReinhart, quali sarebbero i vantaggi con 'typedef' apprach? Questa versione sembra molto più pulita e manca anche di dichiarazioni extra. praticamente antipasto qui.
Abhinav Gauniyal,

3
@JonathonReinhart ben individuato; va notato esplicitamente che il typedef del puntatore offusca il codice e quindi non deve essere usato.
MM

128

Questa domanda ha già la risposta per definire i puntatori a funzioni, tuttavia possono diventare molto disordinati, specialmente se li passerai intorno all'applicazione. Per evitare questa spiacevolezza, consiglierei di digitare il puntatore a funzione in qualcosa di più leggibile. Per esempio.

typedef void (*functiontype)();

Dichiara una funzione che restituisce void e non accetta argomenti. Per creare un puntatore a questo tipo di funzione ora puoi fare:

void dosomething() { }

functiontype func = &dosomething;
func();

Per una funzione che restituisce un int e prende un carattere che faresti

typedef int (*functiontype2)(char);

e per usarlo

int dosomethingwithchar(char a) { return 1; }

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

Ci sono librerie che possono aiutare a trasformare i puntatori a funzioni in tipi leggibili. La libreria di funzioni boost è eccezionale e vale la pena!

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

è molto più bello di quanto sopra.


4
Se vuoi "trasformare un puntatore a funzione in un tipo", non hai bisogno della libreria boost. Basta usare typedef; è più semplice e non richiede librerie extra.
wizzwizz4,

73

Da C ++ 11 è possibile utilizzare la libreria funzionale per fare ciò in modo sintetico e generico. La sintassi è, ad esempio,

std::function<bool (int)>

dove si booltrova qui il tipo restituito di una funzione a argomento singolo il cui primo argomento è di tipoint .

Ho incluso un programma di esempio di seguito:

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

A volte, tuttavia, è più comodo usare una funzione template:

// 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
La domanda riguarda C; C ++ non si applica qui.
Super Cat,

16
La domanda per C ++ ( stackoverflow.com/questions/6339970/… ) è citata qui, quindi penso che questa risposta sia a posto.
m__T

8
Forse la domanda per C ++ non dovrebbe essere citata qui perché questa domanda è C.
Michael Fulton,

5
Questa domanda è emersa per prima quando ho cercato "chiamata alla funzione c ++ come parametro", quindi questa risposta serve bene allo scopo qui.
ufoxDan

25

Passare l' indirizzo di una funzione come parametro a un'altra funzione come mostrato di seguito

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

Inoltre possiamo passare la funzione come parametro usando il puntatore a funzione

#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
Risposta molto bella, con interi blocchi di codice (invece di tagliare tutto in un pasticcio incomprensibile). Potresti approfondire le differenze di entrambe le tecniche?
Rafael Eyng,

5

Le funzioni possono essere "passate" come puntatori a funzione, come da ISO C11 6.7.6.3p8: " Una dichiarazione di un parametro come '' tipo di ritorno funzione '' deve essere regolata su '' puntatore al tipo di ritorno funzione '' , come in 6.3 .2.1. ". Ad esempio, questo:

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

è equivalente a questo:

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

Da quale documento stai citando? Qualche link ad esso?
Rafael Eyng,

1
Sto citando dallo standard ISO C11.
Doppelheathen,


-2

Non è proprio una funzione, ma è un pezzo di codice localizzato. Ovviamente non passa il codice solo il risultato. Non funzionerà se passato a un dispatcher di eventi da eseguire in un secondo momento (poiché il risultato viene calcolato ora e non quando si verifica l'evento). Ma localizza il tuo codice in un unico posto se questo è tutto ciò che stai cercando di fare.

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

Stai solo invocando una funzione. Come passeresti a qualche altra funzione al posto di IncMultInt?
Tejas Pendse,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.