¿Qué es un error de referencia/símbolo externo no definido y cómo lo soluciono?


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

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

Author: Baum mit Augen, 2012-09-25

29 answers

Compilar un programa C++ se lleva a cabo en varios pasos, como se especifica en 2.2 (créditos a Keith Thompson por la referencia):

La precedencia entre las reglas sintácticas de la traducción se especifica mediante las siguientes fases [ver nota al pie].

  1. Los caracteres de los archivos de origen físicos se asignan, de una manera definida por la implementación, al conjunto de caracteres de origen básico (introducción de caracteres de nueva línea para los indicadores de final de línea) si necesario. [SNIP]
  2. Cada instancia de un carácter de barra invertida (\) seguida inmediatamente por un carácter de nueva línea se elimina, formar líneas de origen lógicas. [SNIP]
  3. El archivo fuente se descompone en tokens de preprocesamiento (2.5) y secuencias de caracteres de espacio en blanco (incluidos los comentarios). [SNIP]
  4. Se ejecutan las directivas de preprocesamiento, se expanden las invocaciones de macro y el operador único _Pragma las expresiones se ejecutan. [SNIP]
  5. 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 raw, se convierte en el miembro correspondiente del conjunto de caracteres de ejecución; [SNIP]
  6. Los tokens literales de cadena adyacentes se concatenan.
  7. Los espacios en blanco que separan los tokens ya no son significativos. Cada el token de preprocesamiento se convierte en un token. (2.7). El los tokens resultantes se analizan sintáctica y semánticamente y traducido como una unidad de traducción. [SNIP]
  8. Las unidades de traducción traducidas y las unidades de instanciación se combinan de la siguiente manera: [SNIP]
  9. Se resuelven todas las referencias de entidades externas. Los componentes de la biblioteca están vinculados para satisfacer referencias externas a entidades no definidas en el traducción actual. Todo tal traductor la salida se recoge en un imagen del programa que contiene la información necesaria para la ejecución en su entorno de ejecución. (énfasis mío)

[footnote] Las implementaciones deben comportarse como si estas fases separadas ocurrieran, aunque en la práctica diferentes fases podrían estar juntas.

Los errores especificados ocurren durante esta última etapa de compilación, más comúnmente conocida como vinculación. Básicamente significa que compilaste un montón de archivos de implementación en archivos objeto o bibliotecas y ahora desea que funcionen juntos.

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

Si eres usando Microsoft Visual Studio, verás que los proyectos generan archivos .lib. 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 enlaza, y los símbolos exportados se proporcionan para las bibliotecas que usan .lib (si las hay).

Existen mecanismos similares para otros compiladores/ plataformas.

Los mensajes de error comunes son error LNK2001, error LNK1120, error LNK2019 para Microsoft Visual Studio y undefined reference to symbolNamefor GCC.

El código:

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;
}

Generará los siguientes errores con GCC :

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

Y errores similares con Microsoft Visual Studio:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

Las causas comunes incluyen:

 716
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-18 03:51:35

Miembros de la clase:

Un destructor puro virtual necesita una implementación.

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

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

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

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

Esto es similar a los métodos no-virtual sin definición, con el añadido razonando que la declaración pure genera una vtable ficticia y puede obtener el error del enlazador sin usar la función:

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

Para que esto funcione, declarad X::foo() como puro:{[25]]}

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

No - virtual miembros de la clase

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

struct A
{ 
    ~A();
};

Lo siguiente produciría el error:

A a;      //destructor undefined

La implementación puede ser inline, en la propia definición de clase:

struct A
{ 
    ~A() {}
};

O exterior:

A::~A() {}

Si la implementación está fuera de la definición de la clase, pero en un encabezado, los métodos deben marcarse como inline para evitar una definición múltiple.

Todos los métodos miembros utilizados deben definirse si se utilizan.

Un error común es olvidar calificar el nombre:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

La definición debe ser

void A::foo() {}

static los miembros de datos deben definirse fuera de la clase en una unidad de traducción única :

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

An inicializador se puede proporcionar para un static const miembro de datos de tipo integral o enumeración dentro de la definición de clase; sin embargo, el uso de odr de este miembro aún requerirá una definición de ámbito de espacio de nombres como se describió anteriormente. C++11 permite la inicialización dentro de la clase para todos los miembros de datos static const.

 154
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-09 01:06:32

Fallo al enlazar con bibliotecas/archivos objeto apropiados o compilar archivos de implementación

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

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

g++ -o test objectFile1.o objectFile2.o -lLibraryName

