2009-11-18 16 views
14

Capisco che posso usare i puntatori per le funzioni.A cosa servono i puntatori di funzione e come li utilizzerei?

Qualcuno può spiegare perché li si userebbe e come? Un breve codice di esempio sarebbe molto utile per me.

+8

Guarda la funzione di libreria standard 'qsort()' e vedi se questo ti aiuta. –

+1

http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work/840669#840669 –

+0

Sono anche utilizzati sotto il cofano dei sistemi operativi molto .. Come ho creato un sistema operativo con la struttura del driver utilizza molti puntatori di funzione. Sono molto utili perché sono come i metodi 'virtuali/astratti 'eccetto per il fatto che devi controllarlo e gestirlo. – Earlz

risposta

22

Un caso semplice è come questo: si dispone di una serie di operazioni (funzioni) in base alla propria logica aziendale. È disponibile una funzione di hashing che riduce un problema di input a una delle funzioni della business logic. Un codice pulito avrebbe una matrice di puntatori di funzione, e il tuo programma dedurrà un indice a quell'array dall'input e lo chiamerà.

Ecco un esempio di codice:

typedef void (*fn)(void) FNTYPE; 
FNTYPE fn_arr[5]; 

fn_arr[0] = fun1; // fun1 is previously defined 
fn_arr[1] = fun2; 
... 

void callMyFun(string inp) { 
    int idx = decideWhichFun(inp); // returns an int between 0 and 4 
    fn_arr[idx](); 
} 

Ma, naturalmente, i callback sono l'uso più comune. Esempio di codice riportato di seguito:

void doLengthyOperation(string inp, void (*callback)(string status)) { 
    // do the lengthy task 
    callback("finished"); 
} 

void fnAfterLengthyTask(string status) { 
    cout << status << endl; 
} 

int main() { 
    doLengthyOperation(someinput, fnAfterLengthyTask); 
} 
0

Un altro utilizzo per i puntatori è l'iterazione di elenchi o matrici.

+0

Non per i puntatori di funzione. Leggi la domanda. –

+1

utilizzando i puntatori di funzione negli iteratori è perfettamente valido, c'è un esempio qui: http://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work/840669#840669 –

13

Un caso di utilizzo piuttosto comune è una funzione di richiamata. Ad esempio, se carichi qualcosa da un DB, puoi implementare la tua funzione di caricamento in modo che riporti l'avanzamento in una funzione di callback. Questo può essere fatto con i puntatori di funzione.

+0

Uno dei miei preferiti. Ma non solo per riportare i progressi - in realtà per passare tutti i record! –

+0

Si tratta di un caso d'uso molto comune. –

5

Callback. Faccio una chiamata asincrona a un blocco di codice e voglio che me lo faccia sapere quando termina, posso mandargli un puntatore a funzione per chiamare una volta terminato.

3

Si utilizza un puntatore a funzione quando è necessario fornire un metodo di richiamata. Uno degli esempi classici è quello di registrare i gestori di segnale - quale funzione verrà chiamato quando il programma ottiene SIGTERM (Ctrl-C)

Ecco un altro esempio:

// The four arithmetic operations ... one of these functions is selected 
// at runtime with a switch or a function pointer 
float Plus (float a, float b) { return a+b; } 
float Minus (float a, float b) { return a-b; } 
float Multiply(float a, float b) { return a*b; } 
float Divide (float a, float b) { return a/b; } 

// Solution with a switch-statement - <opCode> specifies which operation to execute 
void Switch(float a, float b, char opCode) 
{ 
    float result; 

    // execute operation 
    switch(opCode) 
    { 
     case '+' : result = Plus  (a, b); break; 
     case '-' : result = Minus (a, b); break; 
     case '*' : result = Multiply (a, b); break; 
     case '/' : result = Divide (a, b); break; 
    } 

    cout << "Switch: 2+5=" << result << endl;   // display result 
} 

