Tabla de contenido
introducción
Al utilizar el entorno de desarrollo integrado (IDE) para la programación en lenguaje C, después de hacer clic en el botón " Compilar ", el proceso de generación de todo el programa C desde el código fuente hasta el archivo ejecutable se completará automáticamente. El IDE realizará el proceso de compilación del lenguaje C para nosotros en segundo plano, convirtiendo el código fuente en el archivo ejecutable final. Aunque el IDE oculta los detalles de bajo nivel, comprender el proceso de compilación sigue siendo valioso para el programador.
Descripción general
gcc
Ambosg++
son parte de la colección de compiladores GNU (GCC para abreviar), que segcc
usan para compilar código de lenguaje C yg++
se usan para compilar código de lenguaje C++. Su proceso de compilación es similar en la mayoría de los casos , pero dependiendo de la extensión del archivo de entrada y de algunas opciones predeterminadas, llamarán a diferentes interfaces de compilación, a saber, C front end o C++ front end.
La siguiente es una descripción general del proceso de compilación para gcc
yg++
-
Preprocesamiento (Preprocessing): En primer lugar, se preprocesa el archivo de origen. El preprocesador procesará las directivas de preprocesamiento en el código fuente, como
#
las directivas que comienzan con , como#include
,#define
etc., y ampliará las definiciones de macro. El código preprocesado genera un.i
archivo , generalmente en un directorio temporal. -
Compilación: A continuación, la interfaz del compilador compila el código fuente preprocesado en código ensamblador (
.s
archivo ). Esta fase comprueba errores sintácticos y semánticos y realiza optimizaciones , pero no genera código ejecutable. -
Ensamblado (Assembling): El ensamblador (as) convierte el código ensamblador en código máquina y genera un archivo objeto (
.o
archivo ). -
Vinculación: finalmente, el vinculador (
ld
) vincula el archivo de objeto con los archivos de biblioteca requeridos para generar el archivo ejecutable final.
Tomemos un ejemplo simple del proceso de compilación del programa C en el entorno Linux para profundizar nuestra comprensión.
Ejemplo de código (principal.c):
#include<stdio.h>
int main(){
printf("Hello Linux\n");
return 0;
}
1. Pretratamiento
El preprocesamiento es el primer paso en el proceso de compilación, que procesa
#
las directivas de preprocesamiento que comienzan con y expande las definiciones de macro. El preprocesador realiza las siguientes tareas principales:
Instrucción de procesamiento
#include
: inserte el contenido del archivo de encabezado especificado en el código fuente. De esta forma, las declaraciones y definiciones de otras funciones o variables se pueden utilizar en el archivo fuente.Definiciones de macros de proceso: expanda las macros definidas en el código en expresiones o declaraciones correspondientes. Por ejemplo, reemplazará
#define MAX_VALUE 100
todo con en el código fuente .MAX_VALUE
100
Procesar instrucciones de compilación condicional: como
#ifdef
,#ifndef
,#if
etc. Estas instrucciones juzgan si compilar algunos bloques de código de acuerdo con las condiciones.El código preprocesado produce un solo
.i
archivo, que es un archivo intermedio con todas las macros expandidas y todos los encabezados incluidos.
ejemplo de sintaxis
gcc -E main.c -o main.i
En el comando -E es para permitir que el compilador salga después del preprocesamiento sin un proceso de compilación posterior, y -o es para especificar el nombre del archivo de salida.
El resultado de usar esta instrucción es insertar todo el contenido del archivo stdio.h en main.c para formar el archivo main.i.
Se puede ver que el archivo main.i después del preprocesamiento es obviamente mucho más grande que el archivo main.c. Revisemos el archivo main.i, porque main.i todavía es un archivo de texto en este momento.
(Use el comando head para ver el archivo main.i)
Dos, compila
La compilación es la segunda etapa del código preprocesado. Un front-end del compilador (como
cc1
occ1plus
) toma el código preprocesado y lo convierte en código ensamblador. Durante la fase de compilación, el compilador realiza las siguientes tareas principales:
Verificación gramatical y semántica: el compilador verifica si el código cumple con las reglas gramaticales de C/C++ y realiza un análisis semántico para asegurarse de que el código no tenga errores lógicos.
Generar una representación intermedia: el compilador convierte el código en una representación intermedia, generalmente una representación independiente del hardware de bajo nivel.
Optimización: el compilador puede optimizar la representación intermedia para mejorar la eficiencia de ejecución del programa y la calidad del código.
La fase de compilación no genera un archivo ejecutable, sino que convierte el código en código ensamblador, generalmente guardado como
.s
un archivo.
ejemplo de sintaxis
gcc -S main.i -o main.s
En el comando -S hace que el compilador se detenga después de la compilación sin un proceso de compilación posterior, y -o especifica el nombre del archivo de salida.
El tamaño del archivo de ensamblaje compilado ya es muy pequeño, mucho más pequeño que el archivo main.i después del preprocesamiento.
Una vez finalizado el proceso de compilación, se generará el código ensamblador test.s del programa, que también es un archivo de texto . Vamos a ver.
La figura es el código ensamblador en main.s.
3. Compilación
En la etapa de ensamblado, el ensamblador (
as
) toma el código ensamblador generado por la compilación y lo convierte en código máquina. Las tareas del ensamblador incluyen:
Convierta el código de ensamblaje en código de máquina: traduzca las instrucciones de ensamblaje en código de ensamblaje en instrucciones de máquina que una arquitectura de hardware específica pueda entender.
Generar archivos de objetos: genere uno o más archivos de objetos (
.o
archivos), uno para cada archivo fuente o unidad de compilación.Los archivos de objetos son representaciones binarias de código de máquina, pero aún no son programas ejecutables finales porque algunas referencias de símbolos pueden permanecer sin resolver.
ejemplo de sintaxis
gcc -c main.s -o main.o
opción en el comando -c
, le dice a gcc que solo compile, no que vincule. Por lo tanto, este comando solo convertirá el código ensamblador en un archivo de objeto y no generará un archivo ejecutable. -o es para especificar el nombre del archivo de salida.
Un archivo de objeto test.o
es una representación binaria de código de máquina que se puede usar como entrada para vincular para producir el ejecutable final.
4. Enlaces
- Un enlazador toma uno o más archivos de objeto y los archivos de biblioteca requeridos y los combina en el ejecutable final.
- El vinculador resuelve las referencias de símbolos en los archivos de objetos, encuentra las definiciones de símbolos correspondientes y reubica los símbolos para que apunten correctamente a sus definiciones.
- Combine archivos de biblioteca para producir un ejecutable completo que contenga todo el código de máquina y los símbolos resueltos.
ejemplo de sintaxis
gcc main.o -o main
comando es un comando gcc que vincula gcc main.o -o main
un archivo de objeto ( main.o
) en un archivo ejecutable ( ). main
En este comando, no usamos -c
opciones, por lo que gcc realizará la operación de enlace para generar el archivo ejecutable final.
Cuando se ejecuta el comando gcc main.o -o main
, gcc main.o
vinculará el archivo de objeto junto con los archivos de biblioteca requeridos (si corresponde) y generará el archivo ejecutable final main
. Este archivo ejecutable es un programa C que puede ejecutarse en Linux.
Al ejecutar ./main
el comando, se ejecuta main
el ejecutable llamado . Este es el archivo ejecutable del programa C que generamos gcc
anteriormente con el comando.
Resumir
El proceso de generación de un programa ejecutable se divide en cuatro pasos:
- Desde un archivo .c hasta un archivo .i, este proceso se denomina preprocesamiento.
- Del archivo .i al archivo .s, este proceso se denomina compilación.
- Desde el archivo .s hasta el archivo .o, este proceso se denomina ensamblado.
- Desde archivos .o hasta archivos ejecutables, este proceso se denomina vinculación.
En el entorno de desarrollo integrado, después de hacer clic en el botón "Compilar", el IDE completará automáticamente las cuatro etapas anteriores sin realizar manualmente cada paso. Si no hay errores de compilación, el ejecutable final se crea y se puede ejecutar directamente en el IDE.
Aunque el IDE nos brinda herramientas de compilación convenientes, sigue siendo importante que los programadores comprendan el proceso de compilación del lenguaje C, especialmente al resolver algunos errores de compilación u optimizar, comprender el proceso subyacente puede ayudarnos a comprender mejor y mejorar el código.