El libraryName aquí es solo el nombre desnudo de la biblioteca, sin adiciones específicas de la plataforma. Así, por ejemplo, en Linux los archivos de la biblioteca se llaman normalmente libfoo.so pero solo escribirías -lfoo. En Windows ese mismo archivo podría llamarse foo.lib, pero usarías el mismo argumento. Es posible que tenga que agregar el directorio donde se pueden encontrar esos archivos usando -L‹directory›. Asegúrese de no escribir un espacio después de -l o -L.

Para XCode : Agregue 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.

Bajo MSVS, los archivos agregados a un proyecto automáticamente tienen sus archivos objeto vinculados entre sí y se generaría un archivo lib (en uso común). Para usar los símbolos en un proyecto separado, es necesario incluir los archivos lib en la configuración del proyecto. Esto se hace en la sección Enlazador de las propiedades del proyecto, en Input -> Additional Dependencies. (la ruta al archivo lib debe ser añadido en Linker -> General -> Additional Library Directories) Cuando se utiliza una biblioteca de terceros que se proporciona con un archivo lib, si no lo hace, generalmente se produce el error.

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

En Programación de Windows, el signo revelador de que no vinculó una biblioteca necesaria es que el nombre del símbolo sin resolver comienza con __imp_. Busque el nombre de la función en la documentación, y debería decir qué biblioteca necesita usar. Por ejemplo, MSDN pone la información en un cuadro en la parte inferior de cada función en una sección llamada "Biblioteca".

 100
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-23 09:32:00

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

Una declaración de variable típica es

extern int x;

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

int x;

Por ejemplo, lo siguiente generaría un error:

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

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

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

Tenga cuidado de que la función que implementa coincida exactamente con la uno que declaraste. Por ejemplo, es posible que tenga calificadores de CV no coincidentes:

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)

Otros ejemplos de desajustes incluyen

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

El mensaje de error del compilador a menudo le dará el declaración de la variable o función que fue declarada pero nunca definida. Compáralo de cerca con la definición que proporcionaste. Asegúrese de que cada detalle coincida.

 93
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-23 15:54:29

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

El orden en el que las bibliotecas están vinculadas importa si las bibliotecas dependen unas de otras. En general, si library A depende de library B, entonces libA DEBE aparecer antes de libB en las banderas del enlazador.

Por ejemplo:

// 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;
};

Crear las bibliotecas:

$ 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

Compilar:

$ 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

Así que para repetir de nuevo, el orden HACE importa!

 75
Author: Svalorzen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-10 11:46:08

Qué es una "referencia indefinida/símbolo externo no resuelto"

Trataré de explicar qué es una "referencia indefinida/símbolo externo no resuelto".

Nota: uso g++ y Linux y todos los ejemplos son para ello

Por ejemplo tenemos algún código

// 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;
}

Y

// 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);
}

Hacer archivos objeto

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

Después de la fase de ensamblador tenemos un archivo objeto, que contiene cualquier símbolo a exportar. Mira el símbolos

$ 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]

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

Por lo tanto, vemos seguir símbolos para exportar.

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

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

Enlazar nuestros archivos objeto

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

Y ejecutarlo

$ ./prog
123

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

// 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);
}

Y reconstruir un archivo objeto

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

OK (sin errores), porque solo construir archivo de objeto, la vinculación no se hace todavía. Intenta vincular

$ 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

Ha ocurrido porque nuestro local_var_name 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

$ 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

Por lo tanto, hemos visto que no hay ninguna etiqueta para local_var_name, es por eso que linker no la ha encontrado. Pero somos hackers :) y podemos arreglarlo. Abra src1.s en su editor de texto y cambiar

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

A

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

Es decir, usted debe tener como abajo

    .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:
; ...

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

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

Ok, ver readelf output (símbolos)

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

Ahora local_var_name tiene Bind GLOBAL (era LOCAL)

Enlace

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

Y ejecutarlo

$ ./prog 
123456789

Ok, lo hackeamos:)

Entonces, como resultado, se produce un "error de referencia no definida/símbolo externo no resuelto" cuando el enlazador no puede encontrar símbolos globales en archivos objeto.

 65
Author: Kastaneda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-09 13:30:37

Los símbolos fueron definidos en un programa C y usados en código C++.

La función (o variable) void foo() se definió en un programa C y se intenta usarla en un programa C++:

void foo();
int main()
{
    foo();
}

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

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

De forma equivalente, en lugar de definirse en un programa en C, la función (o variable) void foo() se definió en C++ pero con enlace C:

extern "C" void foo();

E intenta usarlo en un programa C++ con C++ vinculación.

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

extern "C" {
    #include "cheader.h"
}
 62
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-17 07:25:35

