Lenguaje C: ¿Un programa sin función main?

En este artículo vamos a jugar con algunos conceptos de programación en lenguaje C, para que nuestro código C corra sin declarar la función main.


Analicemos el siguiente segmento de código escrito en Lenguaje C:

Ahora compilemos y veamos si corre:

Funcionó correctamente… ahora la pregunta es: ¿Puede compilar y enlazar un código escrito en Lenguaje C sin función main?

La respuesta es SI… y NO, simultáneamente 😀

Todo código escrito en lenguaje C debe poseer una función main para que pueda enlazarse y ejecutarse, ya que de otra forma no hay compilador que pueda generar un binario ejecutable válido… de eso no hay duda, es de manual.

Lenguaje C: analizando las macrosc clang lenguaje c programming programacion

Entonces… ¿por qué el código anterior sí se ejecuta sin problemas? Este código claramente no tiene una función main definida explícitamente, y sin embargo funciona sin ningún inconveniente.

Y la respuesta está en las macros definidas (#define) en las primeras líneas de código.

Las macros creadas con la directiva de preprocesamiento #define son la clave del funcionamiento de este código.

¿Qué es lo que hacen estas macros?

Las directivas de preprocesamiento, como todo programador C/C++ sabe, permiten ejecutar líneas de código de preprocesador antes de pasar a la compilación y posterior enlace/link de nuestro programa con las bibliotecas del sistema.

El operador ‘##’ permite, a nivel de preprocesamiento, “pegar” tokens o contenidos entre si, antes de compilar. Así podemos “unir” dos o más caracteres entre si, formando nuevas palabras que serán compiladas en el proceso de compilación.

Analicemos la línea 2 del programa:

#define decodificar(j,u,n,c,o,T,I,C) c##j##n##u

¿Qué hace el preprocesador con esta directiva?

Simplemente “concatena” cuatro caracteres para realizar la sustitución. De los argumentos que recibe la macro-función decodificar, a saber, “j”, “u”, “n”, “c”, “o”, “T”, “I”, y “C”, concatena el cuarto carácter (“c”) con el primero (“j”), luego el tercero (“n”), y por último el segundo (“u”), formando la palabra “cjnu”.

Ahora miremos la tercer línea de código:

#define mensaje decodificar(a,n,i,m,a,r,s,e)

En esta línea el preprocesador reemplaza la palabra “mensaje” con la expresión decodificar(a, n, i, m, a, r, s, e). De acuerdo con la macro definida en la línea anterior, el argumento debe ser expandido en la concatenación de los caracteres 4to, 1ro, 3ro y 2do. Aquí, en la serie de argumentos de la macro decodificar, es decir, en (a, n, i, m, a, r, s, e), los caracteres 4to, 1ro, 3ro y 2do son, respectivamente, “m”, “a”, “i”, y “n”.

Al concatenarlos, sí, obtenemos “main”, de modo que la línea “int mensaje” va a ser reemplazada, a nivel de preprocesamiento (antes de pasar a la compilación) como “int main“.

¿Y si pre-procesamos a ver qué sale?

Imaginemos que pudiéramos pre-procesar el código anterior, y ver qué es lo que realmente se está compilando… bah, dejémonos de imaginar, y pre-procesemos, sin compilar, el código… veamos la magia:

diego@cryptos:/tmp$ gcc -E prueba_main.c -o prueba_main.i

El código resultante, prueba_main.i, es el programa pre-procesado y previo a la compilación. Esto lo obtenemos gracias al modificador “-E” del compilador GCC.

La salida será extensa, principalmente por la inclusión de la cabecera stdio.h… pero si vemos al final del código qué es lo que tenemos, vamos a apreciar algo similar a esto:

Sí señores, el código que va a compilarse finalmente sí tiene una función main declarada.

Conclusión

Como conclusión, no existe código funcional en lenguaje C que pueda compilar, enlazar y correr sin una función main. En este ejemplo hemos ocultado la definición del main utilizando un juego de directivas de preprocesamiento y sustituciones, que transforman la palabra “mensaje” en “main”.