Compilación de programas en C

Author
Por Darío Rivera
Publicado el en Lenguaje C

En un post anterior vimos cómo crear nuestro primer programa en C, el clásico Hola Mundo!. Sin embargo, vimos una manera muy sencilla de compilar y ejecutar dicho programa. El día de hoy veremos un poco más a fondo cómo compilar programas en lenguaje C.

Proceso de compilación

Antes que nada veamos un poco el proceso mediante el cual un programa se convierte en un ejecutable en C.

diagrama compilación

Todo comienza con la edición del programa mediante un editor con el cual guardamos el archivo con extensión .c. Este archivo es el punto de entrada para el compilador el cual lo convierte en un archivo objeto con extensión .o o .obj. Finalmente el enlazador o editor de enlace, une en un único archivo ejecutable los módulos que forman parte del programa y las bibliotecas del compilador de C que el programa utilice. Generalmente si no se especifica la extensión será un archivo .out.

Adicionalmente, este archivo puede ser tomado por el depurador para hacer debug cuando obtenemos resultados inesperados o cuando queremos verificar que todo está bien ejecutando el programa paso a paso.

El proceso de compilación suele ser un poco más complejo que el mostrado anteriormente. Cuando se invoca al compilador, el preprocesador es el encargado (antes de compilar) de procesar las directrices del compilador. Esto es, todas las líneas que empiezen con el caracter #. Una vez hecho esto, se pasa el archivo al compilador para realizar el proceso de compilación.

diagrama compilación

En términos prácticos, el preprocesador actúa como un primer filtro antes de que el compilador haga su trabajo. Por ejemplo, cuando en C escribimos #include <stdio.h>, el preprocesador sustituye esa línea por el contenido completo del archivo de cabecera correspondiente, de modo que el compilador ya tenga disponibles todas las funciones declaradas allí. Gracias a estas transformaciones previas, el compilador recibe un archivo ya adaptado con todas las sustituciones y configuraciones necesarias para generar el programa final.

Instalación del compilador

La mayoría de sistemas Linux cuentan con compiladores de C y C++. En sistemas basados en GNU/Linux Debian basta ejecutar el siguiente comando para instalar el compilador GCC.

sudo apt-get install gcc

Si utilizas Ubuntu puedes seguir este tutorial que te da todo lo necesario para empezar con C.

- Empezar con C en Ubuntu 22.04 (Jammy Jellyfish)

Compilación sencilla

Recordemos que para compilar un programa solo basta ejecutar el comando de compilación, en nuestro caso gcc y espeficar el nombre del archivo.

gcc program.c

Esto creará por defecto el archivo de salida llamado a.out. Para especificar el nombre del archivo de salida podemos utilizar la opción -o.

gcc program.c -o program

En donde program.c es el archivo con código C y program será el archivo compilado que será generado. Aún cuando nuestro programa tenga referencias a otros archivos de cabecera solo necesitaremos compilar nuestro archivo principal. Ejemplo:

main.c

#include <stdio.h>
#include "variables.c"

int main()
{
    printf("PI is %.4f\n", pi);
    return 0;
}

variables.c

float pi = 3.1516;

Este programa hace un include del archivo variables.c. Lo que hace el preprocesador en estos casos es incluir el contenido del archivo en nuestro archivo principal para procesarlo como si fuera uno solo. Entender esto es muy importante, ya que en programas realmente grandes la compilación completa puede volverse un proceso más complejo y demandante, lo que nos motiva a organizar mejor el código en módulos y aprovechar herramientas que faciliten su mantenimiento.

Compilación de múltiples archivos

La compilación de múltiples archivos ocurre cuando agregamos archivos de cabecera que generalmente provienen de librerías externas. Veamos el siguiente ejemplo:

main.c

#include <stdio.h>
#include "mylib.h"

int main()
{
    printf("PI is %.4f\n", pi);
    return 0;
}

mylib.h

extern float pi;

mylib.c

float pi = 3.1516;

para compilar este programa debemos correr el siguiente comando referenciando a nuestros dos archivos con extensión .c.

gcc main.c mylib.c

Nota que se ha utilizado extern para referenciar a variables creadas en otro archivo. Este modificador tiene la ventaja de no crear un nuevo espacio en memoria y permitir modularizar nuestro programa. Este mismo ejemplo lo pudieras compilar por pasos, algo que sería muy útil en aplicaciones grandes en donde si hacemos un cambio en un archivo tendríamos que compilar toda la aplicación nuevamente.

gcc -c mylib.c
gcc -c main.c
gcc main.o mylib.o

Al ejecutar gcc -c archivo.c se genera un archivo objeto. Esto es así ya que el parámetro -c evita que el compilador enlace los archivos, por lo cual, generá los archivos objeto para que nosotros mismos hagamos el enlace.

Los archivos con extensión .o son archivos objeto que el compilador ha procesado y son pasados al linker (enlazador) para generar un programa ejecutable.

Si tuvieramos un número muy grande de dependencias y realizaramos un cambio en la dependencia X, el paso a seguir para compilar sería el mismo.

gcc -c X.c
gcc -c main.c
gcc main.o X.o A.o B.o C.o

En donde A.o, B.o, C.o son otras dependencias generadas.

Ejecución

Para correr el programa basta ejecutarlo de la siguiente manera:

./output

Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.