Si todo lo demás falla, recompile.

Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 simplemente recompilando el archivo ofensivo. Cuando reconstruí, 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. Ninguno existe para empezar. Cuando intenta compilar A, el paso de enlace fallará porque no puede encontrar a B. Lib. A. se generará lib, pero no dll. Luego compila B, que tendrá éxito y generará B. lib. Volver a compilar A ahora funcionará porque ahora se encuentra B. lib.

 59
Author: sgryzko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-03 20:56:14

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

MSVS requiere que especifique qué símbolos exportar e importar usando __declspec(dllexport) y __declspec(dllimport).

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

#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif

La macro THIS_MODULE solo se definiría en el módulo que exporta la función. Así, la declaración:

DLLIMPEXP void foo();

Se expande a

__declspec(dllexport) void foo();

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

__declspec(dllimport) void foo();

Y le dice al compilador que la definición está en una de las bibliotecas con las que ha enlazado (véase también 1)).

Puede importar / exportar clases similares:

class DLLIMPEXP X
{
};
 51
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-18 06:26:07

Las implementaciones de plantillas no son visibles.

Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las utilicen. Eso significa que no puede 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 impl que incluya al final del encabezado que declara la plantilla. Una situación común es:

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

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

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}

Para arreglar esto, debe mover la definición de X::foo a la archivo de cabecera o algún lugar visible para la unidad de traducción que lo utiliza.

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 declararse previamente.

Para mayor explicación y otra posible solución (instanciación explícita) ver esta pregunta y respuesta.

 49
Author: Luchian Grigore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:46

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 claras.

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

B. ¿Qué es externo? En VC++, cada archivo fuente (.cpp,.c, etc.) se considera como una unidad de traducción, el compilador compila una unidad a la vez, y genera un archivo objeto (.obj) para la actual dependencia de traducción. (Tenga en cuenta que cada archivo de cabecera que este archivo fuente incluido será preprocesado y se considerará como parte de esta unidad de traducción)Todo dentro de una unidad de traducción se considera como interno, todo lo demás se considera como externo. En C++, puede hacer referencia a un símbolo externo utilizando palabras clave como extern, __declspec (dllimport) y así sucesivamente.

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

  • Todos los archivos objeto que se generan en tiempo de compilación
  • Todas las bibliotecas (.lib) que son explícita o implícitamente especificado como dependencias adicionales de este edificio aplicación.

Este proceso de búsqueda se llama resolve.

D. Finalmente, ¿por qué Un Símbolo Externo Sin Resolver? Si el enlazador no puede encontrar la definición externa de un símbolo que no tiene definición interna, informa de 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 enlazador no pudo encontrar la definición de símbolos externos, las posibles causas se puede ordenar como:

  1. La definición existe

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

int foo()
{
    return 0;
}

En b.cpp queremos llamar a la función foo, por lo que añadimos

void foo();

Para declarar function foo (), y llamarla en otro cuerpo de función, diga bar():

void bar()
{
    foo();
}

Ahora, cuando construya este código, obtendrá 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 (diferente valor de retorno). Este es el caso de que la definición existe.

  1. La definición no existe

Si queremos llamar a algunas funciones de una biblioteca, pero la biblioteca de importación no se agrega a la lista de dependencias adicionales (establecida desde: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) de la configuración de su proyecto. Ahora el enlazador reportará un LNK2019 ya que la definición no existe en el ámbito de búsqueda actual.

 48
Author: Nima Soroush,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 09:05:14

Referencia indefinida a WinMain@16 o similar 'inusual' main() referencia del punto de entrada (especialmente para visual-studio ).

Es posible que no haya elegido el tipo de proyecto correcto con su IDE real. El IDE puede querer enlazar, por ejemplo, proyectos de aplicaciones de Windows a dicha función de punto de entrada (como se especifica en la referencia faltante anterior), en lugar de la firma comúnmente utilizada int main(int argc, char** argv);.

Si su IDE es compatible con Proyectos de consola simples es posible que desee elija este tipo de proyecto, en lugar de un proyecto de aplicación de Windows.


Aquí están case1 y case2 manejados con más detalle desde un problema del mundo real.

 34
Author: πάντα ῥεῖ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-12 17:39:25

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

 32
Author: Dula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-18 17:06:24

Microsoft ofrece un #pragma para hacer referencia a la biblioteca correcta en el momento del enlace;

#pragma comment(lib, "libname.lib")

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

 32
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-10 06:37:35

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 de paquete solo tenía bibliotecas para Visual Studio 2010 y 2012.

