¿Cómo la compilación y vinculación de los procesos de trabajo?


(Nota: Esto pretende ser una puerta de entrada a Desbordamiento de pila en C++ FAQ. Si quieres una crítica a la idea de proporcionar un documento de preguntas frecuentes en este formulario, a continuación la publicación en el meta, la que comenzó todo esto sería el lugar para hacerlo. Las respuestas a esta pregunta son monitoreados en el C++ sala de chat, donde el FAQ idea que comenzó en el primer lugar, entonces, la respuesta es muy probable que se lea por aquellos a quien se le ocurrió la idea).

6 Comentarios

  1. 510

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

    1. Preprocesamiento: el preprocesador lleva un código fuente de C++ archivo y ofertas con el #includes, #defines y otras directivas de preprocesador. La salida de este paso es un «puro» archivo de C++, sin pre-procesador de directivas.

    2. De compilación: el compilador toma el pre-procesador de salida y produce un archivo de objeto de ella.

    3. Vinculación: el enlazador toma los archivos objeto producido por el compilador y se produce una biblioteca o un archivo ejecutable.

    Preprocesamiento

    El preprocesador se encarga de la las directivas de preprocesador, como #include y #define. Esto es independiente de la sintaxis de C++, que es la razón por la que debe usarse con cuidado.

    Se trabaja en un archivo fuente de C++ en un momento por la sustitución de #include directivas con el contenido de los respectivos archivos (que es por lo general sólo declaraciones), haciendo la sustitución de macros (#define), y selección de diferentes partes del texto en función de #if, #ifdef y #ifndef directivas.

    El preprocesador trabaja en una secuencia de preprocesamiento de fichas. Sustitución de Macro se define como la sustitución de las fichas con las otras fichas (el operador ## permite la fusión de dos fichas cuando tenga sentido).

    Después de todo esto, el preprocesador produce una sola salida que es una secuencia de tokens resultantes de las transformaciones descritas anteriormente. También agrega algunos marcadores especiales que decirle al compilador donde cada línea de vino de manera que pueda usarlos para producir sensible mensajes de error.

    Algunos errores pueden ser producidos en esta etapa con el uso inteligente de la #if y #error directivas.

    Compilación

    El paso de compilación se realiza en cada salida del preprocesador. El compilador analiza el puro código fuente de C++ (ahora sin ningún directivas del preprocesador) y lo convierte en código ensamblador. A continuación invoca subyacente de back-end(ensamblador en toolchain) que reúne a ese código en código de máquina produciendo un archivo binario en algún formato(ELF, COFF, una.fuera, …). Este archivo objeto que contiene el código compilado (en formato binario) de los símbolos definidos en la entrada. Los símbolos en los archivos de objeto son mencionados por su nombre.

    Archivos objeto puede referirse a los símbolos que no están definidos. Este es el caso cuando se utiliza una declaración, y no proporcionan una definición de ella. El compilador no le importa esto, y estará feliz de producir el objeto de archivo siempre que el código fuente está bien formado.

    Compiladores suelen dejar de dejar de compilación en este punto. Esto es muy útil porque con ella se puede compilar cada archivo de código fuente por separado. La ventaja que ofrece es que no es necesario volver a compilar todo si sólo cambia un solo archivo.

    El objeto de producción de archivos, se pueden poner en archivos especiales llamados bibliotecas estáticas para que sea fácil de reutilizar más adelante.

    Es en esta etapa que «regular» errores del compilador, como errores de sintaxis o error de sobrecarga de resolución de errores, se informó.

    Vinculación

    El enlazador es lo que produce la compilación final de salida de los archivos objeto que el compilador produce. Esta salida puede ser compartida (o dinámica) de la biblioteca (y aunque el nombre es similar, no tienen mucho en común con las bibliotecas estáticas se mencionó anteriormente) o un archivo ejecutable.

    Que vincula a todos los archivos objeto mediante la sustitución de las referencias a undefined symbols con las direcciones correctas. Cada uno de estos símbolos pueden ser definidas en otros archivos de objetos o en las bibliotecas. Si están definidos en otras bibliotecas de la biblioteca estándar, usted necesita saber el enlazador acerca de ellos.

    En esta etapa los errores más comunes son falta de definiciones duplicadas o definiciones. El primero significa que cualquiera de las definiciones no existen (es decir, que no están escritas), o que el objeto de archivos o bibliotecas donde residen no se dieron para que el enlazador. El último es obvia: el mismo símbolo se define en dos objetos diferentes, archivos o bibliotecas.

    • La fase de compilación también llamadas ensamblador antes de convertir a archivo de objeto.
    • Donde están las optimizaciones aplicadas? A primera vista parece que se llevaría a cabo en el paso de compilación, pero por otro lado me imagino que una adecuada optimización sólo puede hacerse después de la vinculación.
    • tradicionalmente se ha hecho durante la compilación, pero los compiladores modernos de apoyo a la denominada «enlace-optimización del tiempo», que tiene la ventaja de ser capaz de optimizar a través de unidades de traducción.
    • Para ser claros, el vínculo-la optimización del tiempo no impide la optimización de ser hecho en el paso de la compilación. Lo que hace es tomar ventaja de la información adicional en el enlace de tiempo para realizar más potente optimizaciones.
    • No C tienen la misma pasos?
    • Si el enlazador convierte los símbolos que aluden a clases/método en las bibliotecas, en las direcciones, eso no significa que la biblioteca de archivos binarios se almacenan en las direcciones de memoria que el sistema operativo se mantiene constante? Estoy confundido en cuanto a cómo el vinculador gustaría saber la dirección exacta de, digamos, la stdio binario para todos los sistemas de destino. La ruta de acceso del archivo siempre sería la misma, pero la dirección exacta puede cambiar, ¿verdad?
    • Me pregunto lo mismo también. Espero que esos no son direcciones de memoria en tiempo de ejecución, a menos que alguien se aclara.
    • El proceso de elaboración seguido por la vinculación que se llama edificio.
    • Sí @KevinZhu estos pasos son los mismos para C.
    • Depende de la plataforma y el enlazador, pero en general, un enlazador sólo genera direcciones relativas. Eso significa que podría poner main() a 0, myFunction() en 100. Luego, cuando el sistema operativo se carga el ejecutable para correr, se carga el código en una determinada dirección y, a continuación, todas las direcciones son compensados por cualquiera que sea la dirección del archivo ejecutable de código se carga en el. (Esto sólo añade un número)
    • Sin embargo, algunas plataformas tienen el compilador de pre-determinar las direcciones. E. g. en muchos sistemas Unix, cada aplicación tiene «direcciones virtuales», que significa que hay una parte especial de la CPU, la Unidad de Administración de Memoria (MMU), que se traduce todas las direcciones de la «falsedad» de las direcciones del archivo ejecutable a las direcciones reales de memoria real (esto también se utiliza para el intercambio). En el caso de que usted generalmente tienen reservado un rango, por ejemplo, 0…10000, reservados para el sistema operativo y, a continuación, su aplicación en las direcciones de 10000+.
    • A partir del segundo párrafo, el tercero en la sección «Vinculación», aparece el término «bibliotecas» se refieren a «las bibliotecas estáticas»? Pregunto esto porque es un poco confuso para mí como un novato. Por favor, poke si me contestas a mi pregunta.

  2. 35

    Este tema es discutido en el CProgramming.com:

    https://www.cprogramming.com/compilingandlinking.html

    Aquí es lo que el autor no escribió:

    Compilar no es lo mismo como la creación de un archivo ejecutable!
    En su lugar, la creación de un archivo ejecutable es un proceso de varias etapas divididas en
    dos componentes: compilación y vinculación. En realidad, incluso si un programa
    «compila bien» que en realidad no podría trabajar debido a errores durante la
    la vinculación de la fase. El proceso total de va de los archivos de código fuente
    a un ejecutable podría ser denominado como una generación.

    Compilación

    Compilación se refiere al procesamiento de los archivos de código fuente (.c, .cc, o
    .cpp) y la creación de un ‘objeto’ de archivo. Este paso no crear
    nada de lo que el usuario realmente puede ejecutar. En lugar de ello, el compilador simplemente
    produce las instrucciones en lenguaje máquina que corresponden a la
    archivo de código fuente que fue compilado. Por ejemplo, si se compila (pero
    no link) tres archivos independientes, tres serán objeto de archivos
    creado como resultado, cada una 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 en una máquina
    archivo de idioma-pero no se puede ejecutar a ellos, sin embargo! Usted necesita a su vez de ellos
    en ejecutables su sistema operativo puede utilizar. Que es donde el
    enlazador viene en.

    Vinculación

    La vinculación se refiere a la creación de un único archivo ejecutable de
    varios archivos objeto. En este paso, es común que el vinculador
    se quejan de indefinido funciones (comúnmente, principal en sí). Durante
    compilación, si el compilador no se pudo encontrar la definición de una
    función en particular, que acaba de asumir que la función fue
    definida en otro archivo. Si este no es el caso, no hay manera de que el
    compilador sabría-no mirar el contenido de más de
    un archivo a la vez. El enlazador, por otro lado, puede mirar
    varios archivos e intenta encontrar las referencias de las funciones que
    no fueron mencionados.

    Usted podría preguntarse por qué hay diferentes compilación y vinculación pasos.
    En primer lugar, es probablemente más fácil de implementar las cosas de esa manera. El compilador
    hace su cosa, y el enlazador hace su cosa: manteniendo la
    funciones separadas, la complejidad del programa se reduce. Otro
    (más que obvio) la ventaja es que permite la creación de grandes
    programas sin tener que rehacer el paso de compilación cada vez que un archivo
    se cambia. En su lugar, mediante la llamada «compilación condicional», es
    necesario para compilar sólo los archivos de origen que han cambiado; por
    el resto, el objeto de archivos son suficientes para que el enlazador.
    Finalmente, esto hace que sea fácil de implementar las bibliotecas de pre-compilado
    código: basta con crear los archivos objeto y vínculo igual que cualquier otro
    archivo de objeto. (El hecho de que cada archivo se compila por separado de
    la información contenida en otros archivos, por cierto, se llama el
    «aparte modelo de compilación».)

    Para obtener los beneficios completos de la condición de compilación, es probablemente
    más fácil conseguir un programa para ayudar a usted que probar y recordar que
    los archivos que han cambiado desde la última vez que se compila. (Usted podría, por supuesto,
    acaba de volver a compilar todos los archivos que tiene una marca de tiempo mayor que el
    marca de tiempo del objeto correspondiente en el archivo.) Si estás trabajando con un
    entorno de desarrollo integrado (IDE) que puede tomar el cuidado de
    esto para usted. Si usted está utilizando las herramientas de línea de comandos, hay una ingeniosa
    utilidad llamada marca que viene con la mayoría de los *nix distribuciones. A lo largo de
    con la compilación condicional, tiene varias otras características agradables para
    de programación, tales como permitir que los diferentes compilaciones de su programa
    — por ejemplo, si usted tiene una versión de producción de un resultado detallado para la depuración.

    Saber la diferencia entre la fase de compilación y el enlace
    fase puede hacer que sea más fácil a la caza de insectos. Errores del compilador son generalmente
    sintáctica en la naturaleza-la que falta un punto y coma, un paréntesis adicionales.
    La vinculación de errores por lo general tienen que ver con la falta o varios
    definiciones. Si obtiene un error de que una función o variable
    definido varias veces desde el enlazador, que es un buen indicio de que
    el error es que dos de sus archivos de código fuente tienen la misma función
    o variable.

    • Lo que yo no estoy entendiendo es que si el preprocesador maneja las cosas como #incluye para crear un super archivo, a continuación, seguramente no hay nada para enlazar después de eso?
    • A ver si lo que escribí a continuación se hace ningún sentido para usted. He tratado de describir el problema desde el interior hacia fuera.
    • Es demasiado tarde para comentar esto, pero otros podrían encontrar útil. norton ghost.ser/D0TazQIkc8Q, Básicamente, de incluir archivos de cabecera y estos archivos de encabezado generalmente contienen sólo las declaraciones de variables y funciones, y no hay definiciones, definiciones pueden estar presentes en un archivo de origen separado.Así preprocesador es sólo incluyendo las declaraciones y no las definiciones aquí es donde enlazador de ayuda.Vincular el archivo de origen que se utiliza la variable/función con el archivo de origen que las define.
  3. 23

    En el frontal estándar:

    • un unidad de traducción es la combinación de una fuente de archivos, incluidos los encabezados de los archivos de origen y de menos líneas de código fuente ignorado por el condicional inclusión directiva de preprocesador.

    • el estándar define las 9 fases de la traducción. Los cuatro primeros corresponden a preprocesamiento, las tres son la compilación, el siguiente es el de la creación de instancias de plantillas (la producción de la creación de instancias de unidades) y el último es el de la vinculación.

    En la práctica, la octava fase (la de la creación de instancias de plantillas) se realiza durante el proceso de compilación, pero algunos compiladores de retardo a la vinculación de fase y algunos de difundirla en los dos.

    • Podría lista de todos los 9 las fases? Eso sería una buena adición a la respuesta, creo. 🙂
    • Relacionados: stackoverflow.com/questions/1476892/….
    • sólo añadir que la creación de instancias de plantilla justo antes de la última fase en la respuesta señalado por @ose. Si mal no recuerdo hay que ser sutiles diferencias en la precisión de los términos en el manejo de los distintos personajes, pero no creo que salgan a la superficie en el diagrama de etiquetas.
    • sí, pero se supone que esta es la pregunta frecuente, ¿no? Así que no debería esta información estará disponible esto? 😉
    • cuando he de tiempo voy a tratar de poner un diagrama similar (que sería mejor hilight el multi unidad de compilación de casos y el manejo de plantillas). Pero no aguanta la respiración, ser claro en ASCII art es un arte realmente no amo.
    • Estoy de acuerdo. ¿Por qué no publicar una respuesta que explica esto en detalle?
    • simplemente listado de ellos por su nombre sería de gran ayuda. Entonces la gente sabe qué buscar si quieren más detalle. De todos modos, +1 en su respuesta, en cualquier caso 🙂

  4. 14

    El flaco es que una carga de CPU de datos de direcciones de memoria, almacena los datos para las direcciones de memoria, y ejecutar instrucciones de forma secuencial de direcciones de memoria, con algunos saltos condicionales en la secuencia de instrucciones de procesado. Cada una de estas tres categorías de instrucciones implica el cálculo de una dirección a una celda de memoria para ser utilizado en la instrucción de la máquina. Debido a que las instrucciones de la máquina son de longitud variable, dependiendo de la instrucción que participan, y debido a que la cadena de una longitud variable de ellos juntos como vamos a construir nuestro código máquina, no es un proceso de dos pasos que intervienen en el cálculo y la construcción de todas las direcciones.

    Primero debemos trazar la asignación de memoria lo mejor que podamos, antes de que podamos saber exactamente qué va en cada celda. Podemos averiguar la bytes o palabras, o lo que sea que forma las instrucciones y los literales y de los datos. Acabamos de empezar a asignar la memoria y la construcción de los valores que se crea el programa como vamos, y tomar nota de cualquier lugar en el que tenemos que volver atrás y corregir una dirección. En ese lugar ponemos un maniquí sólo de la almohadilla de la ubicación, para que podamos seguir para calcular el tamaño de la memoria. Por ejemplo, nuestro primer código de la máquina puede llevar una celda. El siguiente código máquina puede tomar de 3 células, incluyendo un código de máquina de la célula y dos de la dirección de las células. Ahora nuestro puntero de dirección es de 4. Sabemos lo que pasa en la máquina de la célula, que es el código de operación, pero tenemos que esperar para calcular lo que va en la dirección de las células hasta que sabemos donde los datos se encuentra, es decir, ¿cuál será la dirección del equipo de los datos.

    Si se tratase de un archivo de origen de un compilador, en teoría, podría producir completamente ejecutable del código de la máquina sin un enlazador. En un proceso de dos pasadas se podría calcular todas las direcciones reales para todas las celdas de datos de referencia para cualquier máquina de cargar o almacenar instrucciones. Y se podría calcular todas las direcciones absolutas de referencia para cualquier absoluta instrucciones de salto. Esta es la manera más simple de los compiladores, como la que en Adelante obra, no enlazador.

    Un enlazador es algo que permite que los bloques de código se compila por separado. Esto puede acelerar el proceso general de construcción de código, y permite cierta flexibilidad con la forma en que los bloques se utilizan posteriormente, en otras palabras, ellos pueden ser reubicados en la memoria, por ejemplo la adición de 1000 a cada dirección para meterse el bloque por 1000 dirección de células.

    Así que lo que el compilador de salidas es áspera código máquina que aún no está totalmente construido, pero está diseñado de modo sabemos que el tamaño de todo, en otras palabras, para que podamos empezar a calcular donde todas las direcciones absolutas se encuentran. el compilador también se genera una lista de los símbolos, que son el nombre/dirección de pares. Los símbolos se relacionan con un desplazamiento de memoria en el código de la máquina en el módulo con un nombre. El desplazamiento de ser la absoluta distancia a la ubicación de la memoria del símbolo en el módulo.

    Que es donde llegamos al enlazador. El vinculador primero golpea a todos estos bloques de código máquina juntas de extremo a extremo y notas de down, donde cada uno comienza. A continuación, se calcula la dirección fija sumando el desplazamiento relativo dentro de un módulo y la posición absoluta del módulo en el más grande de diseño.

    Obviamente he simplificada de esta manera usted puede tratar de comprenderlo, y que han deliberadamente no se utiliza la jerga de los archivos objeto, símbolo de las mesas, etc. lo que para mí es parte de la confusión.

  5. 9

    Fíjate en la URL: http://faculty.cs.niu.edu/~mcmahon/CS241/Notes/compile.html

    El completar esta cumpliendo proceso de C++ es introducido claramente en esta dirección.

    • Gracias por compartirlo, es tan simple y sencillo de entender.
    • Bueno, recursos, puede poner un poco de explicación básica de los procesos, la respuesta está marcado por el algoritmo como de baja calidad b/c es corta y solo la url.
    • Un bonito corto tutorial que he encontrado: calleerlandsson.com/the-four-stages-of-compiling-a-c-program
  6. 5

    GCC compila una C/C++ programa ejecutable en 4 pasos.

    Por ejemplo, un «gcc -o hello.exe hello.c» se lleva a cabo de la siguiente manera:

    1. Pre-procesamiento de

    Preprocessin a través de la GNU Preprocesador de C (cpp.exe), que incluye
    los encabezados (#include) y amplía las macros (#define).

    cpp hola.c > hola.yo

    La resultante de archivo intermedio «hola.i» contiene la ampliación de código fuente.

    2. Compilación

    El compilador compila el pre-procesado de código fuente en código ensamblador para un procesador específico.

    gcc-S hola.yo

    La opción-S especifica para producir código de la asamblea, en lugar de código objeto. La resultante de la asamblea archivo es «hola.s».

    3. Asamblea

    El ensamblador (as.exe) convierte el código ensamblador a código máquina en el archivo de objeto «hola.o».

    como -oh, hola.oh, hola.s

    4. Enlazador

    Finalmente, el enlazador (ld.exe) enlaza el código objeto con el código de la biblioteca para producir un archivo ejecutable «hello.exe».

    ld-o hello.exe hola.o …las bibliotecas…

Dejar respuesta

Please enter your comment!
Please enter your name here