¿Cómo se declara una matriz de punteros de función constante en C?

Necesito declarar una serie de punteros a funciones como esta:

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

Sin embargo, quiero que la matriz se declare como constante, tanto los datos de la matriz como el puntero a los datos. Desafortunadamente, no recuerdo dónde colocar la (s) palabra (s) clave (s) const.

Supongo que el puntero real, MESSAGE_HANDLERS en este caso, ya es constante porque se declara como una matriz. Por otro lado, ¿no podrían los punteros de función dentro de la matriz cambiarse en tiempo de ejecución si se declaran como se muestra?

Hay una técnica para recordar cómo construir ese tipo. Primero intente leer los punteros que comienzan con su nombre y leer de derecha a izquierda.

¿Cómo declarar esas cosas sin ayuda?

Arrays

 T t[5]; 

Es una matriz de 5 T. Para hacer de T un tipo de función, escribe el tipo de retorno a la izquierda y los parámetros a la derecha:

 void t[5](void); 

sería una matriz de 5 funciones que devuelven el vacío y no toman parámetros . ¡Pero las funciones en sí no se pueden rellenar en matrices! No son objetos. Sólo los punteros a ellos pueden.

Qué pasa

 void * t[5](void); 

Eso sigue siendo incorrecto, ya que solo cambiaría el tipo de retorno para que sea un puntero para anular. Tienes que usar paréntesis:

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

y esto realmente funcionará. t es una matriz de 5 punteros a funciones que devuelven el vacío y no toman parámetros .

¡Genial! ¿Qué pasa con una serie de punteros a arras? Eso es muy similar. El tipo de elemento aparece a la izquierda y la dimensión a la derecha. Nuevamente, se necesitan paréntesis porque, de lo contrario, la matriz se convertiría en una matriz multidimensional de punteros enteros:

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

¡Eso es! Una matriz de 5 punteros a matrices de 3 int .

¿Qué pasa con las funciones?

Lo que acabamos de aprender también es cierto acerca de las funciones. Declaremos una función que toma un int que devuelve un puntero a otra función que no toma ningún parámetro y devuelve un vacío:

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

Necesitamos paréntesis de nuevo por la misma razón que la anterior. Ahora podríamos llamarlo y llamar a la función devuelta apuntada de nuevo.

 f(10)(); 

Devolver un puntero a la función devolver otro puntero a la función

Que hay de esto

 f(10)(true)(3.4); 

? En otras palabras, ¿cómo se vería una función que toma int devolviendo un puntero a una función tomando bool devolviendo un puntero a una función doblando y devolviendo el vacío ? La respuesta es que simplemente los anidan:

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

Podrías hacerlo tiempos interminables. De hecho, también puede devolver un puntero a una matriz al igual que puede hacerlo a una función:

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

Esta es una función que toma int devolviendo un puntero a una función tomando bool devolviendo un puntero a una matriz de 3 int

¿Qué tiene que ver con const?

Ahora que lo anterior explica cómo construir tipos más complejos a partir de tipos fundamentales, puede colocar const en lugares donde ahora sabe a dónde pertenecen. Solo considera:

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

La T es el tipo básico que terminamos apuntando al final. La c significa const o no const. Por ejemplo

 int const * const * name; 

declarará nombre para tener el puntero de tipo a un puntero constante a una constante int . Puede cambiar el name , pero no puede cambiar *name , que sería de tipo

 int const * const 

y tampoco **name , que sería de tipo

 int const 

Apliquemos esto a un puntero de función de arriba:

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

Esto declararía la matriz para contener punteros constantes. Así que después de crear (e inicializar) la matriz, los punteros son const, porque la const apareció después de la estrella. Tenga en cuenta que no podemos poner una const delante de la estrella en este caso, ya que no hay punteros a funciones constantes . Las funciones simplemente no pueden ser constantes ya que eso no tendría sentido. Así que lo siguiente no es válido:

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

Conclusión

La forma en que C ++ y C de declarar funciones y matrices en realidad es un poco confusa. Primero debes entenderlo, pero si lo entiendes, puedes escribir declaraciones de funciones muy compactas usándolo.

cdecl dice:

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

¿Es lo que necesitas?

En situaciones como esta, haga un typedef para nombrar su firma de función, que lo hace mucho más simple:

 typedef void MESSAGE_HANDLER(void); 

Con eso en su lugar, debería ser justo:

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

Para obtener el contenido real de la constante de matriz.

EDITAR : Se eliminó la parte del puntero de typedef , esto realmente es mejor (vivir y aprender).

Con VisualStudio 2008, obtengo:

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

No estoy seguro de si esto funcionará en ‘C’. funciona en ‘C ++’:

  • Primero defina MESSAGE_HANDLERS como un tipo:

    typedef void (*MESSAGE_HANDLER)();

  • Luego, usa la definición de tipo para declarar tu matriz como una constante:

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

El truco está en el typedef , si puede hacer lo mismo semánticamente en ‘C’, también debería funcionar.