La solución correcta es esperar que el desarrollador publique 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 packages dentro del directorio de la solución) encontrando packagename\build\native\packagename.targets y dentro de ese archivo, copiando todas las secciones v110. Cambié el v110 a v120 en los campos de condición solo teniendo mucho cuidado de dejar las rutas del nombre de archivo como v110. Esto simplemente permitió que Visual Studio 2013 se vinculara a las bibliotecas de 2012, y en este caso, funcionó.

 31
Author: Malvineous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-28 03:52:36

Supongamos que tienes un gran proyecto escrito en c++ que tiene miles de .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 buscar en el archivo .vcxproj y empezar a compilar cada archivo que tiene la extensión .cpp. Orden de compilación es undefined.So no debe asumir que el archivo principal.cpp se compila primero
  • Si .archivos cpp depende de adicional .archivos h para encontrar símbolos eso puede o no estar definido en el archivo .cpp
  • Si existe uno .archivo cpp en el que el compilador no pudo encontrar un símbolo, un error de tiempo del compilador levanta el mensaje El símbolo x no pudo ser encontrado
  • Para cada archivo con extensión .cpp se genera un archivo objeto .o y también Visual Studio escribe la salida en un archivo llamado ProjectName.Cpp.Limpiar.txt que contiene todos los archivos objeto que deben ser procesados por el enlazador.

El Segundo paso de la compilación lo realiza el Enlazador.El enlazador debe fusionar todo el archivo objeto y construir finalmente la salida ( que puede ser un ejecutable o una biblioteca)

Pasos Para Vincular un proyecto

  • Analizar todos los archivos objeto y encontrar la definición que solo se declaró en encabezados (por ejemplo: El código de un método de una clase como se menciona en respuestas anteriores, o evento la inicialización de una variable estática que es miembro dentro de una clase)
  • Si no se pudo encontrar un símbolo en los archivos objeto, también se busca en Bibliotecas Adicionales.Para agregar una nueva biblioteca a un proyecto Propiedades de configuración -> Directorios VC++ -> Biblioteca Directorios y aquí especificó una carpeta adicional para buscar bibliotecas y propiedades de configuración -> Enlazador -> Input para especificar el nombre de la biblioteca. -Si el Enlazador no pudo encontrar el símbolo que escribes en uno .cpp plantea un error de tiempo enlazador que puede sonar como error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

Observación

  1. Una vez que el Enlazador encuentra un símbolo, no lo busca en otras bibliotecas
  2. El orden de enlazar bibliotecas sí importa.
  3. Si el enlazador encuentra un símbolo externo en una biblioteca estática, lo incluye 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 En tiempo de ejecución pueden ocurrir bloqueos

Cómo resolver este tipo de error

Error de Tiempo del Compilador:

  • Asegúrese de escribir correctamente la sintaxis de su proyecto c++.

Enlazador Error de tiempo

  • Define todos los símbolos que declaras en tus archivos de cabecera
  • Use #pragma once para permitir que el compilador no incluya un encabezado si ya estaba incluido en el actual .cpp que se compilan
  • Asegúrese de que su biblioteca externa no contenga símbolos que puedan entrar en conflicto con otros símbolos que haya definido en sus archivos de encabezado
  • Cuando utilice la plantilla para asegurarse de incluir la definición de cada función de plantilla en el encabezado archivo para permitir que el compilador genere el código apropiado para cualquier instancia.
 30
Author: eFarzad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-08 10:15:57

Un error en el compilador/IDE

Recientemente tuve este problema, y resultó que era un error en Visual Studio Express 2013. Tuve que eliminar un archivo fuente del proyecto y volver a añadirlo 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ón para hacer esto, también puede hacerlo manualmente eliminando los archivos objeto)
  • Intente iniciar un nuevo proyecto, copiar todo el código fuente de el original.
 26
Author: developerbmw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:30

Vinculado .el archivo lib está asociado a un .dll

Tuve el mismo problema. Digamos que tengo proyectos MyProject y TestProject. Había vinculado efectivamente el archivo lib de MyProject al TestProject. Sin embargo, este archivo lib fue producido como el DLL para el MyProject fue construido. Además, no contuve código fuente para todos los métodos en el MyProject, sino solo acceso a los puntos de entrada de la DLL.

Para resolver el problema, construí el MyProject como una LIB, y vinculé TestProject a esto .lib archivo (copio y pego el generado .archivo lib en la carpeta TestProject). Entonces puedo construir de nuevo MyProject como DLL. Está compilando ya que la lib a la que está enlazado TestProject contiene código para todos los métodos en clases en MyProject.

 23
Author: octoback,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-04 15:02:33

Utilice el enlazador para ayudar a diagnosticar el error

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

  • Invocación de enlace (línea de comandos),
  • 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; normalmente agregaría -v -Wl,--verbose o -v -Wl,-v a la línea de comandos. Más detalles se pueden encontrar aquí;

