2010-09-14 8 views
30

Je suis tombé sur ce morceau de code et je me suis complètement perdu en interprétant sa signification.Quelle est la signification de ce morceau de code? void (* signal (int sig, void (* func) (int))) (int);

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

Qu'est-ce qu'une explication détaillée du code à la ligne 2?

Je sais que void et int sont des types, le * func est un pointeur pour une fonction, et les parenthèses sont pour la priorité. Mais je n'obtiens toujours pas le (* signal ...), le (int) et le tout ensemble. Le plus détaillé, le mieux. J'ai probablement connu le sens/l'effet de cette déclaration. Mais je devais faire quelques essais plus pour me aider à comprendre ce qui se passe, comme ci-dessous:

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(){} 

Dans le code ci-dessus, je me suis cassé void (*signal(int sig, void (*func)(int)))(int) en deux lignes. Pour la ligne 3, j'ai essayé à la fois void (*signal)(int) et void (signal)(int), avec le même résultat d'erreur indiquant que je tentais de redéclarer 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

Maintenant, je sais que les deux essais sont des moyens incorrects de déclaration, mais pourquoi sont-ils incorrects ? Pourquoi la manière originale de déclaration n'est-elle PAS une redéclaration?

+3

+1 pour montrer que vous comprenez réellement * certains * par opposition à * aucun *. – BoltClock

+3

Essayez http://www.cdecl.org –

+2

Curieusement, cdecl.org donne une erreur de syntaxe sur celui-ci. Quelqu'un peut-il expliquer cela? –

Répondre

36

Il est la déclaration d'une fonction qui prend une int et un pointeur vers une fonction (en int renvoyant void) et en retournant un pointeur vers une fonction (en int et retour vide).


Explication ou guide d'interprétation

Vous pouvez interpréter en traitant tout entre parenthèses comme une seule entité et travaille ensuite vers l'intérieur pour la règle « déclaration suivante utilisation ».

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

L'entité entre parenthèses ressemble à une fonction prenant int et renvoyant void.

dépouillant la partie extérieure:

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

Ainsi, signal prend quelques paramètres et retourne quelque chose qui peut être déréférencé (en raison du premier *) pour former une fonction prise int et retour void.

Cela signifie que signal est une fonction renvoyant un pointeur vers une fonction (en prenant int et en renvoyant void).

En regardant les paramètres qu'il prend un int (à savoir sig) et void (*func)(int) qui est un pointeur vers une fonction (en prenant int et retour void).

+9

+1. Très concis. Il est utile de connaître la règle Clockwise/spiral lorsque vous essayez d'analyser des déclarations C complexes. - http://c-faq.com/decl/spiral.anderson.html –

+1

Personnellement, je ne suis pas un fan de la règle horaire/sprial. Je préfère travailler de l'extérieur. Je sais que ce n'est pas une approche populaire, mais je suis beaucoup plus heureux que j'applique les règles de grammaire correctement avec mon approche. –

+0

C'est marrant que la page de règles dans le sens des aiguilles d'une montre/spirale ci-dessus inclue la même déclaration que celle demandée par l'OP. –

0

pointeur retour à une fonction qui prend un:

    entier
  • comme premier argument de raisonnement et
  • un pointeur vers une fonction (qui prend un int et renvoie void) comme un argument comme un second argument .

Et prend un argument entier.

7

Ceci est l'un des exemples classiques de la façon dont les déclarations C compliquées peuvent devenir.
Pour comprendre cette déclaration, il permet généralement d'introduire un typedef:

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

Le typedef déclare un pointeur vers une fonction (en un rien paramètre int et retour). La fonction signal peut maintenant être considérée comme une fonction qui prend deux paramètres (un int et un pointeur vers une fonction) et renvoie un pointeur vers une fonction.

Ceci peut également être dérivé de la déclaration d'origine, mais cela demande un peu de pratique. La manière habituelle est de commencer à l'identifiant que les noms de l'entité la plus externe (signal est ce cas):

signal is a ...

Ensuite, vous avez bien lu jusqu'à ce que vous trouviez une parenthèse fermante inégalée ou la fin de la déclaration: void (*signal(int sig, void (*func)(int))(int)

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

Vous pouvez maintenant choisir entre l'analyse des paramètres en premier ou la valeur de retour en premier. Je vais faire la valeur de retour en premier. Pour cela, vous lisez en arrière pour trouver la parenthèse ouverte correspondant: void (signal(/ ... */))(int)

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

lecture avant et en arrière de cette façon que vous obtenez à des étapes successives:

`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

Un mnémotechnique que j'ai créé il y a plusieurs années, ce qui est précieux en essayant de comprendre les types complexes:

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. 

Sauf entre parenthèses changer cette priorité, bien sûr.

application à ce cas:

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. 

Avec un peu de pratique, vous arrivez au point où vous pouvez faire tout cela à la volée:

"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. 

(Désolé Oui, ce mnémonique (avec l'implicite "sauf pour les parenthèses, bien sûr) fonctionne pour toutes les déclarations C, peu importe à quel point les pointeurs, les tableaux, une Les fonctions nd sont mélangées.

Il s'agit d'une compétence vraiment utile lorsque vous essayez de comprendre comment le code de quelqu'un d'autre fonctionne ... ou même de trouver quelque chose de votre propre que vous n'avez pas vu depuis longtemps.

Mais, oui, la meilleure façon de gérer tout ce que vous ne pensez pas que les gens seront en mesure de lire un coup d'oeil est de construire en couches avec typedefs. Les types de composants sont susceptibles d'être utiles eux-mêmes, et en le prenant une étape à la fois, les gens ne se perdent pas en essayant de déterminer quelle parenthèse correspond à laquelle. Soyez gentil avec la prochaine personne qui touche votre code!

Si vous trouvez l'utile mnémotechnique, ne hésitez pas à citer ailleurs - il suffit de me donner crédit à son auteur, s'il vous plaît.

Par ailleurs, il existe également des outils « C explicateur » qui va analyser la chaîne C delaractions et faire la conversion à la description en anglais pour vous. Le mien s'appelait CEX, pour des raisons évidentes, mais beaucoup d'autres existent et vous devriez pouvoir en trouver un si vous ne voulez pas commettre cette compétence pour wetware ou si quelqu'un vous remet quelque chose qui est vraiment trop moche pour que vous puissiez suivre.

+0

Et oui, le fait que Démarrer et Suffixe, permissif et Prefix, partager les consonnes initiales est délibérée; Cela vous aide à vous souvenir du bon ordre. – keshlam

1

Prenons un exemple de la façon dont cette déclaration nasty pourrait être utilisé:

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

Sans verbosité trop, on pourrait dire que « signal » est une fonction avec deux paramètres qui renvoie une fonction.

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

Voyons voir ce que sera la sortie:

Si la couleur = 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 la couleur = 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 ! 
Questions connexes