Pregunta

Necesito declarar una matriz 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 en la matriz como el puntero a los datos. Desafortunadamente, no recuerdo dónde colocar las palabras clave 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 cambiarse los punteros de función dentro de la matriz en tiempo de ejecución si se declara como se muestra?

¿Fue útil?

Solución

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

¿Cómo declarar esas cosas sin ayuda?

Matrices

T t[5];

es una matriz de 5 T . Para hacer que T sea un tipo de función, escriba 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 vacío y no toman parámetros . ¡Pero las funciones en sí no se pueden rellenar en matrices! No son objetos. Solo los punteros a ellos pueden.

¿Qué pasa con

?
void * t[5](void);

Eso todavía está mal, ya que simplemente cambiaría el tipo de retorno para que sea un puntero a 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 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 sobre 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 void:

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

necesitamos paréntesis nuevamente por la misma razón que arriba. Ahora podríamos llamarlo, y llamar a la función devuelta apuntada nuevamente.

f(10)();

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

¿Qué pasa con esto?

f(10)(true)(3.4);

? En otras palabras, ¿cómo se vería una función tomando int devolviendo un puntero a una función tomando bool regresando un puntero a una función tomando double y regresando void ? La respuesta es que simplemente los anidas:

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

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

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

Esta es una función que toma int devolviendo un puntero a una función que toma 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 considere:

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

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

int const * const * name;

declarará que el nombre tiene el puntero tipo a un puntero constante a un int constante . Puede cambiar name , pero no puede cambiar * name , que sería del tipo

int const * const

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

int const

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

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

Esto realmente declararía que la matriz contiene punteros constantes. Entonces, después de crear (e inicializar) la matriz, los punteros son constantes, porque el const apareció después de la estrella. Tenga en cuenta que no podemos poner un const antes 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. Por lo tanto, lo siguiente no es válido:

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

Conclusión

La forma en C ++ y C de declarar funciones y matrices en realidad es un poco confusa. Primero debe entenderlo, pero si lo comprende, puede escribir declaraciones de funciones muy compactas utilizándolo.

Otros consejos

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 la firma de su función, lo que lo hace mucho más simple:

typedef void MESSAGE_HANDLER(void);

con eso en su lugar, debería ser solo:

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

Para obtener el contenido real de la matriz constante.

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

Con Visual Studio 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, use la definición de tipo para declarar su matriz como una constante:

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top