Saltar al contenido

¿Cómo funciona el proceso de compilación / vinculación?

Hola, descubrimos la respuesta a lo que estabas buscando, desplázate y la encontrarás aquí.

Solución:

La compilación de un programa en C ++ consta de tres pasos:

  1. Preprocesamiento: el preprocesador toma un archivo de código fuente C ++ y se ocupa del #includes, #definesy otras directivas del preprocesador. El resultado de este paso es un archivo C ++ “puro” sin directivas de preprocesador.

  2. Compilación: el compilador toma la salida del preprocesador y produce un archivo objeto a partir de él.

  3. Vinculación: el vinculador toma los archivos objeto producidos por el compilador y genera una biblioteca o un archivo ejecutable.

Preprocesamiento

El preprocesador maneja el directivas del pre procesador, igual que #include y #define. Es independiente de la sintaxis de C ++, por lo que debe usarse con cuidado.

Funciona en un archivo fuente de C ++ a la vez reemplazando #include directivas con el contenido de los archivos respectivos (que generalmente son solo declaraciones), haciendo reemplazo de macros (#define), y seleccionando diferentes porciones de texto dependiendo de #if, #ifdef y #ifndef directivas.

El preprocesador funciona en una secuencia de tokens de preprocesamiento. La sustitución de macros se define como la sustitución de tokens por otros tokens (el operador ## permite fusionar dos tokens cuando tiene sentido).

Después de todo esto, el preprocesador produce una única salida que es un flujo de tokens resultante de las transformaciones descritas anteriormente. También agrega algunos marcadores especiales que le dicen al compilador de dónde proviene cada línea para que pueda usarlos para producir mensajes de error sensibles.

En esta etapa se pueden producir algunos errores con el uso inteligente del #if y #error directivas.

Compilacion

El paso de compilación se realiza en cada salida del preprocesador. El compilador analiza el código fuente puro de C ++ (ahora sin directivas de preprocesador) y lo convierte en código ensamblador. Luego invoca el back-end subyacente (ensamblador en la cadena de herramientas) que ensambla ese código en código de máquina produciendo un archivo binario real en algún formato (ELF, COFF, a.out, …). Este archivo de objeto contiene el código compilado (en forma binaria) de los símbolos definidos en la entrada. Los símbolos de los archivos de objetos se denominan por su nombre.

Los archivos de objeto pueden hacer referencia a símbolos que no están definidos. Este es el caso cuando usa una declaración y no proporciona una definición para ella. Al compilador no le importa esto, y felizmente producirá el archivo objeto siempre que el código fuente esté bien formado.

Los compiladores normalmente le permiten detener la compilación en este punto. Esto es muy útil porque con él puede compilar cada archivo de código fuente por separado. La ventaja que esto proporciona es que no es necesario volver a compilar todo si solo cambia un solo archivo.

Los archivos de objeto producidos se pueden guardar en archivos especiales llamados static bibliotecas, para facilitar su reutilización más adelante.

Es en esta etapa que se informan los errores “regulares” del compilador, como errores de sintaxis o errores de resolución de sobrecarga fallidos.

Enlace

El enlazador es lo que produce el resultado final de la compilación a partir de los archivos objeto que produjo el compilador. Esta salida puede ser una biblioteca compartida (o dinámica) (y aunque el nombre es similar, no tienen mucho en común con static bibliotecas mencionadas anteriormente) o un ejecutable.

Vincula todos los archivos de objeto reemplazando las referencias a símbolos indefinidos con las direcciones correctas. Cada uno de estos símbolos se puede definir en otros archivos de objeto o en bibliotecas. Si están definidos en bibliotecas que no sean la biblioteca estándar, debe informar al vinculador sobre ellos.

En esta etapa, los errores más comunes son definiciones faltantes o definiciones duplicadas. Lo primero significa que las definiciones no existen (es decir, no están escritas), o que los archivos de objeto o las bibliotecas donde residen no se entregaron al enlazador. Lo último es obvio: el mismo símbolo se definió en dos bibliotecas o archivos de objetos diferentes.

Este tema se discute en CProgramming.com:
https://www.cprogramming.com/compilingandlinking.html

Esto es lo que escribió el autor:

¡Compilar no es lo mismo que crear un archivo ejecutable! En cambio, la creación de un ejecutable es un proceso de varias etapas dividido en dos componentes: compilación y vinculación. En realidad, incluso si un programa “se compila bien”, es posible que no funcione debido a errores durante la fase de vinculación. El proceso total de pasar de los archivos de código fuente a un ejecutable podría denominarse compilación.

Compilacion

La compilación se refiere al procesamiento de archivos de código fuente (.c, .cc o .cpp) y la creación de un archivo ‘objeto’. Este paso no crea nada que el usuario pueda ejecutar. En cambio, el compilador simplemente produce las instrucciones en lenguaje de máquina que corresponden al archivo de código fuente que fue compilado. Por ejemplo, si compila (pero no vincula) tres archivos separados, tendrá tres archivos objeto creados como salida, cada uno con el nombre .o o .obj (la extensión dependerá de su compilador). Cada uno de estos archivos contiene una traducción de su archivo de código fuente a un archivo de lenguaje de máquina, ¡pero aún no puede ejecutarlos! Necesita convertirlos en ejecutables que su sistema operativo pueda usar. Ahí es donde entra el enlazador.

Enlace

La vinculación se refiere a la creación de un solo archivo ejecutable a partir de múltiples archivos de objeto. En este paso, es común que el enlazador se queje de funciones indefinidas (comúnmente, main en sí). Durante la compilación, si el compilador no puede encontrar la definición de una función en particular, simplemente asumirá que la función se definió en otro archivo. Si este no es el caso, no hay forma de que el compilador lo sepa; no mira el contenido de más de un archivo a la vez. El vinculador, por otro lado, puede buscar en varios archivos e intentar encontrar referencias para las funciones que no se mencionaron.

Es posible que se pregunte por qué hay pasos separados de compilación y vinculación. Primero, probablemente sea más fácil implementar cosas de esa manera. El compilador hace lo suyo y el enlazador hace lo suyo: al mantener las funciones separadas, se reduce la complejidad del programa. Otra ventaja (más obvia) es que permite la creación de grandes programas sin tener que rehacer el paso de compilación cada vez que se cambia un archivo. En su lugar, utilizando la denominada “compilación condicional”, es necesario compilar solo los archivos fuente que han cambiado; por lo demás, los archivos objeto son una entrada suficiente para el enlazador. Finalmente, esto simplifica la implementación de bibliotecas de código precompilado: simplemente cree archivos de objeto y vincúlelos como cualquier otro archivo de objeto. (El hecho de que cada archivo se compile por separado de la información contenida en otros archivos, dicho sea de paso, se denomina “modelo de compilación independiente”).

Para obtener todos los beneficios de la compilación de condiciones, probablemente sea más fácil conseguir un programa que le ayude que intentar recordar qué archivos ha cambiado desde la última compilación. (Por supuesto, podría simplemente volver a compilar cada archivo que tenga una marca de tiempo mayor que la marca de tiempo del archivo de objeto correspondiente). Si está trabajando con un entorno de desarrollo integrado (IDE), es posible que ya se encargue de esto. Si está utilizando herramientas de línea de comandos, hay una utilidad ingeniosa llamada make que viene con la mayoría de las distribuciones * nix. Junto con la compilación condicional, tiene varias otras características interesantes para la programación, como permitir diferentes compilaciones de su programa, por ejemplo, si tiene una versión que produce una salida detallada para la depuración.

Conocer la diferencia entre la fase de compilación y la fase de enlace puede facilitar la búsqueda de errores. Los errores del compilador suelen ser de naturaleza sintáctica: falta un punto y coma, un paréntesis adicional. Los errores de enlace suelen tener que ver con definiciones faltantes o múltiples. Si obtiene un error de que una función o variable se define varias veces desde el enlazador, es una buena indicación de que el error es que dos de sus archivos de código fuente tienen la misma función o variable.

En el frente estándar:

  • a unidad de traducción es la combinación de archivos fuente, encabezados incluidos y archivos fuente menos cualquier línea fuente omitida por la directiva del preprocesador de inclusión condicional.

  • el estándar define 9 fases en la traducción. Los primeros cuatro corresponden al preprocesamiento, los tres siguientes son la compilación, el siguiente es la instanciación de plantillas (produciendo unidades de instanciación) y el último es el enlace.

En la práctica, la octava fase (la creación de instancias de plantillas) se realiza a menudo durante el proceso de compilación, pero algunos compiladores la retrasan hasta la fase de vinculación y algunos la distribuyen en las dos.

Comentarios y valoraciones

Agradecemos que desees añadir valor a nuestro contenido informacional cooperando tu experiencia en las críticas.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *