ptr pointer c

pointer - ptr function c



¿Cuál es el significado de esta pieza de código? void(* signal(int sig, void(* func)(int)))(int); (5)

Encontré este fragmento de código y me perdí por completo al interpretar su significado.

#include <signal.h> void (*signal(int sig, void (*func)(int)))(int);

¿Cuál es una explicación detallada del código en la línea 2?

Sé que void e int son tipos, el * func es un puntero para una función y los corchetes son para prioridad. Pero todavía no obtengo la (* señal ...), la (int), y todo combinado entre sí. Cuanto más detallado mejor.

Probablemente he sabido el significado / efecto de esta declaración. Pero tuve que hacer algunas pruebas más para ayudarme a entender lo que está sucediendo, como se muestra a continuación:

1 #include <signal.h> 2 void (*signal)(int sig, void (*func)(int)); 3 void (*signal)(int); // then void (signal)(int) again. 4 //void (*signal(int sig, void (*func)(int)))(int); //break this line into two lines above 5 6 int main(){}

En el código anterior, rompí void (*signal(int sig, void (*func)(int)))(int) en dos líneas. Para la línea 3, probé tanto void (*signal)(int) como void (signal)(int) , con el mismo resultado de error que indicaba que estaba intentando redeclarar la signal :

TestDeclaration.c: 2: error: ''señal'' redeclarada como un tipo diferente de símbolo /usr/include/signal.h:93: error: la declaración anterior de ''señal'' estaba aquí
TestDeclaration.c: 3: error: ''señal'' redeclarada como un tipo diferente de símbolo /usr/include/signal.h:93: error: la declaración anterior de ''señal'' estaba aquí

Ahora sé que ambos ensayos son formas incorrectas de declaración, pero ¿por qué son incorrectos? ¿Por qué la forma original de declaración NO es una redeclaración?


Devolver el puntero a una función que toma una:

  • entero como primer argumento argumento y
  • un puntero a una función (que toma un int y devuelve el vacío) como un argumento como un segundo argumento.

Y toma un argumento entero.


Es la declaración de una función tomando un int y un puntero a una función (tomando int void) y retornando un puntero a una función (tomando int y regresando void).

Explicación o guía para la interpretación

Puede interpretar tratando todo entre paréntesis como una sola entidad y luego trabajando hacia adentro usando la regla "declaración siguiente al uso".

void (* signal (int sig, void (* func) (int))) (int);

La entidad entre paréntesis parece una función que toma int y devuelve void .

Despojando la parte exterior:

*signal(int sig, void (*func)(int))

Entonces, la signal toma algunos parámetros y devuelve algo que puede ser desreferenciado (debido al inicio * ) para formar una función que toma int y devuelve void .

Esto significa que la signal es una función que devuelve un puntero a una función (tomando int y int void ).

Mirando los parámetros se necesita un int (es decir, sig ) y void (*func)(int) que es un puntero a una función (tomando int y int void ).


Este es uno de los ejemplos clásicos de cuán complicadas pueden llegar a ser las declaraciones de C.
Para comprender esta declaración, generalmente es útil introducir un typedef:

typedef void (*sighandler_t)(int); sighandler_t signal(int sig, sighandler_t func);

El typedef declara un puntero a una función (toma un parámetro int y no devuelve nada). La signal función ahora se puede ver como una función que toma dos parámetros (un int y un puntero a una función) y devuelve un puntero a una función.

Esto también puede derivarse de la declaración original, pero requiere un poco de práctica. La forma habitual es comenzar por el identificador que nombra a la entidad más externa (la signal es este caso):

signal es ...