// Solution with a function pointer - <pt2Func> is a function pointer and points to 
// a function which takes two floats and returns a float. The function pointer 
// "specifies" which operation shall be executed. 
void Switch_With_Function_Pointer(float a, float b, float (*pt2Func)(float, float)) 
{ 
    float result = pt2Func(a, b); // call using function pointer 

    cout << "Switch replaced by function pointer: 2-5="; // display result 
    cout << result << endl; 
} 

si può imparare di più su puntatori a funzione qui http://www.newty.de/fpt/index.html

Se si ha maggiore familiarità con i linguaggi orientati agli oggetti, è possibile considerarlo come il modo in cui C implementa lo strategy design pattern.

+1

Avrebbe +1 se il linguaggio C avesse 'cout << endl' (e anche il tuo codice C++ deve essere' using namespace std; 'per farlo funzionare) –

+0

Dov'è la menzione di' opcode' nel tuo 'Switch_With_Function_Pointer() 'funzione? –

+0

@Joy - Nell'istruzione 'switch()'. –

1

Facciamo una funzione map -come per C.

void apply(int *arr, size_t len, int (*func)(int)) 
{ 
    for(size_t i = 0; i < len; i++) 
     arr[i] = func(arr[i]); 
} 

In questo modo, siamo in grado di trasformare una funzione che funziona su interi a lavorare su array di interi. Si potrebbe anche fare una versione simile:

void apply_enumerated(int *arr, size_t len, int (*func)(size_t, int)) 
{ 
    for(size_t i = 0; i < len; i++) 
     arr[i] = func(i, arr[i]); 
} 

Questo fa la stessa cosa, ma permette la nostra funzione di sapere quale elemento si trova.Potremmo usare questo, ad esempio:

int cube(int i) { return i * i * i } 

void print_array(int *array, size_t len, char *sep) 
{ 
    if(sep == NULL) sep = ", "; 
    printf("%d", *array); 
    for(size_t i = 1; i < len; i++) printf("%s%d", sep, array[i]) 
} 

#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) 

int main(void) 
{ 
    int array[5] = { 1, 2, 3, 4, 5 }; 
    print_array(array, ARRAY_SIZE(array), NULL); 
    apply(array, ARRAY_SIZE(array), cube); 
    print_array(array, ARRAY_SIZE(array), NULL); 
    return 0; 
} 

Questo codice stamperà:

1, 2, 3, 4, 5 
1, 8, 27, 64, 125 

Per il nostro esempio, l'enumerazione:

int mult(size_t i, int j) { return i * j } 

// print_array and ARRAY_SIZE as before 

int main(void) 
{ 
    int array[5] = { 1, 2, 3, 4, 5 }; 
    print_array(array, ARRAY_SIZE(array), NULL); 
    apply_enumerated(array, ARRAY_SIZE(array), mult); 
    print_array(array, ARRAY_SIZE(array), NULL); 
    return 0; 
} 

Questo stampa:

1, 2, 3, 4, 5 
0, 2, 6, 12, 20 

Come un esempio più reale del mondo, a st la ring library potrebbe avere una funzione che applica una funzione che opera su singoli caratteri a tutti i caratteri nella stringa. Un esempio di tali funzioni sono le funzioni della libreria standard toupper() e tolower() in ctype.h - potremmo utilizzare questa funzione string_apply() per rendere facilmente una funzione string_toupper().

3

Sono sorpreso che nessuno abbia menzionato "macchine di stato". I puntatori di funzione sono un modo molto comune per implementare macchine di stato per attività come l'analisi. Vedi ad esempio: link.

0

Per il codice, consultare la risposta di qrdl a state machines tutorials.

Ho usato una variante del suo metodo per implementare un menu per un display LCD che ho su una scheda. Molto molto utile, rende la codifica molto più pulita e più facile da leggere. L'uso di puntatori di funzione semplifica l'espansione del numero, dei nomi e dell'ordine dei menu, e nasconde tutti quei dettagli dalla funzione di chiamata di livello superiore che vede solo "hey, scrivo sul display LCD qui".

Problemi correlati