enlazadores ejemplos depuradores codigo c++ compiler-construction linker

ejemplos - C++: funcionalidad del compilador y enlazador



depuradores (8)

Ah, pero podría tener NonDefinedFunction (int) en otra unidad de compilación.

El compilador produce algunos resultados para el vinculador que básicamente dice lo siguiente (entre otras cosas):

  • Qué símbolos (funciones / variables / etc.) están definidos.
  • Qué símbolos están referenciados pero indefinidos. En este caso, el vinculador debe resolver las referencias buscando a través de los otros módulos que están vinculados. Si no puede, obtiene un error de enlazador.

Quiero entender exactamente qué parte de un compilador de programa mira y qué enlazador mira. Entonces escribí el siguiente código:

#include <iostream> using namespace std; #include <string> class Test { private: int i; public: Test(int val) {i=val ;} void DefinedCorrectFunction(int val); void DefinedIncorrectFunction(int val); void NonDefinedFunction(int val); template <class paramType> void FunctionTemplate (paramType val) { i = val } }; void Test::DefinedCorrectFunction(int val) { i = val; } void Test::DefinedIncorrectFunction(int val) { i = val } void main() { Test testObject(1); //testObject.NonDefinedFunction(2); //testObject.FunctionTemplate<int>(2); }

Tengo tres funciones:

  • DefinedCorrectFunction - Esta es una función normal declarada y definida correctamente.
  • DefinedIncorrectFunction - Esta función se declara correctamente pero la implementación es incorrecta (falta;
  • Función no definida: solo declaración. Sin definición
  • FunctionTemplate - Una plantilla de función.

    Ahora, si compilo este código, obtengo un error de compilación para el '';'' faltante en DefinedIncorrectFunction.
    Supongamos que soluciono esto y luego hago un comentario de testObject.NonDefinedFunction (2). Ahora recibo un error de enlazador. Comente ahora testObject.FunctionTemplate (2). Ahora obtengo un error de compilación para el '';'' faltante.

Para las plantillas de funciones entiendo que no son tocadas por el compilador a menos que sean invocadas en el código. Así que el desaparecido '';'' no se queja por el compilador hasta que llamé a testObject.FunctionTemplate (2).

Para testObject.NonDefinedFunction (2), el compilador no se quejó, pero sí el enlazador. Para mi comprensión, a todo el compilador le interesaba saber que se ha declarado una función de Función No Definida. No le importó la implementación. Entonces el enlazador se quejó porque no pudo encontrar la implementación. Hasta aquí todo bien.

Donde me confundo es cuando el compilador se quejó sobre DefinedIncorrectFunction. No buscó la implementación de NonDefinedFunction pero pasó por la función DefinedIncorrectFunction.

Así que no tengo muy claro qué hace exactamente el compilador y qué hace el enlazador. Mi entendimiento es que los enlaces enlazan los componentes con sus llamadas. Entonces, cuando se llama a NonDefinedFunction, busca la implementación compilada de NonDefinedFunction y se queja. Pero el compilador no se preocupó por la implementación de NonDefinedFunction, pero sí por DefinedIncorrectFunction.

Realmente apreciaría si alguien puede explicar esto o proporcionar alguna referencia.

Gracias.


Creo que esta es tu pregunta:

Donde me confundo es cuando el compilador se quejó sobre DefinedIncorrectFunction. No buscó la implementación de NonDefinedFunction pero pasó por la función DefinedIncorrectFunction.

El compilador intentó analizar DefinedIncorrectFunction (porque proporcionó una definición en este archivo fuente) y hubo un error de sintaxis (falta punto y coma). Por otro lado, el compilador nunca vio una definición para NonDefinedFunction porque simplemente no había código en este módulo. Es posible que haya proporcionado una definición de NonDefinedFunction en otro archivo fuente, pero el compilador no lo sabe. El compilador solo mira un archivo de origen (y sus archivos de encabezado incluidos) a la vez.


Digamos que quieres comer algo de sopa, entonces vas a un restaurante.

Busca en el menú sopa. Si no lo encuentras en el menú, sales del restaurante. (algo así como un compilador que se queja de que no pudo encontrar la función) Si lo encuentras, ¿qué haces?

Llamas al camarero para que te traiga algo de sopa. Sin embargo, solo porque está en el menú, no significa que también lo tengan en la cocina. Podría ser un menú anticuado, podría ser que alguien se olvidó de decirle al chef que se supone que debe hacer sopa. Así que de nuevo, te vas. (como un error del vinculador que no pudo encontrar el símbolo)


El enlazador está ahí para enlazar en un código definido (posiblemente) en módulos externos: bibliotecas o archivos de objeto que usará junto con este archivo fuente particular para generar el ejecutable completo. Por lo tanto, si tiene una declaración pero no tiene una definición, su código se compilará porque el compilador sabe que el vinculador podría encontrar el código faltante en otro lugar y hacerlo funcionar. Por lo tanto, en este caso obtendrá un error del vinculador, no del compilador.

Si, por otro lado, hay un error de sintaxis en su código, el compilador ni siquiera puede compilar y obtendrá un error en esta etapa. Las macros y las plantillas pueden comportarse de forma un tanto diferente, sin causar errores si no se utilizan (las plantillas son casi tanto como macros con una interfaz algo más agradable), pero también depende de la gravedad del error. Si se equivoca tanto que el compilador no puede determinar dónde termina el código de plantilla / macro y comienza el código normal, no podrá compilarse.

Con código regular, el compilador debe compilar incluso el código muerto (código no referenciado en su archivo fuente) porque alguien podría querer usar ese código de otro archivo fuente, al vincular su archivo .o a su código. Por lo tanto, el código macro sin plantilla debe ser sintácticamente correcto incluso si no se usa directamente en el mismo archivo fuente.


El punto y coma faltante es un error de sintaxis y, por lo tanto, el código no debe compilarse. Esto podría suceder incluso en una implementación de plantilla. Básicamente, hay una etapa de análisis sintáctico y, aunque para un humano es obvio cómo "arreglar y recuperar", un compilador no tiene que hacer eso. No puede simplemente "imaginar que el punto y coma está allí porque eso es lo que quería decir" y continuar.

Un enlazador busca definiciones de funciones para llamar donde se requieren. No es necesario aquí así que no hay queja. No hay ningún error en este archivo como tal, ya que incluso si fuera necesario, podría no implementarse en esta unidad de compilación en particular. El enlazador es responsable de juntar diferentes unidades de compilación, es decir, "unirlas".


Lo que hace el compilador y lo que hace el enlazador depende de la implementación: una implementación legal podría simplemente almacenar la fuente tokenizada en el "compilador" y hacer todo lo que esté en el vinculador. Las implementaciones modernas posponen cada vez más al enlazador, para una mejor optimización. Y muchas implementaciones tempranas de plantillas ni siquiera miraban el código de la plantilla hasta el momento del enlace, aparte de los paréntesis correspondientes para saber dónde terminaba la plantilla. Desde el punto de vista del usuario, le interesa más saber si el error "requiere un diagnóstico" (que puede ser emitido por el compilador o el enlazador) o es un comportamiento indefinido.

En el caso de DefinedIncorrectFunction , usted proporciona el texto fuente que la implementación debe analizar. Ese texto contiene un error para el cual se requiere un diagnóstico. En el caso de la función no NonDefinedFunction : si se utiliza la función, no proporcionar una definición (o proporcionar más de una definición) en el programa completo es una violación de la regla de una definición, que es un comportamiento indefinido. No se requiere diagnóstico (pero no puedo imaginar una implementación que no proporcionó una para la definición faltante de una función que se usó).

En la práctica, los errores que pueden detectarse fácilmente simplemente examinando el ingreso de texto de una sola unidad de traducción están definidos por el estándar para "requerir un diagnóstico", y serán detectados por el compilador. Los errores que no pueden detectarse mediante el examen de una sola unidad de traducción (por ejemplo, una definición faltante, que podría estar presente en una unidad de traducción diferente) son un comportamiento formalmente indefinido; en muchos casos, los errores pueden ser detectados por el vinculador, y en tales casos, las implementaciones de hecho emitirán un error.

Esto es algo modificado en casos como las funciones en línea, donde se le permite repetir la definición en cada unidad de traducción, y extremadamente modificada por las plantillas, ya que muchos errores no se pueden detectar hasta la creación de instancias. En el caso de las plantillas, la norma deja a las implementaciones una gran libertad: al menos, el compilador debe analizar la plantilla lo suficiente como para determinar dónde termina la plantilla. El estándar agregó cosas como typename , sin embargo, para permitir mucho más análisis antes de la creación de instancias. Sin embargo, en contextos dependientes, algunos errores no pueden detectarse antes de la creación de instancias, lo que puede ocurrir en tiempo de compilación o en tiempo de enlace: las implementaciones tempranas favorecieron la creación de instancias de tiempo de enlace; la instanciación de tiempo de compilación domina hoy, y es utilizada por VC ++ y g ++.


El compilador comprueba que el código fuente es conforme al idioma y se adhiere a la semántica del lenguaje. El resultado del compilador es código de objeto.

Linker vincula los diferentes módulos de objetos para formar un exe. Las definiciones de funciones se ubican en esta fase y el código apropiado para llamarlas se agrega en esta fase.

El compilador compila el código en forma de unidades de traducción . Recopilará todo el código que se incluye en un archivo fuente .cpp ,
DefinedIncorrectFunction() se define en su archivo de origen, por lo que el compilador verifica su validez de idioma.
NonDefinedFunction() tiene cualquier definición en el archivo fuente por lo que el compilador no necesita compilarlo, si la definición está presente en algún otro archivo fuente, la función se compilará como parte de esa unidad de traducción y además el enlazador enlazará a él, si en la etapa de enlace la definición no es encontrada por el enlazador, entonces generará un error de enlace.


La función del compilador es compilar el código que ha escrito y convertirlo en archivos de objeto. Entonces si te has perdido una ; o usó una variable indefinida, el compilador se quejará porque estos son errores de sintaxis.

Si la compilación continúa sin ningún problema, se producen los archivos del objeto . Los archivos objeto tienen una estructura compleja pero básicamente contienen cinco cosas

  1. Encabezados: la información sobre el archivo
  2. Código de objeto: código en lenguaje de máquina (este código no se puede ejecutar solo en la mayoría de los casos)
  3. Información de reubicación: qué partes del código necesitarán tener direcciones cambiadas cuando ocurra la ejecución real
  4. Tabla de símbolos: símbolos a los que hace referencia el código. Se pueden definir en este código, importarse de otros módulos o definidos por el vinculador
  5. Información de depuración: utilizada por los depuradores

El compilador compila el código y llena la tabla de símbolos con cada símbolo que encuentra. Símbolos se refiere a ambas variables y funciones. La respuesta a esta pregunta explica la tabla de símbolos.

Esto contiene una colección de código ejecutable y datos que el enlazador puede procesar en una aplicación en funcionamiento o una biblioteca compartida. El archivo objeto tiene una estructura de datos llamada tabla de símbolos que mapea los diferentes elementos en el archivo objeto con nombres que el vinculador puede comprender.

El punto a tener en cuenta

Si llama a una función desde su código, el compilador no pone la dirección final de la rutina en el archivo del objeto. En su lugar, pone un valor de marcador en el código y agrega una nota que le dice al vinculador que busque la referencia en las diversas tablas de símbolos de todos los archivos de objeto que está procesando y pegue allí la ubicación final.

Los archivos de objeto generados son procesados ​​por el vinculador que completará los espacios en tablas de símbolos, enlazará un módulo con el otro y finalmente dará el código ejecutable que puede cargar el cargador.

Entonces en tu caso específico -

  1. DefinedIncorrectFunction () - El compilador obtiene la definición de la función y comienza a compilarla para crear el código objeto e insertar la referencia apropiada en la Tabla de símbolos. La compilación falla debido a un error de sintaxis, por lo que el compilador cancela con un error.
  2. NonDefinedFunction () - El compilador obtiene la declaración pero no tiene definición, por lo que agrega una entrada a la tabla de símbolos y marca el enlazador para agregar los valores apropiados (dado que el enlazador procesará un conjunto de archivos de objetos, es posible que esta definición esté presente en otro archivo objeto ) En su caso, no especifica ningún otro archivo, por lo que el vinculador aborta con una undefined reference to NonDefinedFunction error de undefined reference to NonDefinedFunction porque no puede encontrar la referencia a la entrada de la tabla de símbolos correspondiente.

Para entenderlo, digamos que su código está estructurado de la siguiente manera

File- try.h

#include<string> #include<iostream> class Test { private: int i; public: Test(int val) {i=val ;} void DefinedCorrectFunction(int val); void DefinedIncorrectFunction(int val); void NonDefinedFunction(int val); template <class paramType> void FunctionTemplate (paramType val) { i = val; } };

Archivo try.cpp

#include "try.h" void Test::DefinedCorrectFunction(int val) { i = val; } void Test::DefinedIncorrectFunction(int val) { i = val; } int main() { Test testObject(1); testObject.NonDefinedFunction(2); //testObject.FunctionTemplate<int>(2); return 0; }

Primero copiemos y ensamblemos el código pero no lo vinculemos

$g++ -c try.cpp -o try.o $

Este paso continúa sin ningún problema. Entonces tienes el código objeto en try.o. Tratemos de vincularlo

$g++ try.o try.o: In function `main'': try.cpp:(.text+0x52): undefined reference to `Test::NonDefinedFunction(int)'' collect2: ld returned 1 exit status

Olvidó definir Test :: NonDefinedFunction. Vamos a definirlo en un archivo separado .

File- try1.cpp

#include "try.h" void Test::NonDefinedFunction(int val) { i = val; }

Vamos a compilarlo en código objeto

$ g++ -c try1.cpp -o try1.o $

De nuevo, es exitoso. Intentemos vincular solo este archivo

$ g++ try1.o /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crt1.o: In function `_start'': (.text+0x20): undefined reference to `main'' collect2: ld returned 1 exit status

No main tan won ''; t link !!

Ahora tiene dos códigos de objetos separados que tienen todos los componentes que necesita. Solo pasa AMBOS a linker y deja que haga el resto

$ g++ try.o try1.o $

¡¡No hay error!! Esto se debe a que el vinculador encuentra definiciones de todas las funciones (aunque esté disperso en diferentes archivos de objetos) y rellena los espacios en blanco en códigos objeto con los valores apropiados