¿Puntero de función typedef?

460

Estoy aprendiendo cómo cargar DLL de forma dinámica, pero lo que no entiendo es esta línea

typedef void (*FunctionFunc)();

Tengo algunas preguntas. Si alguien puede contestarle, estaría agradecido.

  1. ¿Por qué se typedefusa?
  2. La sintaxis parece extraña; ¿después de voidque no debería haber un nombre de función o algo así? Parece una función anónima.
  3. ¿Se ha creado un puntero de función para almacenar la dirección de memoria de una función?

Entonces estoy confundido en este momento; ¿Puedes aclararme las cosas?

Jack Harvin
fuente
55
Eche un vistazo al enlace (última sección) learncpp.com/cpp-tutorial/78-function-pointers
enthusiasticgeek
99
Cabe señalar que desde c ++ 11 using FunctionFunc = void (*)();se puede utilizar en su lugar. Es un poco más claro que solo está declarando un nombre para un tipo (puntero para funcionar)
user362515
solo para agregar a @ user362515, una forma un poco más clara para mí es:using FunctionFunc = void(void);
topspin
@topspin IIRC estos dos no son lo mismo. Uno es un tipo de puntero de función, el otro es un tipo de función. Hay una conversión implícita, por eso funciona, IANA (C ++) L, así que uno puede intervenir y corregirme. En cualquier caso, si la intención es definir un tipo de puntero, creo que la sintaxis con el *es un poco más explícita.
user362515

Respuestas:

463

typedefes una construcción de lenguaje que asocia un nombre a un tipo.
Lo usa de la misma manera que usaría el tipo original, por ejemplo

  typedef int myinteger;
  typedef char *mystring;
  typedef void (*myfunc)();

usándolos como

  myinteger i;   // is equivalent to    int i;
  mystring s;    // is the same as      char *s;
  myfunc f;      // compile equally as  void (*f)();

Como puede ver, podría reemplazar el nombre typedefed con su definición dada anteriormente.

La dificultad radica en el puntero a las funciones de sintaxis y legibilidad en C y C ++, y typedefpuede mejorar la legibilidad de tales declaraciones. Sin embargo, la sintaxis es apropiada, ya que las funciones, a diferencia de otros tipos más simples, pueden tener un valor de retorno y parámetros, por lo tanto, la declaración a veces larga y compleja de un puntero para funcionar.

La legibilidad puede comenzar a ser realmente complicada con punteros a matrices de funciones y algunos otros sabores aún más indirectos.

Para contestar tus tres preguntas

  • ¿Por qué se usa typedef? Para facilitar la lectura del código, especialmente para punteros a funciones o nombres de estructura.

  • La sintaxis parece extraña (en el puntero a la declaración de la función) Esa sintaxis no es obvia de leer, al menos al comenzar. Usar una typedefdeclaración en su lugar facilita la lectura

  • ¿Se ha creado un puntero de función para almacenar la dirección de memoria de una función? Sí, un puntero de función almacena la dirección de una función. Esto no tiene nada que ver con la typedefconstrucción que solo facilita la escritura / lectura de un programa; el compilador solo expande la definición typedef antes de compilar el código real.

Ejemplo:

typedef int (*t_somefunc)(int,int);

int product(int u, int v) {
  return u*v;
}

t_somefunc afunc = &product;
...
int x2 = (*afunc)(123, 456); // call product() to calculate 123*456
e2-e4
fuente
66
en el último ejemplo, no solo 'cuadrado' se referiría a la misma cosa, es decir, puntero a la función en lugar de usar & cuadrado.
pranavk
2
Cuestión, en su primer ejemplo typedef usted tiene de la forma typedef type aliaspero con los punteros de función allí sólo parece ser de 2 argumentos, typedef type. ¿El alias está predeterminado en el nombre especificado en el argumento de tipo?
dchhetri
2
@pranavk: Sí, squarey &square(y, de hecho, *squarey **square) todos se refieren al mismo puntero de función.
Jonathan Leffler
66
@ user814628: No está claro exactamente lo que está preguntando. Con typedef int newname, te estás convirtiendo newnameen un alias para int. Con typedef int (*func)(int), se está convirtiendo funcen un alias para int (*)(int): un puntero para funcionar tomando un intargumento y devolviendo un intvalor.
Jonathan Leffler
10
Supongo que estoy confundido sobre el pedido. Con typedef int (*func)(int), entiendo que func es un alias, solo un poco confundido porque el alias está enredado con el tipo. Pasando por typedef int INTun ejemplo, sería más fácil si el puntero de la función typedef fuera de forma typedef int(*function)(int) FUNC_1. De esa manera, puedo ver el tipo y el alias en dos tokens separados en lugar de combinarlos en uno.
dchhetri
188
  1. typedefse usa para los tipos de alias; en este caso que estés aliasing FunctionFunca void(*)().

  2. De hecho, la sintaxis parece extraña, mira esto:

    typedef   void      (*FunctionFunc)  ( );
    //         ^                ^         ^
    //     return type      type name  arguments
  3. No, esto simplemente le dice al compilador que el FunctionFunctipo será un puntero de función, no define uno, como este:

    FunctionFunc x;
    void doSomething() { printf("Hello there\n"); }
    x = &doSomething;
    
    x(); //prints "Hello there"