Para MSVC, /VERBOSE (en particular /VERBOSE:LIB) se añade a la línea de comandos link.

 23
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 10:41:08

Dado que la gente parece estar dirigida a esta pregunta cuando se trata de errores de enlazador, voy a agregar esto aquí.

Una posible razón para los errores del enlazador con GCC 5.2.0 es que ahora se elige una nueva biblioteca ABI de libstdc++ por defecto.

Si obtiene errores de enlazador sobre referencias indefinidas a símbolos que involucran tipos en el espacio de nombres std::__cxx11 o la etiqueta [abi: cxx11], probablemente indique que está tratando de vincular archivos objeto que fueron compilados con diferentes valores para la macro _GLIBCXX_USE_CXX11_ABI. Esto sucede comúnmente cuando se enlaza 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 la nueva ABI, entonces tendrá que recompilar su código con la ABI antigua.

Así que si de repente tienes errores de enlazador al cambiar a un GCC después de 5.1.0 esto sería una cosa para comprobar.

 18
Author: Plankalkül,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-10 11:03:35

Un envoltorio alrededor de GNU ld que no soporta scripts enlazadores

Algunos archivos. so son en realidad scripts enlazadores ld de GNU , por ejemplo libtbb.so el archivo es un archivo de texto ASCII con este contenido:

INPUT (libtbb.so.2)

Algunas compilaciones más complejas pueden no soportar esto. Por ejemplo, si incluye-v en las opciones del compilador, puede ver que el mainwin gcc wrapper mwdip descarta los archivos de comandos del script del enlazador en la lista de salida detallada de bibliotecas para vincular. Un un trabajo simple es reemplazar el archivo de comandos de entrada del script enlazador con una copia del archivo en su lugar (o un enlace simbólico), por ejemplo,

cp libtbb.so.2 libtbb.so

O podría reemplazar el argumento-l con la ruta completa de .so, por ejemplo, en lugar de -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

 17
Author: JDiMatteo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-30 20:47:37

Hacer amistad con plantillas...

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

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

El operator<< se declara como una función que no es plantilla. Para cada tipo T usado con Foo, debe haber un operator<< no templado. Por ejemplo, si hay un tipo Foo<int> declarado, entonces debe haber una implementación de operador de la siguiente manera;

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

Dado que no está implementado, el enlazador no lo encuentra y resulta en error.

Para corregir esto, puede declarar un operador de plantilla antes del tipo Foo y luego declarar como amigo, la instanciación apropiada. La sintaxis es un poco incómoda, pero se ve de la siguiente manera;

// 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
}

El código anterior limita la amistad del operador a la instanciación correspondiente de Foo, es decir, la instanciación operator<< <int> está limitada al acceso a los miembros privados de la instanciación de Foo<int>.

Alternativas include;

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

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

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

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 un miembro del espacio de nombres 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 se proporcione una declaración coincidente en el ámbito del espacio de nombres...

Hay más información sobre amigos de plantillas en cppreference y el C++ FAQ.

Listado de código que muestra las técnicas above .


Como nota al margen del ejemplo de código fallido; g++ advierte sobre esto de la siguiente manera

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

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

 16
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-10 21:29:24

Inconsistente UNICODE definiciones

Una compilación de Windows UNICODE se construye con TCHAR etc. se define como wchar_t etc. Cuando no se construye con UNICODE definido como construir con TCHAR definido como char etc. Estos UNICODE y _UNICODE define afectan a todos los "T" tipos de cadenas; LPTSTR, LPCTSTR y sus alces.

Construir una biblioteca con UNICODE definida e intentar vincularla en un proyecto donde UNICODE no está definida dará lugar a errores de enlazador ya que habrá un desajuste en la definición de TCHAR; char vs wchar_t.

El error generalmente incluye una función un valor con un tipo derivado char o wchar_t, estos podrían incluir std::basic_string<> etc. También. Al navegar a través de la función afectada en el código, a menudo habrá una referencia a TCHAR o std::basic_string<TCHAR>, etc. Este es un signo revelador de que el código fue diseñado originalmente para una construcción UNICODE y Multi-Byte Character (o "narrow").

Para corregir esto, construya todas las bibliotecas necesarias y proyectos con una definición coherente de UNICODE (y _UNICODE).

  1. Esto se puede hacer con cualquiera;

    #define UNICODE
    #define _UNICODE
    
  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 comandos;

    /DUNICODE /D_UNICODE
    

La alternativa también es aplicable, si UNICODE no está destinado a ser utilizado, asegúrese de que las definiciones no están establecidas, y / o el multi-carácter la configuración se utiliza en los proyectos y se aplica consistentemente.

