Este es el follow up de mi artículo previo dedicado a C (Desafío para poner a prueba tus conocimientos de C) para quienes no hayan encontrado la respuesta correcta. Espero que les guste.

¿Qué es esta maraña de paréntesis?

void (*(*f[])())()

Para entender el significado de esta declaración, hay que comenzar desde la variable f hacia afuera, teniendo mucho cuidado con el orden de precedencia con el que se evalúan los identificadores de tipos. De esta forma:

f[] es un arreglo de tamaño no especificado, y como tal significa que es un puntero al primer elemento de un arreglo.

Luego *f[] indica que se trata de un arreglo de punteros.

Lo que sigue es la declaración del tipo de dato de los punteros que contiene el arreglo. Se trata de punteros a funciones (*f[])(), que retornan punteros *(*f[])().

Finalmente, el tipo de dato de los punteros que retornan estas funciones, son funciones (*(*f[])())() que retornan void.

En definitiva, f es un arreglo (de tamaño no especificado) de punteros a funciones que retornan un puntero a una función que retorna void (lo que significa que son funciones que no retornan nada, son simples procedimientos).

void (*(*f[])())()

No es código, ¡es poesía!

Además esto funciona perfectamente, si no me creen hagamos una prueba. Primero crear el fuente cith.c (en alusión a C isn't that hard):

root@debian:/tmp# nano cith.c

Insertar el siguiente código fuente:

#include <stdio.h>
#include <stdlib.h>

void funcion1() {

  // Hacer algo útil...
  printf("Ejecutando funcion1...\n");

}

void * funcion2() {

  // Hacer algo útil
  printf("Ejecutando funcion2...\n");

  // Retornar un puntero a una función que retorna void
  return &funcion1;

}

int main() {

  // Declaro al arreglo f
  void (*(**f)())();

  // Reservo memoria para un arreglo de 10 punteros
  f = malloc(10*sizeof(void *));

/* La sección de código anterior puede reemplazarse simplemente por:

  void (*(*f[10])())();

*/

  // Guardo un puntero a funcion2 en el primer elemento del arreglo
  f[0] = &funcion2;

  // Declaro aux como puntero a una función que retorna void
  // Guardo en aux el resultado de ejecutar el primer elemento del arreglo
  void (* aux)() = f[0]();

  // Ejecuto aux
  aux();

/* A esta altura se debería haber impreso por pantalla:

Ejecutando funcion2...
Ejecutando funcion1...

*/

  // Retorno 0
  return 0;

}

 

Guardar los cambios y cerrar.

Ejecutar un elemento de un arreglo, la belleza de C.

Tal como se observa, "funcion1" es una función que retorna void (no retorna nada) y "funcion2" es una función que retorna un puntero a void (puntero de tipo no especificado).

Ahora, compilar con gcc:

root@debian:/tmp# gcc -o cith cith.c 
cith.c: In function ‘main’:
cith.c:36:8: warning: assignment from incompatible pointer type [enabled by default]

El warning es correcto pues he declarado a "funcion2" como una función que retorna un puntero de tipo no especificado (puntero a void), lo cual no coincide con el tipo exacto de "f". Pero no hay conflicto alguno, un puntero es un puntero (es una simple dirección de memoria), y todos ocupan el mismo espacio en memoria, independientemente de "a qué" apunten.

Para eliminar esta advertencia es posible definir el siguiente tipo de dato:

typedef void (*p2fvoid)();

Y cambiar el tipo de dato de retorno de la función "funcion2":

p2fvoid funcion2() {

Ahora es posible ejecutar el programa. Si todo es correcto, debería imprimir por pantalla primero el mensaje "Ejecutando funcion2..." y luego "Ejecutando funcion1...". Veamos:

root@debian:/tmp# ./cith 
Ejecutando funcion2...
Ejecutando funcion1...

¡Excelente! Y suena potable para un próximo examen de programación en C (inserte emoji de diablillo aquí).

¡Felices fiestas!


Tal vez pueda interesarte


Compartí este artículo