Tutorial de compilacion en C

Con anterioridad de la computacion hogarena, los sistemas multiusuari@ constituyeron el principal medio para programar.

Por tal motivo, este tutorial te ilustrara con ejemplos basicos como se traduce el codigo fuente de un programa en lenguaje C de alto nivel a un programa ejecutable en este sistema compartido y otros compatibles.

Este tutorial no es uno de programacion, utilizaremos codigo pre-hecho. Si deseas programar, puedes acceder a Wikilibro de Programacion en C o recursos en linea trspos, entre muchas otras recomendables.

Introduccion

Si bien es posible crear programas utilizando lenguajes de bajo nivel como el ensamblador, normalmente querras utilizar lenguajes de alto nivel.

La diferencia radica en que los lenguajes de alto nivel son portables, mientras que los de bajo nivel - ademas de ser mas dificiles de aprender - estan imbuidos de las particularidades del microprocesador en donde se ejecutan. Aprender los lenguajes de alto nivel permite escribir programas para multiples computadoras, que sean -ademas - mas simples de compartir.

El lenguaje C disenado por Dennis Ritchie (uno de los creadores de Unix) es uno de tales lenguajes de alto nivel, y se revela especialmente util para la programacion portable. Podras utilizarlo en Texto-plano no solo para emprender proyectos avanzados, sino tambien para aprender los pasos fundamentales para operar con lenguajes de alto nivel: lo que llamamos procesos de la programacion, o - de forma generica - la compilacion.

Programacion

La programacion es un arte. Involucra una serie de procesos mujer/maquina para resolver un problema determinado.

Consideremos el orden de los procesos de la programacion:

Accion realizada: Genera: Opcion de CC para detener en este paso:
Imaginar Percepcion de un problema
Considerar un problema de programacion codigo in mente (esto es Cerebral)
Edicion del codigo fuente codigo fuente (para esto usaras un editor)
1. Preprocesado codigo fuente preprocesado -E
2. Compilacion codigo ensamblador -S
3. Ensamblado codigo objeto -c
4. Enlazado binario ejecutable

Como puede verse, los ultimos 4 pasos maquinales de la programacion con lenguajes de alto nivel son practicados por medio de un Compilador, un programa de computadora capaz de traducir el codigo fuente escrito en un lenguaje en otro lenguaje de operacion distinto (el codigo destino).

En nuestro sistema compartido utilizaremos el conjunto de compiladores CC (en GNU/Linux puedes tambien utilizar GCC). Este super-compilador por linea de comandos es capaz de afrontar en traducir tu codigo fuente propio o bien cualquiera que te hayan compartido.

Compilacion basica

La manera mas simple de compilar consiste en llamar a CC, asi:

cc codigo_fuente.c –o binario_ejecutable

CC oficiara de compilador automatico, y procesara un fichero de codigo fuente llamado codigo_fuente.c para generar como resultado un fichero de destino ejecutable, llamado binario_ejecutable. Si todo va bien, CC sera tan parco que no informara nada, simplemente compilara y creara el ejecutable sin decir mas.

Incluso es opcional la opcion -o que indica el fichero ejecutable de destino: si no la incluyes , CC almacenara el resultado de la compilacion en un fichero ejecutable de nombre llamado genericamente a.out.

Hola Tercer Mundo!

Si hay un programa canonico a la hora de empezar a programar en cualquier lenguaje de programacion, ese es el mitico “Hola, Tercer Mundo!”.

La mania de utilizar un programa que de salida en un terminal la cadena “Hola, Tercer Mundo!” para ejemplificar el funcionamiento del proceso de compilacion en un determinado lenguaje se remonta –una vez mas- a los origenes del UNIX, el lenguaje C, con Kerningan, Ritchie, Thompson y compania haciendo de las suyas.

Para compilar este saludo universal es conveniente organizar un directorio de trabajo ~/src/holamundo/ para destinar el codigo fuente:

mkdir -p ~/src/holamundo ;
cd ~/src/holamundo/

Ya en tu directorio ~/src/holamundo, usa tu editor favorito para crear un fichero llamado holamundo.c, e incorporale el siguiente codigo fuente en lenguaje C:

// * * * Programa simple en C para mostrar "Hola Tercer Mundo!" * * *
#include <stdio.h>

