Pointeurs de fonction facile à déclarer une fois que vous avez la base declarators:
- id:
ID
: ID est un
- Pointeur:
*D
: D pointeur vers
- Fonction:
D(<parameters>)
: D fonction prenant <
paramètres>
de retour
Alors que D est une autre déclaration construit à l'aide de ces mêmes règles. En fin de compte, quelque part, il se termine par ID
(voir ci-dessous pour un exemple), qui est le nom de la déclaration de l'entité. Nous allons essayer de construire une fonction prenant un pointeur vers une fonction prend rien et le retour de type int, et retournant un pointeur à une fonction prenant un char et le retour de type int. Avec type-defs c'est comme ça
typedef int ReturnFunction(char);
typedef int ParameterFunction(void);
ReturnFunction *f(ParameterFunction *p);
Comme vous le voyez, il est assez facile de le construire à l'aide de typedefs. Sans typedefs, il n'est pas difficile, soit avec la déclaration de règles, appliquées de manière cohérente. Comme vous le voyez j'ai raté la partie sur laquelle pointe le pointeur, et la chose la fonction retourne. C'est ce qui apparaît à l'extrême gauche de la déclaration, et n'est pas d'intérêt: Il est ajouté à la fin si l'on construit le constate déjà. Faisons-le. Mettre en place de manière cohérente, premier long - montrant la structure à l'aide de [
et ]
:
function taking
[pointer to [function taking [void] returning [int]]]
returning
[pointer to [function taking [char] returning [int]]]
Comme vous le voyez, on peut décrire un type complètement en ajoutant declarators l'une après l'autre. La Construction peut se faire de deux façons. On est bottom-up, à commencer par la très bonne chose (les feuilles) et sur le même chemin jusqu'à l'identificateur. L'autre façon est de haut en bas, en commençant à l'identifiant, le travail de la manière vers le bas pour les feuilles. Je vais vous montrer deux façons.
De Bas En Haut
La Construction commence par la chose à la droite: La chose retourné, ce qui est la fonction la prise de char. Pour garder la declarators distinctes, je vais les numéroter:
D1(char);
Inséré le char paramètre directement, puisqu'il est trivial. L'ajout d'un pointeur de déclaration par le remplacement, D1
par *D2
. Notez que nous avons pour envelopper les parenthèses autour de *D2
. Ce qui peut être connu par la recherche de la priorité de l' *-operator
et la fonction d'appel de l'opérateur ()
. Sans notre parenthèses, le compilateur pourrait le lire comme *(D2(char p))
. Mais ce ne serait pas un simple remplacement de D1 en *D2
plus, bien sûr. Les parenthèses sont toujours autorisés autour de declarators. Afin de ne pas faire quelque chose de mal si vous ajoutez trop d'entre eux, en fait.
(*D2)(char);
Le type de retour est complet! Maintenant, nous allons remplacer D2
par la fonction de demande de déclaration de fonction prenant <parameters>
de retour, qui est - D3(<parameters>)
qui nous sommes à maintenant.
(*D3(<parameters>))(char)
Notez que les parenthèses sont nécessaires, car nous voulons D3
à une fonction de demande de déclaration et non pas un pointeur déclaration de ce temps. La grande, la seule chose qui reste est les paramètres pour cela. Le paramètre est en fait exactement la même chose que nous avons fait le type de retour, juste avec char
remplacé par void
. Donc je vais le copier:
(*D3( (*ID1)(void)))(char)
J'ai remplacé D2
par ID1
, puisque nous en avons terminé avec ce paramètre (c'est déjà un pointeur vers une fonction - pas besoin d'une autre demande de déclaration). ID1
"sera le nom du paramètre. Maintenant, j'ai dit ci-dessus à la fin on ajoute le type qui tous ceux déclaration de modifier, l'une apparaissant à l'extrême gauche de chaque déclaration. Pour les fonctions, qui devient le type de retour. Pour les pointeurs le fait de taper etc... C'est intéressant lorsqu'il est écrit en bas de la type, il apparaîtra dans l'ordre inverse, à droite :) de toute façon, en le substituant les rendements de la déclaration complète. Les deux fois, int
de cours.
int (*ID0(int (*ID1)(void)))(char)
J'ai appelé l'identificateur de la fonction ID0
dans cet exemple.
De Haut En Bas
Cela commence à l'identifiant le plus à gauche dans la description du type, de l'emballage, qui constate que nous marchons notre chemin à travers le droit. Commencez avec fonction prenant <
paramètres>
de retour
ID0(<parameters>)
La prochaine chose que dans la description (d'après "le retour") a été pointeur. Nous allons l'intégrer:
*ID0(<parameters>)
Ensuite, la prochaine chose était due à la prise de <
paramètres>
de retour. Le paramètre est un simple char, afin de nous mettre tout de suite à nouveau, car il est vraiment trivial.
(*ID0(<parameters>))(char)
Notez les parenthèses, nous avons ajouté, depuis que nous voulons plus que l' *
lie d'abord, et ensuite l' (char)
. Sinon, il serait de lire en fonction prenant <
paramètres>
le retour de fonction .... Rex, fonctions retournant des fonctions qui ne sont même pas autorisés.
Maintenant nous avons juste besoin de mettre <
paramètres>
. Je vais vous montrer une version courte de la deriveration, car je pense que vous avez déjà maintenant avoir l'idée de comment le faire.
pointer to: *ID1
... function taking void returning: (*ID1)(void)
Vient de mettre int
avant la declarators comme nous l'avons fait avec bottom-up, et nous sommes finis
int (*ID0(int (*ID1)(void)))(char)
La bonne chose
Est bottom-up ou top-down de mieux? J'ai l'habitude de bottom-up, mais certaines personnes peuvent être plus à l'aise avec de haut en bas. C'est une question de goût je pense. D'ailleurs, si vous appliquez tous les opérateurs dans cette déclaration, vous finirez toujours par un entier (int):
int v = (*ID0(some_function_pointer))(some_char);
C'est une belle propriété de déclarations en C: La déclaration affirme que si ces opérateurs sont utilisés dans une expression à l'aide de l'identifiant, puis il donne le type de la plus à gauche. C'est comme ça pour les tableaux de trop.
Espère que vous avez aimé ce petit tutoriel! Maintenant, nous pouvons lien vers cette quand les gens s'interroger sur l'étrange syntaxe de déclaration de fonctions. J'ai essayé de mettre un peu de C internals que possible. N'hésitez pas à modifier/corriger les choses.
40 votes
Aussi : Pour une analyse un peu plus approfondie des pointeurs en C, voir blogs.oracle.com/ksplice/entry/the_ksplice_pointer_challenge . Aussi, La programmation depuis la base montre comment ils fonctionnent au niveau de la machine. Comprendre Le "modèle de mémoire" de C est très utile pour comprendre le fonctionnement des pointeurs en C.
9 votes
Excellente information. D'après le titre, je m'attendais à voir une explication du fonctionnement des "pointeurs de fonction", et non de leur codage :)
2 votes
La réponse suivante est plus courte et beaucoup plus facile à comprendre : stackoverflow.com/a/142809/2188550