¿Para qué sirven los punteros de función y cómo los usaría?

Entiendo que puedo usar punteros para las funciones.

¿Alguien puede explicar por qué uno los usaría y cómo? Un código de ejemplo corto me sería muy útil.

Un caso simple es el siguiente: tiene una serie de operaciones (funciones) de acuerdo con su lógica comercial. Tiene una función de hash que reduce un problema de entrada a una de las funciones de lógica de negocios. Un código limpio tendría una matriz de indicadores de función, y su progtwig deducirá un índice de esa matriz de la entrada y la llamará.

Aquí hay un código de muestra:

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

Pero, por supuesto, las devoluciones de llamada son el uso más común. Código de muestra a continuación:

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

Un caso de uso bastante común es una función de callback. Por ejemplo, si carga algo desde un DB, puede implementar su función de carga para que informe el progreso a una función de callback. Esto se puede hacer con punteros a funciones.

Devolución de llamada Hago una llamada asíncrona a un fragmento de código y quiero que me avise cuando termine, puedo enviarle un puntero a la función para llamar una vez que haya terminado.

Me sorprende que nadie haya mencionado “máquinas de estado”. Los punteros de función son una forma muy común de implementar máquinas de estado para tareas como el análisis sintáctico. Ver por ejemplo: enlace .

Utiliza un puntero de función cuando necesita dar un método de callback. Uno de los ejemplos clásicos es registrar manejadores de señal: a qué función se llamará cuando su progtwig obtenga SIGTERM (Ctrl-C)

Aquí hay otro ejemplo:

 // 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 ab; } float Multiply(float a, float b) { return a*b; } float Divide (float a, float b) { return a/b; } // Solution with a switch-statement -  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 -  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; } 

Puede obtener más información sobre los indicadores de función aquí http://www.newty.de/fpt/index.html

Si está más familiarizado con los lenguajes orientados a objetos, puede pensar que es la forma en que C aplica el patrón de diseño de la estrategia .

Hagamos una función tipo map para C.

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

De esta forma, podemos transformar una función que funciona en enteros para trabajar en matrices de enteros. También podríamos hacer una versión similar:

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

Esto hace lo mismo, pero permite que nuestra función sepa en qué elemento está. Podríamos usar esto, por ejemplo:

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

Ese código se imprimirá:

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

Para nuestro ejemplo de enumeración:

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

Esto imprime:

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

Como un ejemplo más real, una biblioteca de cadenas podría tener una función que aplica una función que opera en caracteres únicos a todos los caracteres de la cadena. Un ejemplo de tales funciones son las funciones de biblioteca estándar toupper() y tolower() en ctype.h - podríamos usar esta función string_apply() para hacer fácilmente una función string_toupper() .

Un tutorial muy bueno y fácil de entender: http://www.newty.de/fpt/index.html

Espero que esto ayude.

Otro uso para los punteros es repetir listas o matrices.

Para el código, consulte la respuesta de qrdl a los tutoriales de máquinas de estado .

He usado una variante de su método para implementar un menú para una pantalla LCD que tengo en una pizarra. Muy muy útil, hace que la encoding sea mucho más limpia y fácil de leer. El uso de punteros de función hace que la expansión del número, los nombres y el orden de los menús sea muy fácil, y oculta todos esos detalles de la función de llamadas de nivel superior que solo ve ‘hey, aquí escribo en la pantalla LCD’.