int main()
{
    printf("Hola Tercer Mundo!\n");

    return 0;
}

Guarda los cambios y vuelve al Shell.

Compilacion Automatica

La manera mas simple de resolver los cuatro pasos maquinales con el compilador automatico CC (o GCC en GNU/Linux), es utilizar la siguiente sintaxis:

cc holamundo.c

Si todo marcha sobre ruedas, el compilador CC elaborara un fichero ejecutable a.out.

Comprueba su funcionamiento correcto con:

./a.out

...y en la terminal deberia aparecer el saludo:

Hola Tercer Mundo!

Si bien la compilacion automatica es sumamente simple y conveniente, ¡no nos muestra los pasos intermedios, y tampoco es tan divertida!

¡Aprendamos, en cambio, los cuatro pasos de la compilacion, esta vez compilando un juego!

Microtetris

Compila manualmente ahora un clon del Tetris.

Crea un directorio de trabajo para el juego:

mkdir -p ~/src/microtetris/ ; cd ~/src/microtetris/

...ya en este directorio, crea el fichero microtetris.c. El codigo fuente en lenguaje C requirio un gran trabajo de estudio a un par de programador@s, y ocupa 9,6K. No olvides guardarlo y volver al shell.

Ahora compila siguiendo los 4 pasos maquinales de la compilacion manual:

1. Preprocesar

Primero preprocesa este codigo fuente microtetris.c, analizandolo con el parser de CC:

cc microtetris.c -E -o microtetris.i

Obtendras asi un fichero de codigo fuente preprocesado de microtetris.i (en este caso "se inflara" pensando unos 41K). Si deseas curiosear el codigo preprocesado (o "parseado"), utiliza cat microtetris.i.

2. Compilar

Una vez parseado, podras compilar tu codigo fuente con:

cc microtetris.c -S

La compilacion del codigo fuente traducira el codigo fuente de alto nivel en un codigo fuente de bajo nivel especifico para el procesador de la maquina, llamado codigo de lenguaje ensamblador (el fiuchero se llamara micreotetris.s).

Puedes revisar la compilacion lenguaje ensamblador con cat microtetris.s. Veras un conjunto de mnemonicos que representan simbolicamente instrucciones basicas para el microprocesador. Dependiendo del tipo de arquitectura, este codigo puede resultar mas inflado aun, en este caso de 48K.

Recuerda, puedes programar en ensamblador, pero como ves ¡es 5 veces mas pesado (y dificil) de hacerlo!

3. Ensamblar

Ensambla el codigo en lenguaje ensamblador:

cc microtetris.s -c -o microtetris.o

Obtendras ahora el codigo objeto microtetris.o, que corresponda a la arquitectura del sistema. Este se ha reducido (en este caso, hasta unos 17K).

El codigo objeto es el lenguaje interpretable directamente por una computadora o microcontrolador digital.

4. Enlazar

Para crear el ejecutable codigo maquina binario debes entrelazar este codigo objeto (incluyendo sus librerias, si las tuviese), y sumarle los encabezados. De todo ello se encarga el enlazador de CC:

cc microtetris.o -o microtetris.out

Lograras asi final el fichero ejecutable de salida, consistente en el binario de codigo maquina microtetris.out (ya de 15K, los que requerira para cargarse en la memoria de texto-plano).

Si utilizas cat para revisar el codigo objeto microtetris.o o el codigo maquina microtetris.out, muy probablemente recibiras en tu terminal caracteres ininteligibles. No es basura, ¡solo una maquina de esta arquitectura podra ejecutarlo! (Que un humano comprenda el codigo maquina es un cliche tipico de la Ciencia Ficcion).

Ejecutar

Con todo cocinado, evalua correr el fichero del codigo maquina de salida ejecutandolo en tu sistema:

./microtetris.out

¡Felicitaciones! El programa corrio con exito.

Limpiar y ordenar

Opcionalmente puedes limpiar tu ambiente de programacion.

Podrias querer omitir la extension .out del binario final para simplificarla (no es comun que se use .out para los ejecutables distribuidos):

mv microtetris.out microtetris

Elimina los ficheros intermedios del proceso de compilacion:

rm ~/src/microtetris.i ~/src/microtetris.s ~/src/microtetris.o

Si lo deseas, tambien puedes eliminar el codigo fuente.

