¿Cómo se pasa una función como parámetro en C?

616

Quiero crear una función que realice una función pasada por parámetro en un conjunto de datos. ¿Cómo se pasa una función como parámetro en C?

andrewrk
fuente
12
Si usa funciones / matrices como variables, SIEMPRE use typedef.
Mooing Duck
3
Lo llamamos Puntero de función
AminM
El nombre de la función debe ser un reflejo de la función. La mayoría de las personas que aprenden C cubren qsort tarde o temprano, ¿qué hace exactamente esto?
mckenzm
55
@MooingDuck No estoy de acuerdo con su sugerencia, y su sugerencia carece de razonamiento para apoyar la conclusión. Personalmente prefiero nunca usar typedef en los punteros de función, y creo que hace que el código sea más claro y fácil de leer.
andrewrk
2
@andrewrk: ¿Prefieres void funcA(void(*funcB)(int))y void (*funcA())()a typedef void funcB(); void funcA(funcB)y funcB funcA()? No veo lo bueno.
Mooing Duck

Respuestas:

747

Declaración

Un prototipo para una función que toma un parámetro de función tiene el siguiente aspecto:

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

Esto indica que el parámetro fserá un puntero a una función que tiene un voidtipo de retorno y que toma un solo intparámetro. La siguiente función ( print) es un ejemplo de una función a la que podría pasarse funccomo parámetro porque es del tipo adecuado:

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

Llamada de función

Al llamar a una función con un parámetro de función, el valor pasado debe ser un puntero a una función. Use el nombre de la función (sin paréntesis) para esto:

func(print);

llamaría func, pasándole la función de impresión.

Cuerpo de la función

Al igual que con cualquier parámetro, funcahora puede usar el nombre del parámetro en el cuerpo de la función para acceder al valor del parámetro. Digamos que funcaplicará la función que se pasa a los números 0-4. Considere, primero, cómo sería el ciclo para llamar a print directamente:

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

Dado que funcla declaración de parámetros dice que ese fes el nombre de un puntero a la función deseada, recordamos primero que si fes un puntero, entonces *fes lo que fapunta (es decir, la función printen este caso). Como resultado, simplemente reemplace cada aparición de impresión en el bucle anterior con *f:

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

Fuente

Niyaz
fuente
40
En su primer y último ejemplo de código, el * no es obligatorio. Tanto la definición del parámetro de la ffunción como la llamada a la función pueden tomarse ftal como están sin *. Sin embargo, puede ser una buena idea hacerlo como lo hace, para que sea obvio que el parámetro f es un puntero de función. Pero daña la legibilidad con bastante frecuencia.
Gauthier
55
Ver [c99, 6.9.1§14] para ejemplos. Ambos son correctos, por supuesto, solo quería mencionar la alternativa.
Gauthier
66
De Verdad? ¿La respuesta mejor calificada no hace una sola referencia al uso typedefde los punteros de función for? Lo siento, tengo que votar abajo.
Jonathon Reinhart
3
@JonathonReinhart, ¿cuáles serían las ventajas de la aplicación 'typedef'? Sin embargo, esta versión se ve mucho más limpia y carece de declaraciones adicionales. más o menos entrante aquí.
Abhinav Gauniyal
3
@JonathonReinhart bien visto; Debe notarse explícitamente que los punteros typedefs ofuscan el código y, por lo tanto, no deben usarse.
MM
128

Esta pregunta ya tiene la respuesta para definir punteros de función, sin embargo, pueden volverse muy complicados, especialmente si los va a pasar por su aplicación. Para evitar esta molestia, le recomendaría que escriba el puntero de la función en algo más legible. Por ejemplo.

typedef void (*functiontype)();

Declara una función que devuelve void y no toma argumentos. Para crear un puntero de función para este tipo ahora puede hacer:

void dosomething() { }

functiontype func = &dosomething;
func();

Para una función que devuelve un int y toma un char que haría

typedef int (*functiontype2)(char);

y para usarlo

int dosomethingwithchar(char a) { return 1; }

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

Hay bibliotecas que pueden ayudar a convertir los punteros de función en tipos agradables y legibles. ¡La biblioteca de funciones de impulso es excelente y vale la pena el esfuerzo!

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

es mucho mejor que lo anterior.

roo
fuente
44
Si desea "convertir un puntero de función en un tipo", no necesita la biblioteca de impulso. Solo usa typedef; Es más simple y no requiere ninguna biblioteca adicional.
wizzwizz4
73

Desde C ++ 11 puede usar la biblioteca funcional para hacer esto de manera sucinta y genérica. La sintaxis es, por ejemplo,

std::function<bool (int)>

donde booles el tipo de retorno aquí de una función de un argumento cuyo primer argumento es de tipo int.

He incluido un programa de ejemplo a continuación:

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

Sin embargo, a veces es más conveniente usar una función de plantilla:

// 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);
}
Ricardo
fuente
43
La pregunta es sobre C; C ++ no se aplica aquí.
Super Cat
16
Aquí se hace referencia a la pregunta para C ++ ( stackoverflow.com/questions/6339970/… ), así que creo que esta respuesta está en su lugar.
m__T
8
Quizás la pregunta para C ++ no debería mencionarse aquí porque esta pregunta es C.
Michael Fulton
55
Esta pregunta surgió primero cuando busqué "llamada a función c ++ como parámetro", por lo que esta respuesta está cumpliendo su propósito aquí independientemente.
ufoxDan
25

Pase la dirección de una función como parámetro a otra función como se muestra a continuación

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

También podemos pasar la función como parámetro usando el puntero de función

#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
fuente
1
Muy buena respuesta, con bloques enteros de código (en lugar de cortar todo en un lío incomprensible). ¿Podría explicar las diferencias de ambas técnicas?
Rafael Eyng
5

Las funciones se pueden "pasar" como punteros de función, según ISO C11 6.7.6.3p8: " Una declaración de un parámetro como '' tipo de retorno de función '' se ajustará a '' puntero a tipo de retorno de función '' , como en 6.3 .2.1. ". Por ejemplo, esto:

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

es equivalente a esto:

void foo(int (*bar)(int, int));
doppelheathen
fuente
¿De qué documento estás citando? Cualquier enlace a ella?
Rafael Eyng
1
Estoy citando el estándar ISO C11.
Doppelheathen
4

Necesita pasar un puntero de función . La sintaxis es un poco engorrosa, pero es realmente poderosa una vez que te familiarizas con ella.

saint_groceon
fuente
-2

No es realmente una función, pero es un código localizado. Por supuesto, no pasa el código solo el resultado. No funcionará si se pasa a un despachador de eventos para que se ejecute más adelante (ya que el resultado se calcula ahora y no cuando se produce el evento). Pero sí localiza su código en un solo lugar si eso es todo lo que está tratando de hacer.

#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
fuente
Solo estás invocando una función. ¿Cómo pasarías alguna otra función en lugar de IncMultInt?
Tejas Pendse el