Jacob Relkin
fuente
27
typedefno no declarar un nuevo tipo. usted puede tener muchos typedefnombres -definida del mismo tipo, y ellos son no distinta (por ejemplo WRT. sobrecarga de funciones). hay algunas circunstancias en las que, con respecto a cómo puede usar el nombre, un typedefnombre definido no es exactamente equivalente a lo que se define, pero los typedefnombres múltiples definidos para el mismo son equivalentes.
Saludos y hth. - Alf
Ah lo entiendo ahora. Gracias.
Jack Harvin
44
+1 para la respuesta a la pregunta 2: muy claro. El resto de las respuestas también fueron claras, pero esa se destaca para mí :-)
Michael van der Westhuizen
33

Sin la typedefpalabra, en C ++ la declaración declararía una variable FunctionFuncde tipo puntero para funcionar sin argumentos, regresando void.

En typedefcambio, se define FunctionFunccomo un nombre para ese tipo.

Saludos y hth. - Alf
fuente
55
Esta es una muy buena manera de pensarlo. Si lee atentamente las otras respuestas, en realidad no abordan la confusión del OP sobre el enredo del alias y el nombre. Aprendí algo nuevo al leer esta publicación.
Mad Physicist
9

Si usted puede utilizar C ++ 11 es posible que desee utilizar std::functiony usingpalabra clave.

using FunctionFunc = std::function<void(int arg1, std::string arg2)>;
Halil Kaskavalci
fuente
1
sin la usingpalabra clave C ++ 11 sería typedef std::function<void(int, std::string)> FunctionFunc;, en caso de que alguien quiera otro contenedor alrededor de las funciones sin C ++ 11
Top-Master
2
#include <stdio.h>
#include <math.h>

/*
To define a new type name with typedef, follow these steps:
1. Write the statement as if a variable of the desired type were being declared.
2. Where the name of the declared variable would normally appear, substitute the new type name.
3. In front of everything, place the keyword typedef.
*/

// typedef a primitive data type
typedef double distance;

// typedef struct 
typedef struct{
    int x;
    int y;
} point;

//typedef an array 
typedef point points[100]; 

points ps = {0}; // ps is an array of 100 point 

// typedef a function
typedef distance (*distanceFun_p)(point,point) ; // TYPE_DEF distanceFun_p TO BE int (*distanceFun_p)(point,point)

// prototype a function     
distance findDistance(point, point);

int main(int argc, char const *argv[])
{
    // delcare a function pointer 
    distanceFun_p func_p;

    // initialize the function pointer with a function address
    func_p = findDistance;

    // initialize two point variables 
    point p1 = {0,0} , p2 = {1,1};

    // call the function through the pointer
    distance d = func_p(p1,p2);

    printf("the distance is %f\n", d );

    return 0;
}

distance findDistance(point p1, point p2)
{
distance xdiff =  p1.x - p2.x;
distance ydiff =  p1.y - p2.y;

return sqrt( (xdiff * xdiff) + (ydiff * ydiff) );
}
Amjad
fuente
1
¿En qué circunstancias se necesita el '&'? Por ejemplo, ¿'func_p = & findDistance' y 'func_p = findDistance' funcionan igual de bien?
Donna
1
El nombre de una función es un puntero, por lo que no necesita usar '&' con él. En otras palabras, '&' es opcional cuando se consideran los punteros de función. Sin embargo, para el tipo de datos primitivo, debe usar '&' para obtener su dirección.
Amjad
1
Siempre ha parecido extraño que el '&' pueda ser opcional. Si se usa un typedef para crear un puntero a una primitiva (es decir typedef (*double) p, ¿es '&' opcional?
Donna
Creo que querías escribir typedef double* ppara definir un puntero a un doble. Si desea llenar el ppuntero desde una variable primitiva, deberá usar '&'. Una nota al margen, nosotros * <nombre del puntero> para desreferenciar un puntero. Se *utiliza en el puntero de función para desreferenciar el puntero (nombre de la función) e ir al bloque de memoria donde se encuentran las instrucciones de la función.
Amjad
2

Para el caso general de la sintaxis se puede ver en el anexo A de la norma ANSI C .

En el formulario Backus-Naur desde allí, puede ver que typedeftiene el tipo storage-class-specifier.

En el tipo declaration-specifiers, puede ver que puede mezclar muchos tipos de especificador, cuyo orden no importa.

Por ejemplo, es correcto decir,

long typedef long a;

para definir el tipo acomo un alias para long long. Por lo tanto, para comprender la definición de tipo en el uso exhaustivo, debe consultar alguna forma de backus-naur que defina la sintaxis (hay muchas gramáticas correctas para ANSI C, no solo la de ISO).

Cuando utiliza typedef para definir un alias para un tipo de función, debe colocar el alias en el mismo lugar donde coloca el identificador de la función. En su caso, defina el tipo FunctionFunccomo un alias para que funcione un puntero cuya verificación de tipo esté deshabilitada en la llamada y no devuelva nada.

alinsoar
fuente