No se olvide de ser consistente entre las compilaciones "Release" y "Debug" también.

 13
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-12 06:06:08

Limpiar y reconstruir

Una "limpieza" de la construcción puede eliminar la "madera muerta" que puede quedar por ahí de construcciones anteriores, construcciones fallidas, construcciones incompletas y otros problemas de construcción relacionados con el sistema de construcción.

En general, el IDE o la compilación incluirán alguna forma de función "limpia", pero esto puede no estar configurado correctamente (por ejemplo, en un makefile manual) o puede fallar (por ejemplo, los binarios intermedios o resultantes son de solo lectura).

Una vez que la" limpieza " se ha completado, verifique que el "limpiar" ha tenido éxito y todo el archivo intermedio generado (por ejemplo, un makefile automatizado) se ha eliminado 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 fuente).

 12
Author: Niall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-24 12:41:09

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

  • Está tratando de compilar y vincular su programa con la cadena de herramientas GCC.
  • Su enlace especifica todas las bibliotecas necesarias y rutas de búsqueda de bibliotecas
  • Si libfoo depende libbar, entonces su vinculación correctamente pone libfoo antes de libbar.
  • Su enlace falla con undefined reference to algo errores.
  • Pero todo lo indefinido algo s se declaran en los archivos de cabecera que tiene #include d y de hecho se definen en las bibliotecas que está enlazando.

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ó

My_lib.c

#include "my_lib.h"
#include <stdio.h>

void hw(void)
{
    puts("Hello World");
}

My_lib.h

#ifndef MY_LIB_H
#define MT_LIB_H

extern void hw(void);

#endif

Eg1.c

#include <my_lib.h>

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

Construyes tu biblioteca estática:

$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o

Usted compila su programa:

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

Intenta vincularlo con libmy_lib.a y falla:

$ 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

El mismo resultado si compila y enlaza en un solo paso, como:

$ 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

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

Eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n",zlibVersion());
    return 0;
}

Compila tu programa:

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

Intenta vincular tu programa con libz y falla:

$ 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

Lo mismo si compilas y enlazas de una sola vez:

$ 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

Y a variación del ejemplo 2 que involucra pkg-config:

$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'

¿Qué estás haciendo mal?

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

Enlace ejemplo 1 correctamente:

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

Éxito:

$ ./eg1 
Hello World

Ejemplo de enlace 2 correctamente:

$ gcc -o eg2 eg2.o -lz

Éxito:

$ ./eg2 
1.2.8

Enlaza correctamente el ejemplo 2 pkg-config variación:

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

La explicación

La lectura es opcional de aquí en adelante.

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

Primero, ejemplo 1 , con biblioteca estática my_lib.a

Una biblioteca estática es un archivo indexado de archivos objeto. Cuando el enlazador encuentra -lmy_lib en la secuencia de enlace y se da cuenta de que esto se refiere a la biblioteca estática ./libmy_lib.a, quiere saber si su programa necesita cualquiera de los archivos objeto en libmy_lib.a.

Solo hay un archivo objeto en libmy_lib.a, a saber, my_lib.o, y solo hay una cosa definida en my_lib.o, es decir, la función hw.

El enlazador decidirá que su programa necesita my_lib.o si y solo si ya lo sabe su programa se refiere a hw, en uno o más de los archivos objeto que ya tiene añadido al programa, y que ninguno de los archivos objeto ya ha añadido contiene una definición para hw.

Si eso es cierto, entonces el enlazador extraerá una copia de my_lib.o de la biblioteca y agrégalo a tu programa. Entonces, su programa contiene una definición para hw, así sus referencias a hw son resueltas.

Cuando intenta vincular el programa como:

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

El enlazador no ha añadido eg1.o al programa cuando ve -lmy_lib. Porque en ese momento, no ha visto eg1.o. Su programa todavía no hace ninguna referencias a hw: it todavía no hace ninguna referencia en absoluto, porque todas las referencias que hace están en eg1.o.

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

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

Segundo, ejemplo 2, con biblioteca compartida libz

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

Muchas distribuciones de Linux hoy configuran su cadena de herramientas GCC para que sus controladores de idioma(gcc,g++,gfortran etc) indique al enlazador del sistema (ld) que vincule bibliotecas compartidas en según sea necesario. Tienes una de esas distros.

Esto significa que cuando el enlazador encuentra -lz en la secuencia de enlace, y se da cuenta de que esto se refiere a la biblioteca compartida (por ejemplo) /usr/lib/x86_64-linux-gnu/libz.so, quiere saber si alguna referencia que ha añadido a su programa que aún no están definidos tienen definiciones que son exportadas por libz

