0 Pregunta: Error en la codificación de asignación y no se puede resolver el símbolo externo sin resolver "público: __thiscall XOR :: XOR (int)" [duplicado]

pregunta creada en Thu, Mar 28, 2019 12:00 AM

¿Qué son los errores de símbolos externos de referencia /resolución no definidos? ¿Cuáles son las causas comunes y cómo solucionar /prevenirlas?

Siéntase libre de editar /agregar el suyo propio.

    
1344
  1. @ LuchianGrigore 'siéntase libre de agregar una respuesta' . Preferí agregar el enlace relevante (IMHO) su respuesta principal, si lo desea quiere permitir.
    2014-03-03 22: 36: 01Z
  2. @ jave.web: Mientras eso ocurre, el programador generalmente se da cuenta de que no tiene un puntero this y no tiene acceso a los miembros de la clase. Es bastante raro completar la compilación y solo fallar durante la vinculación, cuando a una función miembro no estática le falta su nombre completo.
    2015-04-27 22: 06: 05Z
  3. @ jave.web: Este fue exactamente mi problema. ¡Gracias! Soy nuevo en cpp, pero por lo que puedo decir, estaba teniendo el problema exacto que Ben Voigt dice que era bastante raro. Creo que tu solución sería una gran respuesta.
    2016-10-20 06: 42: 59Z
  4. @ Snaptastic vea stackoverflow.com/a/12574407/673730 - Un error común es olvidar calificar el nombre :)
    2016-10-20 19: 59: 39Z
  5. Recibí un error al compilar mi proyecto como proyecto x64 . y he usado una Biblioteca que se compiló como x86 . He recompilado la biblioteca como x64 y la resolvió.
    2017-02-06 13: 55: 40Z
30 Respuestas                              30                         

La compilación de un programa en C ++ se realiza en varios pasos, como se especifica en 2.2 (créditos para Keith Thompson por la referencia) :

  

La prioridad entre las reglas de sintaxis de traducción se especifica en las siguientes fases [ver nota al pie de página] .

     
  1. Los caracteres del archivo de origen físico se asignan, de una manera definida por la implementación, al conjunto de caracteres de origen básico   (introduciendo caracteres de nueva línea para los indicadores de fin de línea) si   necesario. [SNIP]
  2.   
  3. Cada instancia de un carácter de barra diagonal inversa (\) seguida inmediatamente por un carácter de nueva línea se elimina, uniendo las líneas de origen físicas a   Formar líneas de origen lógicas. [SNIP]
  4.   
  5. El archivo de origen se descompone en tokens de preprocesamiento (2.5) y secuencias de caracteres de espacios en blanco (incluidos los comentarios). [SNIP]
  6.   
  7. Se ejecutan las directivas de preprocesamiento, se expanden las invocaciones de macro y se ejecutan las expresiones de operador _Pragma unario. [SNIP]
  8.   
  9. Cada miembro del conjunto de caracteres de origen en un literal de carácter o un literal de cadena, así como cada secuencia de escape y nombre de carácter universal   en un literal de carácter o un literal de cadena no cruda, se convierte a   el miembro correspondiente del conjunto de caracteres de ejecución; [SNIP]
  10.   
  11. Los tokens literales de cadena adyacentes están concatenados.
  12.   
  13. Los caracteres de espacio en blanco que separan tokens ya no son significativos. Cada token de preprocesamiento se convierte en un token. (2.7). los   Los tokens resultantes son analizados sintácticamente y semánticamente y   traducido como una unidad de traducción. [SNIP]
  14.   
  15. Las unidades de traducción traducidas y las unidades de instanciación se combinan de la siguiente manera: [SNIP]
  16.   
  17. Todas las referencias de entidades externas se resuelven. Los componentes de la biblioteca están vinculados para satisfacer referencias externas a entidades no definidas en el   traducción actual. Toda la salida del traductor se recoge en un   Imagen del programa que contiene información necesaria para la ejecución en su   entorno de ejecución. (énfasis mío)
  18.   

[nota al pie de página] Las implementaciones deben comportarse como si se produjeran estas fases separadas, aunque en la práctica se pueden plegar diferentes fases.

Los errores especificados ocurren durante esta última etapa de compilación, más comúnmente referidosD a como enlace. Básicamente significa que compilaste un montón de archivos de implementación en archivos de objetos o bibliotecas y ahora quieres que trabajen juntos.

Digamos que definió el símbolo a en a.cpp. Ahora, b.cpp declaró ese símbolo y lo usó. Antes de enlazar, simplemente asume que ese símbolo se definió en algún lugar , pero aún no importa dónde. La fase de vinculación es responsable de encontrar el símbolo y vincularlo correctamente al b.cpp (bueno, en realidad al objeto o biblioteca que lo usa).

Si está utilizando Microsoft Visual Studio, verá que los proyectos generan .lib archivos. Estos contienen una tabla de símbolos exportados y una tabla de símbolos importados. Los símbolos importados se resuelven contra las bibliotecas con las que se vincula, y los símbolos exportados se proporcionan para las bibliotecas que usan ese .lib (si existe).

Existen mecanismos similares para otros compiladores /plataformas.

Los mensajes de error comunes son error LNK2001, error LNK1120, 0600350991100101035062 para Microsoft Visual Studio y 060035099111110101035062 symbolName para GCC

El código:

error LNK2019

generará los siguientes errores con GCC :

undefined reference to

y errores similares con Microsoft Visual Studio :

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

Las causas comunes incluyen:

774
2017-11-18 03: 51: 35Z
  1. Personalmente, creo que los mensajes de error del vinculador de MS son tan legibles como los errores de GCC. También tienen la ventaja de incluir los nombres mutilados y no enredados para los externos no resueltos. Tener el nombre mutilado puede ser útil cuando necesita mirar las bibliotecas o los archivos de objetos directamente para ver cuál podría ser el problema (por ejemplo, una discrepancia de la convención de llamada). Además, no estoy seguro de qué versión de MSVC produjo los errores aquí, pero las versiones más recientes incluyen el nombre (mutilado y desenredado) de la función que se refiere al símbolo externo no resuelto.
    2013-12-30 20: 29: 31Z
  2. David Drysdale escribió un excelente artículo sobre cómo funcionan los enlazadores: Beginner's Guide to Linkers . Dado el tema de esta pregunta, pensé que podría ser útil.
    2015-05-22 14: 48: 22Z
  3. @ TankorSmash Use gcc? MinGW para ser más precisos.
    2016-10-15 01: 17: 24Z
  4. @ luchian sería bueno si agrega el correcto, corrigiendo los errores anteriores
    2018-02-21 23:35:47Z

Miembros de la clase:

Un destructor #pragma puro necesita una implementación.

Declarar un destructor puro aún requiere que lo definas (a diferencia de una función normal):

UNICODE

Esto sucede porque se llama a los destructores de clase base cuando el objeto se destruye implícitamente, por lo que se requiere una definición.

Los métodos virtual deben implementarse o definirse como puros.

Esto es similar a los métodos no-

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition
sin definición, con el razonamiento agregado de que la declaración pura genera un dtable vtable y puede obtener el error del vinculador sin utilizar la función: virtual

Para que esto funcione, declare virtual como puro:

struct X
{
    virtual void foo();
};
struct Y : X
{
   void foo() {}
};
int main()
{
   Y y; //linker error although there was no call to X::foo
}

No X::foo() miembros de la clase

Algunos miembros deben definirse incluso si no se usan explícitamente:

struct X
{
    virtual void foo() = 0;
};

Lo siguiente produciría el error:

virtual

La implementación puede estar en línea, en la propia definición de clase:

struct A
{ 
    ~A();
};

o afuera:

A a;      //destructor undefined

Si la implementación está fuera de la definición de la clase, pero en un encabezado, los métodos deben marcarse como

struct A
{ 
    ~A() {}
};
para evitar una definición múltiple.

Todos los métodos de miembro usados ​​deben definirse si se usan.

Un error común es olvidar calificar el nombre:

A::~A() {}

La definición debería ser

inline

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}
los miembros de datos deben definirse fuera de la clase en una unidad de traducción única :

void A::foo() {}

Se puede proporcionar un inicializador para un miembro de datos static

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x
de tipo integral o de enumeración dentro de la definición de clase; sin embargo, odr-uso de este miembro todavía requerirá una definición de ámbito de espacio de nombres como se describe anteriormente. C ++ 11 permite la inicialización dentro de la clase para todos los miembros de datos static.     
165
2014-09-09 01: 06: 32Z
  1. Simplemente pensé que querría enfatizar que hacer ambas cosas es posible, y que dtor no es realmente una excepción. (No es obvio por su redacción a primera vista).
    2014-09-20 19: 26: 11Z

Error al enlazar con las bibliotecas /archivos de objetos apropiados o compilar archivos de implementación

Comúnmente, cada unidad de traducción generará un archivo de objeto que contiene las definiciones de los símbolos definidos en esa unidad de traducción. Para usar esos símbolos, debes vincularlos con esos archivos de objetos.

En gcc , especificaría todos los archivos de objetos que se vincularán en la línea de comandos o compilará los archivos de implementación.

const

El static const aquí es solo el nombre de la biblioteca, sin adiciones específicas de la plataforma. Así por ejemplo en Linux, los archivos de la biblioteca generalmente se llaman

g++ -o test objectFile1.o objectFile2.o -lLibraryName
pero solo escribirías libraryName. En Windows ese mismo archivo podría llamarse libfoo.so, pero usarías el mismo argumento. Es posible que tenga que agregar el directorio donde se pueden encontrar esos archivos usando -lfoo. Asegúrese de no escribir un espacio después de 06003509911110010105050 o foo.lib.

Para XCode : agrega las rutas de búsqueda de encabezado de usuario - > agregar la ruta de búsqueda de la biblioteca - > arrastre y suelte la referencia de la biblioteca real en la carpeta del proyecto.

En MSVS , los archivos agregados a un proyecto automáticamente tienen sus archivos de objeto vinculados entre sí y se generaría un archivo -L‹directory› (de uso común). Para usar los símbolos en un proyecto separado, deberías debe incluir los archivos -l en la configuración del proyecto. Esto se hace en la sección Vinculador de las propiedades del proyecto, en -L. (la ruta al archivo lib debe ser agregado en lib) Cuando se utiliza una biblioteca de terceros que se proporciona con un archivo Input -> Additional Dependencies, si no lo hace, generalmente se produce el error.

También puede suceder que olvide agregar el archivo a la compilación, en cuyo caso no se generará el archivo objeto. En gcc usted agregaría los archivos a la línea de comando. En MSVS , agregar el archivo al proyecto hará que se compile automáticamente (aunque los archivos pueden, de forma manual, excluirse individualmente de la compilación).

En la programación de Windows, el signo de advertencia de que no vinculó una biblioteca necesaria es que el nombre del símbolo no resuelto comienza con lib. Busque el nombre de la función en la documentación, y debe indicar qué biblioteca necesitará usar. Por ejemplo, MSDN coloca la información en un cuadro al final de cada función en una sección llamada "Biblioteca".

    
103
2016-08-23 09: 32: 00Z

Declarado pero no definió una variable o función.

Una declaración de variable típica es

Linker -> General -> Additional Library Directories

Como esto es solo una declaración, se necesita una definición única . Una definición correspondiente sería:

lib

Por ejemplo, lo siguiente generaría un error:

__imp_

Comentarios similares se aplican a las funciones. Declarar una función sin definirla lleva al error:

extern int x;

Tenga cuidado de que la función que implementa coincida exactamente con la que declaró. Por ejemplo, puede tener calificadores cv no coincidentes:

int x;

Otros ejemplos de desajustes incluyen

  • Función /variable declarada en un espacio de nombres, definida en otro.
  • Función /variable declarada como miembro de la clase, definida como global (o viceversa).
  • El tipo de retorno de la función, el número y los tipos de parámetro, y la convención de llamada no están exactamente de acuerdo.

El mensaje de error del compilador a menudo le dará la declaración completa de la variable o función que se declaró pero nunca se definió. Compárelo de cerca con la definición que proporcionó. Asegúrese de que todos los detalles coincidan.

    
94
2015-06-23 15: 54: 29Z
  1. @ RaymondChen ya está cubierto por stackoverflow.com/a/12574420/673730
    2014-08-08 10: 34: 50Z
  2. En VS, los archivos cpp que coinciden con los del encabezado
    extern int x;
    int main()
    {
        x = 0;
    }
    //int x; // uncomment this line for successful definition
    
    no agregado al directorio de origen también cae dentro de la categoría de definiciones faltantes.
    2018-03-30 12: 18: 02Z

El orden en el que se especifican las bibliotecas vinculadas interdependientes es incorrecto.

El orden en el que se vinculan las bibliotecas SÍ importa si las bibliotecas dependen unas de otras. En general, si la biblioteca

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition
depende de la biblioteca
void foo(int& x);
int main()
{
   int x;
   foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)
, entonces #includes DEBE aparecer antes de 060035099111110101035062 en las banderas de enlace.

Por ejemplo:

A

Crea las bibliotecas:

B

Compilar:

libA

Así que, para repetir, el orden ¡SÍ importa!

    
78
2014-07-10 11: 46: 08Z
  1. Lo curioso es que, en mi caso, tenía un archivo de objeto que depende de una biblioteca compartida. Tuve que modificar el Makefile y poner la biblioteca DESPUÉS del objeto con gcc 4.8.4 en Debian. En Centos 6.5 con gcc 4.4, Makefile funcionó sin problemas.
    2016-09-17 16: 28: 37Z
  2. - Wl, - start-group .....- Wl, - end-group resuelve este problema.
    2017-12-29 08: 14: 58Z

que es una "unReferencia definida /símbolo externo no resuelto "

Intentaré explicar qué es una "referencia externa no definida /símbolo externo no resuelto".

  

nota: uso g ++ y Linux y todos los ejemplos son para él

Por ejemplo, tenemos algún código

libB

y

// B.h
#ifndef B_H
#define B_H

struct B {
    B(int);
    int x;
};

#endif

// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}

// A.h
#include "B.h"

struct A {
    A(int x);
    B b;
};

// A.cpp
#include "A.h"

A::A(int x) : b(x) {}

// main.cpp
#include "A.h"

int main() {
    A a(5);
    return 0;
};

Hacer archivos de objetos

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

Después de la fase de ensamblador, tenemos un archivo de objeto, que contiene los símbolos para exportar. Mira los símbolos

$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

He rechazado algunas líneas de salida, porque no importan

Por lo tanto, vemos los siguientes símbolos para exportar.

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

src2.cpp no ​​exporta nada y no hemos visto sus símbolos

Vincular nuestros archivos de objetos

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

y ejecútalo

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

El enlazador ve los símbolos exportados y los vincula. Ahora intentamos descomentar las líneas en src2.cpp como aquí

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

y reconstruir un archivo de objeto

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

OK (sin errores), porque solo compilamos un archivo de objeto, la vinculación aún no se ha realizado. Intenta enlazar

$ g++ src1.o src2.o -o prog

Ocurrió porque nuestro nombre_var local es estático, es decir, no es visible para otros módulos. Ahora más profundamente. Obtener la salida de la fase de traducción

$ ./prog
123

Entonces, hemos visto que no hay una etiqueta para local_var_name, por eso el enlazador no lo ha encontrado. Pero somos hackers :) y podemos arreglarlo. Abra src1.s en su editor de texto y cambie

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

a

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

es decir, deberías tener como abajo

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

hemos cambiado la visibilidad de local_var_name y hemos establecido su valor en 456789. Trate de construir un archivo objeto a partir de él

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

ok, vea la salida de lectura (símbolos)

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

ahora local_var_name tiene Bind GLOBAL (was LOCAL)

enlace

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

y ejecútalo

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

ok, lo hackeamos :)

Entonces, como resultado, ocurre un "error de referencia /símbolo externo no resuelto no definido" cuando el vinculador no puede encontrar símbolos globales en los archivos de objeto.

    
69
2017-08-09 13: 30: 37Z

Los símbolos se definieron en un programa en C y se usaron en código C ++.

La función (o variable)

$ g++ -c src1.s -o src2.o
se definió en un programa C e intenta usarla en un programa C ++:
$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

El enlazador de C ++ espera que los nombres estén alterados, por lo que debe declarar la función como:

$ g++ src1.o src2.o -o prog

De manera equivalente, en lugar de definirse en un programa de C, la función (o variable)

$ ./prog 
123456789
se definió en C ++ pero con enlace C: void foo()

e intenta usarlo en un programa C ++ con enlace C ++.

Si se incluye una biblioteca completa en un archivo de encabezado (y se compiló como código C); la inclusión deberá ser la siguiente;

void foo();
int main()
{
    foo();
}
    
64
2014-10-17 07: 25: 35Z
  1. O a la inversa, si desarrolla una biblioteca de C, una buena regla es proteger los archivos de encabezado rodeando todas las declaraciones exportadas con 060035099111100101050101035062 y 060035099111010105050 (06003509911111010101035062). retorno de carro pero no puedo escribir esto correctamente en el comentario).
    2015-07-08 14: 37: 49Z
  2. Como en el comentario anterior, la sección 'Creación de encabezados de lenguaje mixto' aquí ayudó: oracle.com/technetwork/articles/servers-storage-dev/…
    2016-11-23 13: 58: 26Z

Si todo lo demás falla, recompila.

Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 solo por compilar el archivo ofensivo. Cuando volví a construir, el error desapareció.

Esto suele ocurrir cuando dos (o más) bibliotecas tienen una dependencia cíclica. La biblioteca A intenta usar símbolos en B.lib y la biblioteca B intenta usar símbolos de A.lib. Tampoco existen para empezar. Cuando intentascompile A, el paso del enlace fallará porque no puede encontrar B.lib. Se generará A.lib, pero no dll. Luego compila B, que tendrá éxito y generará B.lib. La recompilación de A ahora funcionará porque ahora se encuentra B.lib.

    
62
2013-12-03 20: 56: 14Z
  1. Correcto: esto sucede cuando las bibliotecas tienen una dependencia cíclica.
    2013-12-03 20: 51: 20Z
  2. Amplié su respuesta y la vinculé en la principal. Gracias.
    2013-12-03 20: 56: 29Z

Importando /exportando incorrectamente métodos /clases a través de módulos /dll (específico del compilador).

MSVS requiere que especifiques qué símbolos exportar e importar usando

extern "C" void foo();
int main()
{
    foo();
}
y void foo().

Esta funcionalidad dual generalmente se obtiene mediante el uso de una macro:

extern "C" void foo();

La macro

extern "C" {
    #include "cheader.h"
}
solo se definirá en el módulo que exporta la función. De esa manera, la declaración: #ifdef __cplusplus [\n] extern"C" { [\n] #endif

se expande a

#ifdef __cplusplus [\n] } [\n] #endif

y le dice al compilador que exporte la función, ya que el módulo actual contiene su definición. Al incluir la declaración en un módulo diferente, se expandiría a

[\n]

y le dice al compilador que la definición está en una de las bibliotecas con las que te vinculaste (también consulta 1) ).

También puedes importar /exportar clases:

__declspec(dllexport)     
54
2016-02-18 06: 26: 07Z
  1. Para completar, esta respuesta debe mencionar los archivos __declspec(dllimport) de GCC y 06003509911110010105050 de GCC, ya que estos también influyen en el nombre y presencia del símbolo.
    2012-12-23 15: 39: 03Z
  2. @ rubenvb No he usado los archivos
    #ifdef THIS_MODULE
    #define DLLIMPEXP __declspec(dllexport)
    #else
    #define DLLIMPEXP __declspec(dllimport)
    #endif
    
    en años. Siéntase libre de agregar una respuesta o editar esta.
    2012-12-28 19: 14: 23Z

Implementaciones de plantillas no visibles.

Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las utilizan. Eso significa que no puedes separar la definición de una plantilla a un archivo de implementación. Si debe separar la implementación, la solución habitual es tener un archivo THIS_MODULE que incluya al final del encabezado que Declara la plantilla. Una situación común es:

DLLIMPEXP void foo();

Para solucionar esto, debe mover la definición de

__declspec(dllexport) void foo();
al archivo de encabezado o a algún lugar visible para la unidad de traducción que lo usa.

Las plantillas especializadas se pueden implementar en un archivo de implementación y la implementación no tiene que ser visible, pero la especialización debe ser declarada previamente.

Para obtener una explicación más detallada y otra solución posible (creación de instancias explícita), consulte esta pregunta y su respuesta .

    
51
2017-05-23 12: 10: 46Z
  1. Puede encontrar más información sobre "las plantillas en el encabezado" en stackoverflow.com/questions/495021
    2014-07-04 21: 55: 33Z

Este es uno de los mensajes de error más confusos que todos los programadores de VC ++ han visto una y otra vez. Primero hagamos las cosas con claridad.

A. ¿Qué es el símbolo? En resumen, un símbolo es un nombre. Puede ser un nombre variable, unnombre de la función, un nombre de clase, un nombre typedef, o cualquier cosa, excepto aquellos nombres y signos que pertenecen al lenguaje C ++. Es definido por el usuario o introducido por una biblioteca de dependencia (otra definida por el usuario).

B. ¿Qué es externo? En VC ++, cada archivo de origen (.cpp, .c, etc.) Se considera como una unidad de traducción, el compilador compila una unidad a la vez y genera un archivo de objeto (.obj) para la unidad de traducción actual. (Tenga en cuenta que cada archivo de encabezado que se incluye en este archivo de origen se procesará previamente y se considerará como parte de esta unidad de traducción) Todo lo que se encuentra dentro de una unidad de traducción se considera interno, todo lo demás se considera externo. En C ++, puede hacer referencia a un símbolo externo utilizando palabras clave como

__declspec(dllimport) void foo();
,
class DLLIMPEXP X
{
};
y así sucesivamente.

C. ¿Qué es "resolver"? Resolver es un término de tiempo de enlace. En el tiempo de enlace, el enlazador intenta encontrar la definición externa para cada símbolo en los archivos de objeto que no pueden encontrar su definición internamente. El alcance de este proceso de búsqueda incluye:

  • Todos los archivos de objetos que se generaron en tiempo de compilación
  • Todas las bibliotecas (.lib) que son explícita o implícitamente especificadas como dependencias adicionales de esta aplicación de construcción.

Este proceso de búsqueda se llama resolver.

D. Finalmente, ¿por qué símbolo externo sin resolver? Si el enlazador no puede encontrar la definición externa para un símbolo que no tiene una definición interna, informa un error de Símbolo externo no resuelto.

E. Posibles causas de LNK2019 : error de Símbolo externo no resuelto. Ya sabemos que este error se debe a que el vinculador no pudo encontrar la definición de los símbolos externos, las posibles causas se pueden clasificar de la siguiente manera:

  1. La definición existe

Por ejemplo, si tenemos una función llamada foo definida en a.cpp:

visibility

En b.cpp queremos llamar a la función foo, así que agregamos

.def

para declarar la función foo () y llamarla en otro cuerpo de función, por ejemplo, .def:

impl

Ahora, cuando construyas este código, obtendrás un error LNK2019 quejándose de que foo es un símbolo sin resolver. En este caso, sabemos que foo () tiene su definición en a.cpp, pero diferente de la que estamos llamando (valor de retorno diferente). Este es el caso de que existe una definición.

  1. La definición no existe

Si queremos llamar a algunas funciones en una biblioteca, pero la biblioteca de importación no se agrega a la lista de dependencia adicional (establecida desde:

template<class T>
struct X
{
    void foo();
};

int main()
{
    X<int> x;
    x.foo();
}

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
) de la configuración de su proyecto. Ahora el enlazador informará un LNK2019 ya que la definición no existe en el ámbito de búsqueda actual.     
51
2017-11-08 09: 05: 14Z

referencia indefinida a X::foo o similar 'inusual' 0600350991100101035062 referencia de punto de entrada (especialmente para ).

Es posible que no haya elegido el tipo de proyecto correcto con su IDE real. El IDE puede querer enlazar, por ejemplo. La aplicación de Windows proyecta a dicha función de punto de entrada (como se especifica en la referencia que falta más arriba), en lugar de la firma extern utilizada comúnmente.

Si su IDE admite Proyectos de consola simple , es posible que desee elegir este tipo de proyecto, en lugar de un proyecto de aplicación de Windows.


Aquí están caso1 y case2 manejado con más detalle desde un mundo real problema.

    
38
2018-07-12 17: 39: 25Z
  1. No puedo ayudar, pero señale esta pregunta y el hecho de que esto se debe más a menudo a no tener ninguna función principal que no tener __declspec (dllimport). Los programas válidos de C ++ necesitan un
    int foo()
    {
        return 0;
    }
    
    .
    2014-03-11 03: 46: 03Z

Además, si está utilizando bibliotecas de terceros, asegúrese de tener los binarios correctos de 32/64 bits

    
35
2014-06-18 17: 06: 24Z

Microsoft ofrece un

void foo();
para hacer referencia a la biblioteca correcta en el momento del enlace; bar()

Además de la ruta de la biblioteca, incluido el directorio de la biblioteca, este debe ser el nombre completo de la biblioteca.

    
34
2014-09-10 06: 37: 35Z

El paquete Visual Studio NuGet debe actualizarse para la nueva versión del conjunto de herramientas

Acabo de tener este problema al intentar vincular libpng con Visual Studio 2013. El problema es que el archivo del paquete solo tenía bibliotecas para Visual Studio 2010 y 2012.

La solución correcta es esperar que el desarrollador lance un paquete actualizado y luego actualice, pero funcionó para mí al hackear una configuración adicional para VS2013, apuntando a los archivos de la biblioteca VS2012.

Edité el paquete (en la carpeta

void bar()
{
    foo();
}
dentro del directorio de la solución) encontrando Project | Properties | Configuration Properties | Linker | Input | Additional Dependency y dentro de ese archivo, copiando todas las secciones WinMain@16. Cambié el main() a int main(int argc, char** argv); en solo los campos de condición teniendo mucho cuidado de dejar las rutas de nombre de archivo como WinMain. Esto simplemente permitió que Visual Studio 2013 se vinculara a las bibliotecas para 2012, y en este caso, trabajado.     
32
2015-07-28 03: 52: 36Z
  1. Esto parece demasiado específico, tal vez un nuevo hilo sería un lugar mejor para esta respuesta.
    2015-01-17 13: 56: 59Z
  2. @ LuchianGrigore: Quería publicar aquí ya que esa pregunta era exactamente este problema, pero estaba marcada como un duplicado de esta pregunta, por lo que no podía responderla allí. Así que publiqué mi respuesta aquí en su lugar.
    2015-01-17 14: 00: 44Z
  3. Esa pregunta ya tiene una respuesta aceptada. Está marcado como duplicado porque la causa general se encuentra arriba. ¿Qué pasaría si tuviéramos una respuesta aquí para cada problema con una biblioteca que no está incluida?
    2015-01-17 14: 29: 10Z
  4. @ LuchianGrigore: este problema no es específico de una biblioteca, afecta a todas las bibliotecas que usan el sistema de administración de paquetes de Visual Studio. Acabo de encontrar la otra pregunta porque ambos tuvimos problemas con libpng. También tuve el mismo problema (con la misma solución) para libxml2, libiconv y glew. Esa pregunta es acerca de un problema con el sistema de administración de paquetes de Visual Studio, y mi respuesta explica el motivo y proporciona una solución alternativa. Alguien acaba de ver "externo no resuelto" y asumió que era un problema del enlazador estándar cuando en realidad es un problema de administración de paquetes.
    2015-01-17 14: 40: 40Z

    Supongamos que tienes un gran proyecto escrito en c ++ que tiene mil archivos .cpp y miles de archivos .h. Y digamos que el proyecto también depende de diez bibliotecas estáticas. Digamos que estamos en Windows y construimos nuestro proyecto en Visual Studio 20xx. Cuando presiona Ctrl + F7 Visual Studio para comenzar a compilar toda la solución (supongamos que solo tenemos un proyecto en la solución)

    ¿Cuál es el significado de compilación?

    • Visual Studio busca en el archivo .vcxproj y comienza a compilar cada archivo que tenga la extensión .cpp. El orden de compilación no está definido. Por lo tanto, no debe asumir que el archivo main.cpp se compila primero
    • Si los archivos .cpp dependen de archivos .h adicionales para encontrar símbolos que puede o no ser definido enel archivo .cpp
    • Si existe un archivo .cpp en el que el compilador no pudo encontrar un símbolo, un error de tiempo del compilador muestra el mensaje El símbolo x no se pudo encontrar
    • Para cada archivo con extensión .cpp se genera un archivo de objeto .o y también Visual Studio escribe la salida en un archivo llamado ProjectName.Cpp.Clean.txt que contiene todos los archivos de objetos que deben estar procesado por el enlazador.

    El segundo paso de la compilación lo realiza Linker. Linker debe combinar todo el archivo objeto y, finalmente, compilar la salida (que puede ser un ejecutable o una biblioteca)

    Pasos para vincular un proyecto

    • Analice todos los archivos de objetos y encuentre la definición que solo se declaró en los encabezados (por ejemplo: el código de un método de una clase como se menciona en las respuestas anteriores, o evento la inicialización de una variable estática que es miembro dentro de una clase )
    • Si no se puede encontrar un símbolo en los archivos de objetos, también se busca en bibliotecas adicionales. Para agregar una nueva biblioteca a un proyecto Propiedades de configuración - > Directorios de VC ++ - > Directorios de bibliotecas y aquí especificó una carpeta adicional para buscar bibliotecas y Propiedades de configuración - > Vinculador - > Entrada para especificar el nombre de la biblioteca. -Si el vinculador no pudo encontrar el símbolo que escribes en un .cpp, genera un error de tiempo del vinculador que puede sonar como main

    Observation

    1. Una vez que el enlazador encuentra un símbolo, no lo busca en otras bibliotecas
    2. El orden de las bibliotecas de enlaces sí importa .
    3. Si Linker encuentra un símbolo externo en una biblioteca estática, incluye el símbolo en la salida del proyecto. Sin embargo, si la biblioteca es compartida (dinámica), no incluye el código (símbolos) en la salida, pero Se pueden producir fallos en tiempo de ejecución

    Cómo resolver este tipo de error

    Error de tiempo del compilador:

    • Asegúrese de escribir su proyecto sintáctico c ++ correcto.

    Error de tiempo del enlazador

    • Defina todos los símbolos que declara en sus archivos de encabezado
    • Use #pragma para permitir que el compilador no incluya un encabezado si ya estaba incluido en el .cpp actual que está compilado
    • Asegúrese de que su biblioteca externa no contenga símbolos que puedan entrar en conflicto con otros símbolos que definió en sus archivos de encabezado
    • Cuando usa la plantilla para asegurarse de incluir la definición de cada función de plantilla en el archivo de encabezado para permitir que el compilador genere el código apropiado para cualquier instancia.
    31
    2017-11-08 10: 15: 57Z
    1. ¿No es su respuesta específica para Visual Studio? La pregunta no especifica ninguna herramienta IDE /compilador, por lo que hace que su respuesta sea inútil para una parte no visual-studio.
      2015-08-13 09: 23: 06Z
    2. Tienes razón. Pero cada proceso IDE de compilación /vinculación se realiza de manera ligeramente diferente. Pero los archivos se procesan exactamente de la misma manera (incluso g ++ hace lo mismo cuando analiza las banderas ..)
      2015-08-13 13: 01: 31Z
    3. El problema no se trata realmente del IDE, sino de una respuesta para los problemas de enlace. Los problemas de vinculación no están relacionados con el IDE, sino con el compilador y el proceso de compilación.
      2015-08-13 13: 02: 55Z
    4. Sí. Pero el proceso de creación /vinculación se realiza en g ++ /Visual Studio (compilador proporcionado por Microsoft para VS) /Eclipse /Net Beans de la misma manera
      2015-08-13 13: 06: 05Z

    Un error en el compilador /IDE

    Hace poco tuve este problema y resultó it Fue un error en Visual Studio Express 2013 . Tuve que eliminar un archivo de origen del proyecto y volver a agregarlo para superar el error.

    Pasos a seguir si crees que podría ser un error en el compilador /IDE:

    • Limpie el proyecto (algunos IDE tienen una opciónPara ello, también puedes hacerlo manualmente eliminando los archivos de objeto)
    • intenta iniciar un nuevo proyecto, copiando todo el código fuente del original.
    27
    2017-05-23 12: 18: 30Z
    1. Creer que tus herramientas están dañadas es muy probable que te aleje de la verdadera causa. Es mucho más probable que hayas cometido un error que un compilador que causó tu problema. Limpiar la solución o volver a crear la configuración de compilación puede corregir los errores de compilación, pero eso no significa que haya un error en el compilador. Microsoft no confirma que el enlace "resultó que fue un error" no es reproducible.
      2016-02-01 20: 26: 24Z
    2. @ JDiMatteo Hay 21 respuestas a esta pregunta y, por lo tanto, una cantidad significativa de respuestas no será una solución "probable". Si descartas todas las respuestas que están por debajo de tu umbral de probabilidad, entonces esta página se volverá inútil, ya que la mayoría de los casos comunes se detectan fácilmente de todos modos.
      2016-02-01 22: 00: 05Z

    Usa el enlazador para ayudar a diagnosticar el error

    La mayoría de los enlazadores modernos incluyen una opción detallada que se imprime en diversos grados;

    • Invocación de enlace (línea de comando),
    • Datos sobre qué bibliotecas se incluyen en la etapa de enlace,
    • La ubicación de las bibliotecas,
    • Rutas de búsqueda utilizadas.

    Para gcc y clang; por lo general, agregaría

    #pragma comment(lib, "libname.lib")
    
    o packages a la línea de comandos. Más detalles se pueden encontrar aquí;

    Para MSVC, se agrega packagename\build\native\packagename.targets (en particular v110) a la línea de comandos del enlace.

    25
    2016-02-24 10: 41: 08Z

    El archivo .lib vinculado está asociado a un .dll

    Tuve el mismo problema. Digamos que tengo proyectos MyProject y TestProject. Había vinculado efectivamente el archivo lib para MyProject al TestProject. Sin embargo, este archivo lib se produjo cuando se construyó la DLL para MyProject. Además, no contenía el código fuente de todos los métodos en MyProject, pero solo accedía a los puntos de entrada de la DLL.

    Para resolver el problema, creé el MyProject como un LIB y vinculé TestProject a este archivo .lib (copio el archivo .lib generado en la carpeta TestProject). Entonces puedo construir de nuevo MyProject como un DLL. Se está compilando ya que la biblioteca a la que está vinculado TestProject contiene código para todos los métodos en clases en MyProject.

        
    24
    2014-04-04 15: 02: 33Z

    Dado que las personas parecen estar dirigidas a esta pregunta cuando se trata de errores de vinculador, voy a agregar esto aquí.

    Una posible causa de los errores del vinculador con GCC 5.2.0 es que ahora se elige una nueva biblioteca ABI libstdc ++ de forma predeterminada.

      

    Si recibe errores de vinculador sobre referencias no definidas a símbolos que involucran tipos en el espacio de nombres std :: __ cxx11 o la etiqueta [abi: cxx11], entonces probablemente indica que está intentando enlazar archivos de objetos que se compilaron con diferentes valores para la macro _GLIBCXX_USE_CXX11_ABI. Esto suele suceder cuando se vincula a una biblioteca de terceros que se compiló con una versión anterior de GCC. Si la biblioteca de terceros no se puede reconstruir con el nuevo ABI, entonces deberá recompilar su código con el antiguo ABI.

    Entonces, si de repente aparece un error en el enlazador al cambiar a GCC después de 5.1.0 esto sería una cosa para revisar.

        
    19
    2015-09-10 11: 03: 35Z

    Un contenedor alrededor de GNU ld que no admite scripts de vinculador

    Algunos archivos .so son en realidad GNU lder linkts scripts , por ejemplo El archivo libtbb.so es un archivo de texto ASCII con este contenido:

    v120

    Algunas construcciones más complejas pueden no ser compatibles con esto. Por ejemplo, si incluye -v en las opciones del compilador, puede ver que mainwin gcc wrapper mwdip descarta los archivos de comandos de script del vinculador en la lista de resultados detallados de las bibliotecas a enlazar. Una solución alternativa es reemplazar el archivo de comando de entrada del script de vinculador con una copia del archivo (o un enlace simbólico), por ejemplo,

    v110

    O puede reemplazar el argumento -l con la ruta completa de los .so, por ejemplo. en lugar de error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ) hacer #pragma once

        
    17
    2015-03-30 20: 47: 37Z

    Plantillas de amistad ...

    Dado el fragmento de código de un tipo de plantilla con un operador amigo (o función);

    -v -Wl,--verbose

    El -v -Wl,-v se está declarando como una función sin plantilla. Para cada tipo /VERBOSE usado con /VERBOSE:LIB, debe haber un 06003509911110010105062 sin plantilla. Por ejemplo, si hay un tipo 0600350991111101010505050 declarado, entonces debe haber una implementación del operador como sigue; /VERBOSE

    Dado que no está implementado, el vinculador no puede encontrarlo y produce el error.

    Para corregir esto, puede declarar un operador de plantilla antes del tipo

    INPUT (libtbb.so.2)
    
    y luego declarar como amigo, la instanciación adecuada. La sintaxis es un poco incómoda, pero se ve de la siguiente manera;
    cp libtbb.so.2 libtbb.so
    

    El código anterior limita la amistad del operador con la creación de instancias correspondiente de -ltbb, es decir, la creación de instancias de /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2 está limitada para acceder a los miembros privados de la creación de instancias de 060035099110010105050.

    Las alternativas incluyen;

    • Permitiendo que la amistad se extienda a todas las instancias de las plantillas, de la siguiente manera:

      template <typename T>
      class Foo {
          friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
      };
      
    • O, la implementación para el operator<< se puede hacer en línea dentro de la definición de la clase;

      T

    Nota , cuando la declaración del operador (o función) solo aparece en la clase, el nombre no está disponible para la búsqueda "normal", solo para la búsqueda dependiente del argumento, desde cppreference ;

      

    Un nombre declarado por primera vez en una declaración de amigo dentro de la clase o la plantilla de clase X se convierte en miembro del espacio de nombres que encierra más interno de X, pero no es accesible para la búsqueda (excepto la búsqueda dependiente del argumento que considera X) a menos que haya una declaración coincidente en el se proporciona el ámbito del espacio de nombres ...

    Hay más lecturas en la plantilla de amigos en cppreference y el Preguntas frecuentes de C ++ .

    Listado de códigos que muestra las técnicas anteriores .


    Como una nota al lado del ejemplo de código defectuoso; g ++ advierte sobre esto de la siguiente manera

      

    Foo

         

    operator<<

        
    16
    2016-06-10 21: 29: 24Z

    Su enlace consume bibliotecas antes que los archivos de objetos que se refieren a ellas

    • Está intentando compilar y vincular su programa con la cadena de herramientas de GCC.
    • Su enlace especifica todas las bibliotecas necesarias y las rutas de búsqueda de bibliotecas
    • Si Foo<int> depende de
      std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
      
      , entonces su enlace coloca correctamente Foo antes de
      // forward declare the Foo
      template <typename>
      class Foo;
      
      // forward declare the operator <<
      template <typename T>
      std::ostream& operator<<(std::ostream&, const Foo<T>&);
      
      template <typename T>
      class Foo {
          friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
          // note the required <>        ^^^^
          // ...
      };
      
      template <typename T>
      std::ostream& operator<<(std::ostream&, const Foo<T>&)
      {
        // ... implement the operator
      }
      
      .
    • Tu enlace falla con Foo algo .
    • Pero todos los algo no definidos están declarados en los archivos de encabezado que tiene operator<< <int>d y, de hecho, se definen en las bibliotecas que está vinculando.

    Los ejemplos están en C. También podrían ser C ++

    Un ejemplo mínimo que involucra una biblioteca estática que usted mismo construyó

    mi_lib.c

    Foo<int>

    mi_lib.h

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    

    eg1.c

    operator<<

    Construyes tu biblioteca estática:

    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };
    

    Usted compila su programa:

    warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]

    Intenta vincularlo con note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) y falla:

    libfoo

    El mismo resultado si compilas y vinculas en un solo paso, como:

    libbar

    Un ejemplo mínimo que involucra una biblioteca de sistema compartida, la biblioteca de compresión libfoo

    eg2.c

    libbar

    Compila tu programa:

    undefined reference to

    Intente vincular su programa con #include y falle:

    #include "my_lib.h"
    #include <stdio.h>
    
    void hw(void)
    {
        puts("Hello World");
    }
    

    Lo mismo si compilas y vinculas de una sola vez:

    #ifndef MY_LIB_H
    #define MT_LIB_H
    
    extern void hw(void);
    
    #endif
    

    Y una variación en el ejemplo 2 que implica

    #include <my_lib.h>
    
    int main()
    {
        hw();
        return 0;
    }
    
    :
    $ gcc -c -o my_lib.o my_lib.c
    $ ar rcs libmy_lib.a my_lib.o
    

    ¿Qué estás haciendo mal?

    En la secuencia de archivos de objetos y bibliotecas que desea vincular para que su programa, usted está colocando las bibliotecas antes de los archivos de objetos que se refieren a ellos. Debe colocar las bibliotecas después de los archivos de objetos que se refieren a ellos.

    Enlace el ejemplo 1 correctamente:

    $ gcc -I. -c -o eg1.o eg1.c
    

    Éxito:

    libmy_lib.a

    Enlace el ejemplo 2 correctamente:

    $ gcc -o eg1 -L. -lmy_lib eg1.o 
    eg1.o: In function `main':
    eg1.c:(.text+0x5): undefined reference to `hw'
    collect2: error: ld returned 1 exit status
    

    Éxito:

    $ gcc -o eg1 -I. -L. -lmy_lib eg1.c
    /tmp/ccQk1tvs.o: In function `main':
    eg1.c:(.text+0x5): undefined reference to `hw'
    collect2: error: ld returned 1 exit status
    

    Enlace el ejemplo 2 libz variación correctamente:

    #include <zlib.h>
    #include <stdio.h>
    
    int main()
    {
        printf("%s\n",zlibVersion());
        return 0;
    }
    

    La explicación

    La lectura es opcional a partir de aquí .

    De forma predeterminada, un comando de vinculación generado por GCC, en su distro, consume los archivos en el enlace de izquierda a derecha en secuencia de comandos Cuando encuentra que un archivo se refiere a algo y no contiene una definición para ello, buscará una definición en archivos más a la derecha. Si finalmente encuentra una definición, la Se resuelve la referencia. Si alguna referencia queda sin resolver al final, el enlace falla: el enlazador no busca hacia atrás.

    Primero, ejemplo 1 , con la biblioteca estática

    $ gcc -c -o eg2.o eg2.c
    

    Una biblioteca estática es un archivo indexado de archivos de objetos. Cuando el enlazador encuentra libz en la secuencia de enlace y se da cuenta de que esto se refiere a la biblioteca estática

    $ gcc -o eg2 -lz eg2.o 
    eg2.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    collect2: error: ld returned 1 exit status
    
    , quiere saber si su programa necesita alguno de los archivos objeto en
    $ gcc -o eg2 -I. -lz eg2.c
    /tmp/ccxCiGn7.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    collect2: error: ld returned 1 exit status
    
    .

    Solo hay un archivo de objeto en pkg-config, concretamente

    $ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
    eg2.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    
    , y solo hay una cosa definida en
    $ gcc -o eg1 eg1.o -L. -lmy_lib
    
    , a saber, la función
    $ ./eg1 
    Hello World
    
    .

    El enlazador decidirá que su programa necesita

    $ gcc -o eg2 eg2.o -lz
    
    si y solo si ya sabe eso su programa hace referencia a
    $ ./eg2 
    1.2.8
    
    , en uno o más de los archivos objeto que ya tiene agregado al programa, y ​​que ninguno de los archivos objeto ya ha agregado contiene una definición para pkg-config.

    Si eso es cierto, entonces el enlazador extraerá una copia de

    $ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
    $ ./eg2
    1.2.8
    
    de la biblioteca y Añádelo a tu programa. Entonces, su programa contiene una definición para my_lib.a, por lo que sus referencias a -lmy_lib están resueltas .

    Cuando intentas vincular el programa como:

    ./libmy_lib.a

    el enlazador no ha agregado libmy_lib.a al programa cuando ve libmy_lib.a. Porque en ese momento, no se ha visto my_lib.o. Su programa todavía no hace ninguna referencia a my_lib.o: aún no hace ninguna referencia en absoluto , porque todas las referencias que hace están en hw.

    Así que el enlazador no agrega my_lib.o al programa y no tiene más utilizar para hw.

    A continuación, encuentra hw, y lo agrega a ser programa. Un archivo objeto en el La secuencia de enlace siempre se agrega al programa. Ahora, el programa hace una referencia a my_lib.o, y no contiene una definición de hw; pero no queda nada en la secuencia de enlace que podría proporcionar la falta definición. La referencia a hw termina sin resolver , y el enlace falla.

    Segundo, ejemplo 2 , con biblioteca compartida

    $ gcc -o eg1 -L. -lmy_lib eg1.o
    

    Una biblioteca compartida no es un archivo de archivos de objetos ni nada parecido. Sus mucho más como un programa que no tiene una función eg1.o y en su lugar, expone varios otros símbolos que define, de modo que otros Los programas pueden usarlos en tiempo de ejecución.

    Muchas distribuciones de Linux configuran hoy su cadena de herramientas GCC de modo que sus controladores de idioma (-lmy_lib,0600350991111101035062,eg1.o, etc.) instruya al vinculador del sistema (hw) para vincular las bibliotecas compartidas en una base según sea necesario . Tienes una de esas distribuciones.

    Esto significa que cuando el enlazador encuentra eg1.o en la secuencia de enlace, y se da cuenta de que esto se refiere a la biblioteca compartida (por ejemplo) my_lib.o, desea saber si las referencias que ha agregado a su programa que aún no están definidas tienen definiciones exportadas por libmy_lib.a

    Si eso es cierto, entonces el enlazador no copiará cualquier fragmento de eg1.o y Agrégalos a tu programa; En su lugar, solo se encargará del código de su programa. de modo que: -

    • En el tiempo de ejecución, el cargador del programa del sistema cargará una copia de hw en el el mismo proceso que su programa cada vez que carga una copia de su programa, para ejecutarlo.

    • En tiempo de ejecución, siempre que su programa haga referencia a algo que se define en hw, esa referencia utiliza la definición exportada por la copia de hw en el mismo proceso.

    Su programa quiere referirse a una sola cosa que tiene una definición exportada por libz, a saber, la función main, que se menciona solo una vez, en gcc. Si el enlazador agrega esa referencia a su programa y luego encuentra la definición exportado por g++, la referencia está resuelta

    Pero cuando intentas vincular el programa como:

    gfortran

    el orden de los eventos es incorrecto de la misma manera que en el ejemplo 1. En el momento en que el enlazador encuentra ld, hay no referencias a cualquier cosa en el programa: están todos en -lz, que aún no se ha visto. Entonces el el enlazador decide que no tiene uso para /usr/lib/x86_64-linux-gnu/libz.so. Cuando llega a libz, lo agrega al programa, y luego tiene una referencia indefinida a libz, la secuencia de enlace está terminada; esa referencia no está resuelta y el enlace falla.

    Por último, la variación libz del ejemplo 2 tiene una explicación ahora obvia. Después de la expansión de shell:

    libz

    se convierte en:

    libz

    que es solo el ejemplo 2 de nuevo.

    Puedo reproducir el problema en el ejemplo 1, pero no en el ejemplo 2

    El enlace:

    libz

    funciona bien para ti!

    (O: ese vínculo funcionó bien para usted en, por ejemplo, Fedora 23, pero falla en Ubuntu 16.04)

    Esto se debe a que la distribución en la que funciona el enlace es una de las que no configura su cadena de herramientas GCC para vincular las bibliotecas compartidas según sea necesario .

    En el pasado, era normal que los sistemas similares a Unix vincularan la estática y la compartida. Bibliotecas de diferentes reglas. Se enlazaron bibliotecas estáticas en una secuencia de enlace. En la base según sea necesario explicada en el ejemplo 1, las bibliotecas compartidas se vincularon sin condiciones.

    Este comportamiento es económico en el momento del enlace porque el enlazador no tiene que reflexionar si el programa necesita una biblioteca compartida: si se trata de una biblioteca compartida, vincularlo. Y la mayoría de las bibliotecas en la mayoría de los enlaces son bibliotecas compartidas. Pero también hay desventajas: -

    • No es económico en runtime , ya que puede hacer que las bibliotecas compartidas sean cargado junto con un programa, incluso si no los necesita.

    • Las diferentes reglas de enlace para bibliotecas estáticas y compartidas pueden ser confusas a los programadores inexpertos, que pueden no saber si zlibVersion en su enlace se va a resolver a eg2.c o a libz, y puede que no entienda la diferencia entre bibliotecas compartidas y estáticas de todos modos.

    Esta compensación ha llevado a la situación cismática de hoy. Algunas distros tienen cambiaron sus reglas de vinculación de GCC para bibliotecas compartidas de modo que el según sea necesario El principio se aplica a todas las bibliotecas. Algunas distros se han pegado con las viejas. manera.

    ¿Por qué sigo teniendo este problema incluso si compilo y vinculo al mismo tiempo?

    Si acabo de hacer:

    gcc -o eg2 -lz eg2.o
    

    seguramente gcc tiene que compilar -lz primero, y luego vincular el resultante archivo de objeto con eg2.o. Entonces, ¿cómo no puede saber ese archivo de objeto se necesita cuando se está haciendo el enlace?

    Porque compilar y enlazar con un solo comandono cambia el orden de la secuencia de enlace.

    Cuando ejecuta el comando anterior, libz se da cuenta de que desea compilación + enlace. Así que detrás de las escenas, genera un comando de compilación, y se ejecuta luego genera un comando de vinculación y lo ejecuta, como si usted hubiera ejecutado el dos comandos:

    eg2.o

    Por lo tanto, la vinculación falla igual que si do ejecuta esos dos comandos. los La única diferencia que se nota en el fallo es que gcc ha generado una Archivo de objeto temporal en el caso de compilación + enlace, porque no lo dices para utilizar zlibVersion. Vemos:

    pkg-config

    en lugar de:

    gcc -o eg2 $(pkg-config --libs zlib) eg2.o
    

    Ver también

    El orden en el que se especifican las bibliotecas vinculadas interdependientes es incorrecto

    Poner las bibliotecas interdependientes en el orden incorrecto es solo una forma en el que puede obtener archivos que necesitan definiciones de las cosas que vienen más adelante en el enlace que los archivos que proporcionan las definiciones. Poniendo bibliotecas antes de la Los archivos de objetos que se refieren a ellos son otra forma de cometer el mismo error.

        
    14
    2017-05-23 12: 34: 53Z

    Inconsistentes
    gcc -o eg2 -lz eg2.o
    
    definiciones

    Se ha creado una carga de la carga del tipo de carga de la carga de la carga de un sistema de carga con el número de referencia de
    gcc -o eg2 -lz eg2.o
    
    , p. ej. /msdn.microsoft.com/en-us/library/windows/desktop/dd374131(v=vs.85).aspx "rel =" noreferrer ">" -lfoo "tipos de cadena ; /some/where/libfoo.a, /some/where/libfoo.so y sus alces.

    La creación de una biblioteca con

    $ gcc -o eg1 -I. -L. -lmy_lib eg1.c
    
    definida y el intento de vincularla en un proyecto donde no se define eg1.c dará lugar a errores de vinculador ya que habrá una falta de coincidencia en la definición de 06003509911111010105050; libmy_lib.a vs. gcc.

    El error generalmente incluye una función, un valor con un tipo derivado

    $ gcc -I. -c -o eg1.o eg1.c
    $ gcc -o eg1 -L. -lmy_lib eg1.o
    
    o eg1.o, esto también podría incluir
    /tmp/ccQk1tvs.o: In function `main'
    
    , etc. Cuando se navega a través de la función afectada en el código, a menudo habrá una referencia a
    eg1.o: In function `main':
    
    o UNICODE, etc. Esto es un signo revelador de que el código fue originalmente diseñado para un UNICODE y un carácter de varios bytes (o "estrecho" ) construir.

    Para corregir esto, genere todas las bibliotecas y proyectos necesarios con una definición coherente de TCHAR (y 0600350991100101035062).

    1. Esto se puede hacer con cualquiera de los dos;

      wchar_t
    2. O en la configuración del proyecto;

        

      Propiedades del proyecto > General > Valores predeterminados del proyecto > Conjunto de caracteres

    3. O en la línea de comando;

      UNICODE

    La alternativa también es aplicable, si UNICODE no está diseñado para usarse, asegúrese de que no se hayan definido las definiciones y /o se use la configuración de caracteres múltiples en los proyectos y se aplique de manera consistente.

    No te olvides de ser coherente entre las compilaciones "Release" y "Debug" también.

        
    13
    2017-05-12 06: 06: 08Z

    Cuando sus rutas de inclusión son diferentes

    Los errores del enlazador pueden ocurrir cuando un archivo de encabezado y su biblioteca compartida asociada (archivo .lib) no están sincronizados. Dejame explicar.

    ¿Cómo funcionan los enlazadores? El enlazador hace coincidir una declaración de función (declarada en el encabezado) con su definición (en la biblioteca compartida) comparando sus firmas. Puede obtener un error de vinculador si el vinculador no encuentra una definición de función que coincida perfectamente.

    ¿Es posible obtener un error de vinculador aún cuando la declaración y la definición parecen coincidir? ¡Sí! Pueden tener el mismo aspecto en el código fuente, pero realmente depende de lo que ve el compilador. Esencialmente podrías terminar con una situación como esta:

    TCHAR

    Observe cómo, aunque ambas declaraciones de funciones parecen idénticas en el código fuente, pero son realmente diferentes según el compilador.

    Podrías preguntarte cómo terminas en una situación como esa. Incluir rutas por supuesto! Si al compilar la biblioteca compartida, la ruta de inclusión incluye char y termina usando UNICODE en su propio programa, se quedará rascando su encabezado preguntándose qué sucedió (juego de palabras).

    A continuación se explica un ejemplo de cómo puede suceder esto en el mundo real.

    Mayor elaboración con un ejemplo

    Tengo dos proyectos: _UNICODE y T. Ambos proyectos dependen de LPTSTR. Supongamos que la biblioteca exporta la siguiente función:

    LPCTSTR

    Y luego continúa e incluye la biblioteca en tu propio proyecto.

    UNICODE

    ¡Boom! Recibes un error de vinculador y no tienes idea de por qué está fallando. La razón es que la biblioteca común utiliza diferentes versiones de la misma versión UNICODE (Lo he hecho obvio aquí en el ejemplo al incluir una ruta diferente, pero puede que no siempre sea tan obvio. Tal vez la ruta de inclusión sea diferente en la configuración del compilador ).

    Nota en este ejemplo, el enlazador le diría que no pudo encontrar TCHAR, cuando en realidad sabe que obviamente está siendo exportado por la biblioteca. Podría pasar horas rascándose la cabeza preguntándose qué salió mal. El problema es que el vinculador ve una firma diferente porque los tipos de parámetros son ligeramente diferentes. En el ejemplo, char es un tipo diferente en ambos proyectos en lo que respecta al compilador. Esto podría suceder porque provienen de dos archivos de inclusión ligeramente diferentes (tal vez los archivos de inclusión provienen de dos versiones diferentes de la biblioteca).

    Depurando el enlazador

    DUMPBIN es tu amigo, si estás utilizando Visual Studio. Estoy seguro de que otros compiladores tienen otras herramientas similares.

    El proceso es así:

    1. Tenga en cuenta el extraño nombre que aparece en el error del vinculador. (p. ej., draw @ graphics @ XYZ).
    2. Volcar los símbolos exportados de la biblioteca en un archivo de texto.
    3. Busque el símbolo de interés exportado y observe que el nombre mutilado es diferente.
    4. Preste atención a por qué los nombres destrozados terminaron siendo diferentes. Podrías ver que los tipos de parámetros son diferentes, aunque se vean iguales en el código fuente.
    5. Por lo que son diferentes. En el ejemplo anterior, son diferentes debido a los diferentes archivos de inclusión.

    [1] Por proyecto me refiero a un conjunto de archivos de origen que están vinculados entre sí para generar una biblioteca o un ejecutable.

    EDIT 1: reescribe la primera sección para que sea más fácil de entender. Por favor comente a continuación para hacerme saber si es necesario arreglar algo más. Gracias!

        
    13
    2017-11-12 00: 10: 26Z

    Limpiar y reconstruir

    Una "limpieza" de la compilación puede eliminar la "madera muerta" que puede quedar en las compilaciones anteriores, compilaciones fallidas, compilaciones incompletas y otros problemas de compilación relacionados con el sistema de compilación.

    En general, el IDE o la compilación incluirán alguna forma de función "limpia", pero es posible que no esté configurada correctamente (por ejemplo, en un archivo de configuración manual) o que falle (por ejemplo, los archivos binarios intermedios o resultantes son de solo lectura).

    Una vez que se haya completado la "limpieza", verifique que la "limpieza" haya tenido éxito y que todos los archivos intermedios generados (por ejemplo, un makefile automático) hayan sido eliminados con éxito.

    Este proceso puede verse como un recurso final, pero a menudo es un buen primer paso ; especialmente si el código relacionado con el error se ha agregado recientemente (ya sea localmente o desde el repositorio de origen).

        
    12
    2016-02-24 12: 41: 09Z

    Falta "extern" en wchar_t declaraciones /definiciones de variables (solo C ++)

    Para las personas que provienen de C, podría ser una sorpresa que en C ++ global charvariables tengan vínculos internos (o estáticos). En C, este no fue el caso, ya que todas las variables globales son implícitamente wchar_t (es decir, cuando falta la palabra clave 0600350991100101035062).

    Ejemplo:

    std::basic_string<>

    correcto sería usar un archivo de encabezado e incluirlo en file2.cpp y file1.cpp

    TCHAR

    Alternativamente, se podría declarar la variable std::basic_string<TCHAR> en el archivo1.cpp con el 06003509911110010105062 explícito

        
    7
    2017-08-03 08: 01: 49Z

    A pesar de que esta es una pregunta bastante antigua con múltiples respuestas aceptadas, me gustaría compartir cómo resolver un error oscuro "referencia indefinida a".

    Diferentes versiones de bibliotecas

    Estaba usando un alias para referirme a UNICODE: el sistema de archivos está en la biblioteca estándar desde C ++ 17 pero mi programa necesitaba también compilar en C ++ 14 , así que decidí usar una variable alias:

    _UNICODE

    Digamos que tengo tres archivos: main.cpp, file.h, file.cpp:

    • file.h # include's < experimental :: filestystem > y contiene el código anterior
    • file.cpp , la implementación de file.h, # include " file.h "
    • main.cpp # include's < filestystem > y " file.h "

    Tenga en cuenta las bibliotecas diferentes utilizadas en main.cpp y file.h. Desde main.cpp # include'd " file.h " después de < filestystem &gt ;, la versión del sistema de archivos utilizada era la C ++ 17 una . Solía ​​compilar el programa con los siguientes comandos:

    $

    #define UNICODE
    #define _UNICODE
    
    - > compila main.cpp a main.o
    $
    /DUNICODE /D_UNICODE
    
    - > compila file.cpp y file.h a file.o
    $
    // header1.h
    typedef int Number;
    void foo(Number);
    
    // header2.h
    typedef float Number;
    void foo(Number); // this only looks the same lexically
    
    - > enlaces main.o y file.o

    De esta manera cualquier función contenida en file.o y utilizada en main.o que requería header1.h dio errores de "referencia indefinida" porque main.o se refirió a header2.h pero file.o a graphics.lib .

    Resolución

    Para solucionar esto, solo necesitaba cambiar < experimental :: filestystem > en archivo.h a < filestystem > .

        
    3
    2018-10-14 11: 44: 14Z

    Al enlazar con bibliotecas compartidas, asegúrese de que los símbolos utilizados no estén ocultos.

    El comportamiento predeterminado de gcc es que todos los símbolos son visibles. Sin embargo, cuando las unidades de traducción se construyen con la opción main.exe, solo las funciones /símbolos marcados con common_math.h son externos en el objeto compartido resultante.

    Puede verificar si los símbolos que busca son externos invocando:

    // graphics.lib    
    #include "common_math.h" 
    
    void draw(vec3 p) { ... } // vec3 comes from common_math.h
    

    los símbolos ocultos /locales se muestran en

    // main.exe
    #include "other/common_math.h"
    #include "graphics.h"
    
    int main() {
        draw(...);
    }
    
    con el tipo de símbolo en minúscula, por ejemplo common_math.h en lugar de `T para la sección de código: draw()

    También puede utilizar vec3 con la opción const para desmarcar los nombres (si se usó C ++).

    Similar a Windows-dlls, uno marcaría las funciones públicas con una definición, por ejemplo const definida como:

    extern

    Lo que corresponde aproximadamente a la versión de Windows /MSVC:

    static

    Más información sobre visibilidad se puede encontrar en la wiki de gcc.


    Cuando una unidad de traducción se compila con

    // file1.cpp
    const int test = 5;    // in C++ same as "static const int test = 5"
    int test2 = 5;
    
    // file2.cpp
    extern const int test;
    extern int test2;
    
    void foo()
    {
     int x = test;   // linker error in C++ , no error in C
     int y = test2;  // no problem
    }
    
    , los símbolos resultantes tienen un enlace externo (se muestra con mayúsculas y minúsculas en
    extern const int test;
    extern int test2;
    
    ) y se pueden usar para enlaces externos sin problema si los archivos de objetos se convierten en parte de una biblioteca estática. La vinculación se vuelve local solo cuando los archivos de objetos están vinculados a una biblioteca compartida.

    Para encontrar qué símbolos en un archivo de objeto están ocultos, ejecute:

    const     
    2
    2018-09-10 08: 18: 38Z
    1. Debes usar extern o std::filesystem::path para ver los símbolos externos. También vea Visibilidad en la wiki de GCC.
      2018-09-08 03: 42: 47Z

    Diferentes arquitecturas

    Puede ver un mensaje como:

    #if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
    using path_t = std::experimental::filesystem::path;
    #elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
    using path_t = std::filesystem::path;
    #endif
    

    En ese caso, significa que el disponiblelos símbolos son para una arquitectura diferente a la que está compilando.

    En Visual Studio, esto se debe a la "Plataforma" incorrecta, y debes seleccionar la adecuada o instalar la versión correcta de la biblioteca.

    En Linux, puede deberse a una carpeta de biblioteca incorrecta (utilizando g++ -g -std=c++17 -c main.cpp en lugar de g++ -g -std=c++17 -c file.cpp por ejemplo).

    En MacOS, existe la opción de enviar ambas arquitecturas en el mismo archivo. Puede ser que el enlace espere que ambas versiones estén allí, pero solo una está. También puede ser un problema con la carpeta incorrecta g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs/path_t donde se recoge la biblioteca.

        
    0
    2018-12-10 16: 40: 04Z
    std::filesystem::path
fuente colocada aquí