2010-09-14 15 views
30

Encontré este fragmento de código y me perdí por completo al interpretar su significado.¿Cuál es el significado de este fragmento de código? void (* signal (int sig, void (* func) (int))) (int);

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

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

Sé que void y 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 tenía que hacer algunas pruebas más para ayudar a entender lo que está pasando, de la siguiente manera:

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, He intentado tanto void (*signal)(int) y void (signal)(int), con el mismo resultado de error que indicaba que estaba tratando de redeclare signal:

TestDeclaration.c:2: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here
TestDeclaration.c:3: error: 'signal' redeclared as different kind of symbol /usr/include/signal.h:93: error: previous declaration of 'signal' was here

Ahora sé que tanto los 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?

+3

+1 para mostrar que en realidad se entiende * algunos * de ella en lugar de * ninguno * de ella. – BoltClock

+3

Pruebe http://www.cdecl.org –

+2

Curiosamente, cdecl.org da un error de sintaxis en este caso. ¿Alguien puede explicar esto? –

Respuesta

36

Es la declaración de una función de tomar un int y un puntero a una función (teniendo int devolver void) y devolver un puntero a una función (teniendo int y devolver void).


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

Se puede interpretar mediante el tratamiento de todo en paréntesis como una sola entidad y luego trabajar hacia adentro con la "declaración de la siguiente manera el uso de" regla.

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

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

quitando la parte exterior:

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

Así, signal toma algunos parámetros y devuelve algo que puede ser dereferenced (debido a la líder *) para formar una función de tomar y devolver intvoid.

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

En cuanto a los parámetros se necesita un int (es decir sig) y void (*func)(int) que es un puntero a una función (teniendo int y volviendo void).

+9

+1. Muy conciso Ayuda a saber la regla en el sentido de las agujas del reloj/espiral cuando se intenta analizar las declaraciones complejas de C. - http://c-faq.com/decl/spiral.anderson.html –

+1

Personalmente, no soy partidario de la regla de las agujas del reloj/sprial. Prefiero trabajar desde el exterior. Sé que no es un enfoque popular, pero estoy mucho más contento de aplicar correctamente las reglas de gramática con mi enfoque. –

+0

Es curioso que la página de reglas en el sentido de las agujas del reloj/espiral vinculada arriba incluya la misma declaración sobre la que el OP está preguntando. –

0

Volviendo puntero a una función que toma un:

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

Y toma un argumento entero.

7

Este es uno de los ejemplos clásicos de cuán enrevesadas pueden resultar las declaraciones de C.
Para entender esta declaración, por lo general ayuda a 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 (teniendo un parámetro int y devolver nada). La función signal 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 se puede derivar de la declaración original, pero requiere un poco de práctica. La forma más habitual es empezar por el identificador que da nombre a la entidad externa (signal es este caso):

signal is a ...

Entonces has leído bien hasta que encuentre un paréntesis de cierre no coincida o al final de la declaración: void (*signal(int sig, void (*func)(int))(int)

signal is a function taking ... returning ...

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

`signal is a function taking ... returning a pointer to ...

lectura de ida y vuelta de esta manera se obtiene en etapas sucesivas:

`signal is a function taking ... returning a pointer to a (function taking ... returning ...)

`signal is a function taking ... returning a pointer to a (function taking ... returning void)

`signal is a function taking ... returning a pointer to a (function taking an int and returning void)

`signal is a function taking two parameters: (an int) and (a pointer to a function taking an int and returning void), and returning a pointer to a (function taking an int and returning void)

0

Una mnemónica que creé hace muchos años, que es invaluable cuando se trata de comprender 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 cuando los paréntesis cambian esa precedencia, por supuesto.

Su aplicación en 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, podrás llegar al punto donde se puede hacer todo lo que 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. 

(Lo siento el error la primera vez que salgo - Estoy fuera de la práctica.)

Sí, ese mnemónico (con el significado "excepto paréntesis, por supuesto) funciona para todas las declaraciones C, sin importar qué tan mal sean los punteros, matrices, un nd funciones están entremezcladas.

Esta es una habilidad REALMENTE útil para tratar de descubrir cómo funciona el código de otra persona ... o incluso descubrir algo propio que no ha 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 compilarla 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 mnemónica, siéntase libre de citarla en otro lugar, solo por favor, deme crédito como autor.

Por cierto, también hay herramientas "C Explainer" que analizarán las delaraciones de C y harán la conversión a la descripción en inglés para usted. 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.

+0

Y sí, el hecho de que Start and Suffix, Proceed and Prefix, comparte consonantes iniciales es deliberado; eso te ayuda a recordar el orden correcto. – keshlam

1

Tomemos un ejemplo de cómo podría utilizarse esta declaración desagradable:

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

Vamos a ver lo que será la salida:

Si el 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 el 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 ! 
Cuestiones relacionadas