Luego, lee a la derecha hasta que encuentre un paréntesis de cierre inigualable o el final de la declaración: void (* signal (int sig, void (*func)(int) )(int)

signal es una función que toma ... regresar ...

Ahora puede elegir entre analizar primero los parámetros o el valor de retorno primero. Voy a hacer el valor de retorno primero. Para eso, lee hacia atrás para encontrar el paréntesis de apertura correspondiente: void ( signal( / ... */ ) ) (int)

`signal es una función que toma ... devolviendo un puntero a ...

Leyendo hacia adelante y hacia atrás de esta manera se obtiene en etapas sucesivas:

`signal es una función que toma ... devolviendo un puntero a a (función tomando ... regresando ...)

`signal es una función que toma ... devolviendo un puntero a a (función tomando ... volviendo vacío)

`signal es una función que toma ... devolviendo un puntero a a (función tomando un int y devolviendo el vacío)

`signal es una función que toma dos parámetros: (an int) y (un puntero a una función que toma un vacío int y return), y devuelve un puntero a a (función que toma un vacío int y return)


Una mnemotécnica que creé hace muchos años, que es invaluable cuando intento entender tipos complicados:

Remember these rules for C declares And precedence never will be in doubt Start with the Suffix, Proceed with the Prefix And read both sets from the inside, out.

Excepto donde los paréntesis cambian esa precedencia, por supuesto.

Aplicandolo a este caso:

void (*signal(int sig, void (*func)(int)))(int); signal is: [inside parentheses] [suffix ()] a function, whose arguments are sig, which is [prefix int] an integer, and func, which is: [inside parentheses] [no suffix within these parens] [prefix *] a pointer to [suffix ()] a function, whose argument is an int [no more suffixes] [prefix void] and which returns void [no more prefixes] [no more arguments] [prefix *] And which returns a pointer to [no more prefixes within these parens] [suffix ()] a function, whose argument is an int [no more suffixes] [prefix void] and which returns void.

Con un poco de práctica, llegarás al punto en que puedes hacer todo eso sobre la marcha:

"Signal is function, whose arguments are: sig, an integer, and func, a pointer to a function whose argument is an int and which returns void ... which returns a pointer to a function that takes int as an argument and returns void.

(Perdón por el error la primera vez que salgo - estoy fuera de práctica.)

Sí, ese mnemónico (con la implícita "excepto paréntesis, por supuesto" funciona para todas las declaraciones C, sin importar cuán mal estén entremezclados los punteros, matrices y funciones.

Esta es una habilidad REALMENTE útil para tratar de descubrir cómo funciona el código de otra persona ... o incluso descubrir algo tuyo que no hayas visto en mucho tiempo.

Pero, sí, la mejor manera de manejar cualquier cosa que no creas que la gente pueda leer de un vistazo es construirla en capas con typedefs. Es probable que los tipos de componentes sean útiles por sí mismos, y dar un paso a la vez evita que la gente se pierda tratando de descubrir qué paréntesis concuerda con cuál. ¡Sé amable con la próxima persona que toque tu código!

Si le parece útil la letra mnemónica, siéntase libre de citarla en otro lugar; solo deme crédito como autor, por favor.

Por cierto, también hay herramientas de "Explicador C" que analizarán las delaracciones de C y harán la conversión a su descripción en inglés. El mío se llamaba CEX, por razones obvias, pero existen muchos otros y deberías poder encontrar uno si no quieres comprometer esta habilidad con el wetware o si alguien te entrega algo que es realmente demasiado feo para que puedas seguirlo.


Veamos un ejemplo de cómo podría usarse esta desagradable declaración:

void (*signal(int sig, void (*func)(int)))(int);

Sin demasiada verbosidad, podríamos decir que "señal" es una función con dos parámetros que devuelve una función.

#include <stdio.h> // First function that could be returned by our principal function // Note that we can point to it using void (*f)(int) void car_is_red(int color) { printf("[car_is_red] Color %d (red) is my favorite color too !/n", color); } // Second function that could be returned by our principal function // Note that we can point to it using void (*f)(int) void car_is_gray(int color) { printf("[car_is_gray] I don''t like the color %d (gray) either !/n", color); } // The function taken as second parameter by our principal function // Note that we can point to it using void (*func)(int) void show_car_type(int mod) { printf("[show_car_type] Our car has the type: %d/n",mod); } /* Our principal function. Takes two parameters, returns a function. */ void (* show_car_attributes(int color, void (*func)(int)) )(int) { printf("[show_car_attributes] Our car has the color: %d/n",color); // Use the first parameter int mod = 11; // Some local variable of our function show_car_attributes() func(mod); // Call the function pointed by the second parameter (i.e. show_car_type() ) // Depending on color value, return the pointer to one of two functions // Note that we do NOT use braces with function names if (color == 1) return car_is_red; else return car_is_gray; } //main() function int main() { int color = 2; // Declare our color for the car void (*f)(int); // Declare a pointer to a function with one parameter (int) f = show_car_attributes(color, show_car_type); // f will take the return // value of our principal function. Stated without braces, the // parameter "show_car_types" is a function pointer of type // void (*func)(int). f(color); // Call function that was returned by show_car_attributes() return 0; }

Veamos qué se generará:

Si color = 1

[show_car_attributes] Our car has the color: 1 [show_car_type] Our car has the type: 11 [car_is_red] Color 1 (red) is my favorite color too !

Si color = 2

[show_car_attributes] Our car has the color: 2 [show_car_type] Our car has the type: 11 [car_is_gray] I don''t like the color 2 (gray) either !