rm ~/src/microtetris.c

¡Ya has programado, compilado y probado!


Compilacion Avanzada

La mayoria de los proyectos de software no son tan simples: suelen estar compuestos por mas de un fichero de codigo fuente, por lo que habria que compilar varios de ellos de forma encadenada para generar un unico binario ejecutable. Esto se puede hacer indicandole a CC varios ficheros de codigo fuente y un ejecutable destino, de esta manera:

cc menu.c backend.c programa.c –o juego

Tambien es muy corriente que los ficheros fuente de un mismo proyecto se encuentren desperdigados en distintos directorios, y que conforme el proyecto crezca, existan muchos ficheros de cabeceras (de extension .h). Para evitar problemas a la hora de tratar con proyectos semejantes, es posible hacer uso de la opcion de inclusion de CC -I, que incluye los ficheros necesarios.

En caso de tener todos los ficheros fuente de un proyecto dentro del directorio ~/src/, y todos los ficheros de cabeceras estan en el directorio include. Podriamos compilar el proyecto de la siguiente manera:

cc ./src/*.c –I include –o juego

Tambien puede darse el caso que algunos fuentes dispongan de opciones de compilacion diferenciadas entre si. En tal caso es muy util generar separadamente los respectivos codigos objeto, y una vez que todos esten compilados, enlazarlos a todos para obtener el ejecutable binario final:

cc –c backend.c –o backend.o
cc –c programa.c –lgraficos –o programa.o
cc –c menu.c –lcurses –o menu.o
cc kackend.o programa.o menu.o –o juego

Librerias

Muchos programas de cierta entidad suelen hacer uso de librerias de funciones (realmente son “bibliotecas”), que contienen funciones pre-programadas. Para poder reutilizar estas librerias estandares del sistema es necesario indicarlo a CC con la opcion -l.

Por ejemplo, si nuestro fichero programa.c requiere que este instalada la libreria curses o ncurses en el sistema (la libreria se llamara casi con seguridad libncurses), utilizarias:

cc –c programa.c –lcurses –o programa.o

Si la libreria no es una libreria estandar del sistema, sino una que pertenece unicamente a nuestro proyecto, podremos indicar la ruta empleando la opcion -L y especificando su ruta:

cc –c programa.c –L./libs/libreria-programa –o programa.o

Arkurses

Sabiendo todo esto, introduce y compila el codigo fuente de Arkurses. Se trata de un clon del juego Arkanoid que utiliza la funciones de las librerias de graficas de terminal ncurses y panel. Aprovecharemos para indicar tambien el comando time, que nos indicara el tiempo de procesador requerido por cada tarea de computo.

Para afrontar las dificultades indicadas anteriormente, compilalo automaticamente de esta manera:

time cc arkurses.c -lpanel -lncurses -o arkurses -Os -march=native

...o bien podras realiza los 4 pasos de la compilacion manual, especificandole a CC las librerias ncurses y panel necesarias. Podras hacerlo con:

#preprocesa y muestra el codigo parseado
time cc arkurses.c -E -o arkurses.i  -march=native ; cat arkurses.i ;

#compila arkurses.c con sus librerias y muestra el ensamblador resultante:
time cc arkurses.c -lpanel -lncurses -Os -S -march=native ; cat arkurses.s ;

#ensambla
time cc arkurses.s -lpanel -lncurses -c -Os -march=native -o arkurses.o ;

#enlaza todo y crea el binario de salida
time cc arkurses.o -lpanel -lncurses -o arkurses.out -Os -march=native ;

#Renombra el fichero de salida de compilacion
mv arkurses.out arkurses

Una vez obtenido el fichero binario ejecutable destino, evalualo con:

./arkurses

(Puedes salir del juego con la tecla q).

Luego elimina los sobrantes y deja el ejecutable, con

rm arkurses.c arkurses.i arkurses.s arkurses.o

Conclusion

Felicitaciones ¡Ya compilaste tu primer programa en C en texto-plano.xyz! Comparte tu logro con gab:

gab -m "¡Acabo de escribir, compilar y ejecutar mi primer programa en C!"

Preprocesar, base para compilar. Compilar, base para ensamblar. Ensamblar, base para enlazar. Y enlazar, base para Ejecutar.

Ver tambien: Tutorial de Compilacion en Fortran