Comment déclarer un tableau de pointeurs de fonction constants en C?

J’ai besoin de déclarer un tableau de pointeurs vers des fonctions telles que:

extern void function1(void); extern void function2(void); ... void (*MESSAGE_HANDLERS[])(void) = { function1, function2, ... }; 

Cependant, je veux que le tableau soit déclaré comme constant – à la fois les données du tableau et le pointeur sur les données. Malheureusement, je ne me souviens pas où placer le ou les mots-clés const.

Je suppose que le pointeur actuel, MESSAGE_HANDLERS dans ce cas, est déjà constant car il est déclaré comme un tableau. D’autre part, les pointeurs de fonction dans le tableau ne pourraient-ils pas être modifiés au moment de l’exécution s’ils sont déclarés comme indiqué?

Il existe une technique pour se rappeler comment construire un tel type. Commencez par lire les pointeurs à partir de leur nom et lisez-les de droite à gauche.

Comment déclarer ce genre de choses sans aide?

Tableaux

 T t[5]; 

est un tableau de 5 T. Pour faire de T un type de fonction, écrivez le type de retour à gauche et les parameters à droite:

 void t[5](void); 

serait un tableau de 5 fonctions retournant nul et ne prenant aucun paramètre . Mais les fonctions elles-mêmes ne peuvent pas être empilées dans des tableaux! Ce ne sont pas des objects. Seuls les pointeurs vers eux peuvent.

Qu’en est-il de

 void * t[5](void); 

C’est toujours faux, car cela changerait simplement le type de retour en pointeur d’annulation. Vous devez utiliser des parenthèses:

 void (*t[5])(void); 

et cela fonctionnera réellement. t est un tableau de 5 pointeurs vers des fonctions renvoyant void et ne prenant aucun paramètre .

Génial! Qu’en est-il d’un tableau de pointeurs sur Arras? C’est très similaire. Le type d’élément apparaît à gauche et la dimension à droite. De nouveau, les parenthèses sont nécessaires car sinon, le tableau deviendrait un tableau multidimensionnel de pointeurs entiers:

 int (*t[5])[3]; 

C’est tout! Un tableau de 5 pointeurs sur les tableaux de 3 int .

Qu’en est-il des fonctions?

Ce que nous venons d’apprendre est également vrai pour les fonctions. Déclarons une fonction prenant un int qui renvoie un pointeur sur une autre fonction ne prenant aucun paramètre et renvoyant void:

 void (*f(int))(void); 

nous avons encore besoin de parenthèses pour les mêmes raisons que ci-dessus. Nous pouvons maintenant l’appeler et appeler la fonction renvoyée qui est redirigée.

 f(10)(); 

Renvoyer un pointeur à une fonction renvoyer un autre pointeur à une fonction

Et ça?

 f(10)(true)(3.4); 

? En d’autres termes, comment une fonction qui prend un int renvoyant un pointeur à une fonction prenant un bool renvoyant un pointeur à une fonction prenant un double et renvoyant un vide pourrait-elle ressembler? La réponse est que vous venez de les imbriquer:

 void (*(*f(int))(bool))(double); 

Vous pourriez le faire des temps interminables. En effet, vous pouvez également renvoyer un pointeur sur un tableau, tout comme vous pouvez le faire sur une fonction:

 int (*(*f(int))(bool))[3]; 

Ceci est une fonction prenant int renvoyant un pointeur à une fonction prenant bool renvoyant un pointeur à un tableau de 3 int

Qu’est-ce que cela a à voir avec const?

Maintenant que ce qui a été expliqué ci-dessus explique comment construire des types plus complexes à partir de types fondamentaux, vous pouvez placer const aux endroits où vous savez maintenant à quoi ils appartiennent. Il suffit de considérer:

 T c * c * c ... * c name; 

Le T est le type de base que nous finissons par désigner. Le c représente soit const ou non const. Par exemple

 int const * const * name; 

déclarera name comme ayant le type pointeur sur un pointeur constant sur une constante int . Vous pouvez changer le name , mais vous ne pouvez pas changer le *name , qui serait de type

 int const * const 

et ni **name , qui serait de type

 int const 

Appliquons ceci à un pointeur de fonction ci-dessus:

 void (* const t[5])(void); 

Cela déclarerait en fait que le tableau contiendrait des pointeurs constants. Ainsi, après la création (et l’initialisation) du tableau, les pointeurs sont constants, car le const est apparu après l’écanvas. Notez que nous ne pouvons pas mettre un const avant l’écanvas dans ce cas, car il n’y a pas de pointeur sur les fonctions constantes . Les fonctions ne peuvent tout simplement pas être constantes car cela n’aurait aucun sens. Donc ce qui suit n’est pas valide:

 void (const * t[5])(void); 

Conclusion

La façon de déclarer des fonctions et des tableaux en C ++ et en C est en fait un peu déroutante. Vous devez d’abord vous y familiariser, mais si vous le comprenez, vous pouvez écrire des déclarations de fonction très compactes à l’aide de cette déclaration.

cdecl dit:

 cdecl> explain void (* const foo[])(void) declare foo as array of const pointer to function (void) returning void 

Est-ce ce dont vous avez besoin?

Dans des situations comme celle-ci, faites un typedef pour nommer votre signature de fonction, cela simplifie beaucoup:

 typedef void MESSAGE_HANDLER(void); 

avec cela en place, il devrait être juste:

 MESSAGE_HANDLER * const handlers[] = { function1, function2 }; 

Pour obtenir le contenu réel du tableau constant.

EDIT : Suppression d’une partie du pointeur de la typedef , c’est vraiment mieux (vivre et apprendre).

Avec VisualStudio 2008, je reçois:

 void (* const MESSAGE_HANDLERS[])(void) = { NULL, NULL }; int main () { /* Gives error '=' : left operand must be l-value */ MESSAGE_HANDLERS = NULL; /* Gives error l-value specifies const object */ MESSAGE_HANDLERS[0] = NULL; } 

Je ne sais pas si cela fonctionnera en ‘C’. cela fonctionne en ‘C ++’:

  • Commencez par définir MESSAGE_HANDLERS en tant que type:

    typedef void (*MESSAGE_HANDLER)();

  • Ensuite, utilisez la définition de type pour déclarer votre tableau comme une constante:

    MESSAGE_HANDLER const handlers[] = {function1, function2};

Le truc est dans le typedef , si vous pouvez faire la même chose en “C” sémantiquement, ça devrait marcher aussi.