Si eso es cierto, entonces el enlazador no copiará cualquier fragmento de libz y añádalos a su programa; en su lugar, solo medirá el código de su programa así que: -

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

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

Su programa quiere hacer referencia a una sola cosa que tiene una definición exportada por libz, es decir, la función zlibVersion, a la que se hace referencia una sola vez, en eg2.c. Si el enlazador agrega esa referencia a su programa, y luego encuentra la definición exportados por libz, la referencia es resuelto

Pero cuando intenta vincular el programa como:

gcc -o eg2 -lz eg2.o

El orden de los eventos es incorrecto de la misma manera que con el ejemplo 1. En el punto en que el enlazador encuentra -lz, hay no referencias a nada en el programa: están todos en eg2.o, que aún no se ha visto. Así que el el enlazador decide que no tiene uso para libz. Cuando llega a eg2.o, lo agrega al programa, y luego tiene referencia indefinida a zlibVersion, la secuencia de enlace está terminada; esa referencia no está resuelta, y la conexión falla.

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

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

Se convierte en:

gcc -o eg2 -lz eg2.o

Que es solo el ejemplo 2 de nuevo.

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

El vínculo:

gcc -o eg2 -lz eg2.o

Funciona muy bien para usted!

(O: Ese enlace funcionó bien para usted en, digamos, Fedora 23, pero falla en Ubuntu 16.04)

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

En su día, era normal que los sistemas tipo unix enlazaran estática y compartida bibliotecas por diferentes reglas. Se vincularon bibliotecas estáticas en una secuencia de enlace en la base según sea necesario explicada en el ejemplo 1, pero las bibliotecas compartidas estaban vinculadas incondicionalmente.

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

  • Es antieconómico en tiempo de ejecución , porque 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 confuso a los programadores inexpertos, que pueden no saber si -lfoo en su vinculación se va a resolver a /some/where/libfoo.a o a /some/where/libfoo.so, y podría no entender la diferencia entre bibliotecas compartidas y estáticas Por cierto.

Esta compensación ha llevado a la situación cismática actual. Algunas distribuciones tienen cambió sus reglas de enlace GCC para bibliotecas compartidas para que cuando sea necesario el principio se aplica a todas las bibliotecas. Algunas distribuciones se han quedado con el antiguo manera.

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

Si solo lo hago:

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

Seguramente gcc tiene que compilar eg1.c primero, y luego vincular el resultado archivo objeto con libmy_lib.a. Entonces, ¿cómo puede no saber que el archivo objeto es necesario cuando se está haciendo el enlace?

Porque compilar y enlazar con un solo comando no cambia la orden de la secuencia de enlace.

Cuando ejecuta el comando anterior, gcc descubre que quieres compilación + vinculación. Así que detrás de las escenas, genera un comando de compilación, y se ejecuta luego genera un comando de enlace, y lo ejecuta, como si usted hubiera ejecutado el dos órdenes:

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

Así que el enlace falla como lo hace si hace ejecutar esos dos comandos. El la única diferencia que nota en el fallo es que gcc ha generado un archivo de objeto temporal en el caso compile + link, porque no lo estás diciendo utilizar eg1.o. Nos véase:

/tmp/ccQk1tvs.o: In function `main'

En lugar de:

eg1.o: In function `main':

Véase también

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

Poner las bibliotecas interdependientes en el orden equivocado es solo una manera en el que puede obtener archivos que necesitan definiciones de cosas que vienen más tarde en el enlace que los archivos que proporcionan las definiciones. Poner las bibliotecas antes de la archivos objeto que se refieren a ellos es otra forma de cometiendo el mismo error.

 11
Author: Mike Kinghan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:53

Cuando sus rutas de inclusión son diferentes

Los errores de enlazador pueden ocurrir cuando un archivo de encabezado y su biblioteca compartida asociada (.archivo lib) salir de sincronización. Déjame explicarte.

¿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 enlazador si el enlazador no encuentra una definición de función que coincida perfectamente.

Es posible todavía obtener un error enlazador a pesar de que 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 vea el compilador. Esencialmente podrías terminar con una situación como esta:

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

Tenga en cuenta que aunque ambas declaraciones de función parecen idénticas en el código fuente, son realmente diferentes según el compilador.

Usted podría preguntarse cómo uno termina en una situación como esa? Incluye caminos ¡por supuesto! Si al compilar la biblioteca compartida, la ruta de inclusión conduce a header1.h y termina usando header2.h en su propio programa, se quedará rascando su encabezado preguntándose qué sucedió (juego de palabras).

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

Elaboración adicional con un ejemplo

Tengo dos proyectos: graphics.lib y main.exe. Ambos proyectos dependen de common_math.h. Supongamos que la biblioteca exporta la siguiente función:

// graphics.lib    
#include "common_math.h" 

void draw(vec3 p) { ... } // vec3 comes from common_math.h

Y luego tú sigue adelante e incluye la biblioteca en tu propio proyecto.

// main.exe
#include "other/common_math.h"
#include "graphics.h"

int main() {
    draw(...);
}

Boom! Obtienes un error de enlazador y no tienes idea de por qué está fallando. La razón es que la biblioteca común utiliza diferentes versiones de la misma include common_math.h (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 es diferente en la configuración del compilador).

Nota en este ejemplo, el enlazador te diría que no pudo encontrar draw(), cuando en realidad, usted sabe que obviamente está siendo exportado por la biblioteca. Podrías pasar horas rascándote la cabeza preguntándote qué salió mal. La cosa es, el enlazador ve una firma diferente porque los tipos de parámetros son ligeramente diferentes. En el ejemplo, vec3 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).

Depurar el enlazador

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

El proceso es así: {[14]]}

  1. Tenga en cuenta el extraño nombre estropeado dado en el error del enlazador. (eg. 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. Pagar atención a por qué los nombres destrozados terminaron siendo diferentes. Usted sería capaz de ver que los tipos de parámetros son diferentes, a pesar de que se ven iguales en el código fuente.
  5. Razón por la que son diferentes. En el ejemplo anterior, son diferentes debido a diferentes archivos de inclusión.

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

EDITAR 1: Reescribió la primera sección para ser más fácil de entender. Por favor, comente a continuación para hacerme saber si algo más necesita ser arreglado. ¡Gracias!

 11
Author: fafaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-12 00:10:26

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

Para las personas que vienen de C podría ser una sorpresa que en C++ global constlas variables tienen enlace interno (o estático). En C este no fue el caso, ya que todas las variables globales son implícitamente extern (es decir, cuando falta la palabra clave static).

Ejemplo:

// 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
}

Correcto sería usar un archivo de cabecera e incluirlo en file2.cpp y file1.cpp

extern const int test;
extern int test2;

Alternativamente uno podría declarar la variable const en file1.cpp con extern

 7
Author: Andreas H.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-03 08:01:49

Aunque 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 hacer referencia a std::filesystem::path: 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 un alias variable:

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

Digamos que tengo tres archivos: principal.cpp, archivo.h, file.cpp:

  • archivo.h # include experimental:: filestystem > y contiene el código anterior
  • archivo.cpp , la implementación del archivo.h, # incluye el archivo ".h "
  • main.cpp #incluye el sistema de archivos > y el archivo ".h "

Tenga en cuenta las diferentes bibliotecas utilizadas en main.cpp y archivo.h. Desde main.cpp # incluye el archivo " .h" después de filestystem>, la versión de el sistema de archivos utilizado fue el C++17. Solía compilar el programa con los siguientes comandos:

$ g++ -g -std=c++17 -c main.cpp -> compila main.cpp principal.o
$ g++ -g -std=c++17 -c file.cpp -> compila el archivo.cpp y archivo.h a archivo.o
links g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs -> enlaces principales.o y archivo.o

De esta manera cualquier función contenida en el archivo.o y utilizado en principal.o que requerido path_t dio errores de "referencia indefinida" porque main.o que se refiere std::filesystem::path pero archivo.o a std::experimental::filesystem::path.

Resolución

Para arreglar esto solo necesitaba cambiar <:filestystem> en archivo.h a .

 2
Author: Stypox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-27 18:12:32

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 -fvisibility=hidden, solo las funciones/símbolos marcados con __attribute__ ((visibility ("default"))) son externos en el objeto compartido resultante.

Puede comprobar si los símbolos que busca son externos invocando:{[17]]}

# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL 

Los símbolos ocultos/locales se muestran mediante nm con minúsculas tipo de símbolo, por ejemplo t en lugar de ' T para la sección de código:

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

También puede usar nm con la opción -C para desmangular los nombres (si se usó C++).

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

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

Que corresponde aproximadamente a Windows ' / MSVC-version:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

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


Cuando una unidad de traducción se compila con -fvisibility=hidden los símbolos resultantes todavía tienen enlace externo (se muestra con el tipo de símbolo en mayúsculas por nm) y se puede utilizar para enlace externo sin problema si los archivos objeto se convierten en parte de una bibliotecas estáticas. El enlace se convierte en local solo cuando los archivos objeto están vinculados a una biblioteca compartida.

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

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
 1
Author: ead,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-10 08:18:38