logo

Compilación de un programa en C: detrás de escena

La compilación es el proceso de convertir el código fuente del lenguaje C en código de máquina. Al ser C un lenguaje de nivel medio, necesita un compilador que lo convierta en un código ejecutable para que el programa pueda ejecutarse en nuestra máquina.

El programa C pasa por las siguientes fases durante la compilación:



proceso de compilación en c

Proceso de compilación en C

¿Cómo compilamos y ejecutamos un programa en C?

Primero necesitamos un compilador y un editor de código para compilar y ejecutar un programa en C. El siguiente ejemplo es de una máquina Ubuntu con compilador GCC.

Paso 1: crear un archivo fuente C

Primero creamos un programa en C usando un editor y guardamos el archivo como nombre de archivo.c



códigos de color java
  $ vi filename.c>

Podemos escribir un programa simple de hola mundo y guardarlo.

Paso 2: compilar usando el compilador GCC

Usamos el siguiente comando en la terminal para compilar nuestro archivo fuente filename.c

conjunto de hash de java
  $ gcc filename.c –o filename>

Podemos pasar muchas instrucciones al compilador GCC para diferentes tareas como:



  • La opción -Wall habilita todos los mensajes de advertencia del compilador. Se recomienda esta opción para generar un mejor código.
  • La opción -o se utiliza para especificar el nombre del archivo de salida. Si no utilizamos esta opción, entonces se genera un archivo de salida con el nombre a.out.

Si no hay errores en nuestro programa C, se generará el archivo ejecutable del programa C.

Paso 3: ejecutar el programa

Después de que se genera el ejecutable de compilación, ejecutamos el ejecutable generado usando el siguiente comando.

  $ ./filename>

El programa se ejecutará y el resultado se mostrará en la terminal.

¿Qué pasa dentro del proceso de compilación?

Un compilador convierte un programa C en un ejecutable. Hay cuatro fases para que un programa C se convierta en ejecutable:

    Vinculación de ensamblaje de compilación previa al procesamiento

Al ejecutar el siguiente comando, obtenemos todos los archivos intermedios en el directorio actual junto con el ejecutable.

  $gcc -Wall -save-temps filename.c –o filename>

La siguiente captura de pantalla muestra todos los archivos intermedios generados.

pruebas y tipos de pruebas

Archivos intermedios

Veamos uno por uno qué contienen estos archivos intermedios.

concatenar cadena java

1. Preprocesamiento

Esta es la primera fase por la que se pasa el código fuente. Esta fase incluye:

  • Eliminación de comentarios
  • Expansión de macros
  • Ampliación de los archivos incluidos.
  • Compilación condicional

La salida preprocesada se almacena en el nombre de archivo.i . Veamos qué hay dentro de filename.i: usando $vi nombre de archivo.i

En el resultado anterior, el archivo fuente está lleno de muchísima información, pero al final, nuestro código se conserva.

  • printf ahora contiene a + b en lugar de add(a, b), eso se debe a que las macros se han expandido.
  • Se eliminan los comentarios.
  • #include falta; en lugar de eso, vemos mucho código. Por lo tanto, los archivos de encabezado se han ampliado e incluido en nuestro archivo fuente.

2. Compilando

El siguiente paso es compilar filename.i y producir un; archivo de salida compilado intermedio nombre de archivo.s . Este archivo está en instrucciones de nivel ensamblador. Veamos este archivo usando $nano nombre de archivo.s comando terminal.

Archivo de código ensamblador

La instantánea muestra que está en lenguaje ensamblador, que el ensamblador puede entender.

3. Montaje

En esta fase, el nombre del archivo.s se toma como entrada y se convierte en nombre de archivo.o por el ensamblador. Este archivo contiene instrucciones a nivel de máquina. En esta fase, solo el código existente se convierte a lenguaje de máquina y las llamadas a funciones como printf() no se resuelven. Veamos este archivo usando $vi nombre de archivo.o

pruebas y tipos de software

Código binario

4. Vinculación

Esta es la fase final en la que se realiza toda la vinculación de las llamadas a funciones con sus definiciones. Linker sabe dónde se implementan todas estas funciones. Linker también hace un trabajo adicional, agrega código adicional a nuestro programa que se requiere cuando el programa comienza y finaliza. Por ejemplo, hay un código que se requiere para configurar el entorno, como pasar argumentos de línea de comando. Esta tarea se puede verificar fácilmente usando $tamaño nombre de archivo.o y $ tamaño nombre de archivo . A través de estos comandos, sabemos cómo el archivo de salida aumenta de un archivo objeto a un archivo ejecutable. Esto se debe al código adicional que Linker agrega a nuestro programa.

Nota: GCC por defecto realiza enlaces dinámicos, por lo que printf() está vinculado dinámicamente en el programa anterior. Consulte this, this y this para obtener más detalles sobre enlaces estáticos y dinámicos.