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

¿Cuáles son los errores no definidos de referencia/símbolos externos no resueltos? ¿Cuáles son las causas comunes, y cómo puedo corregir y prevenir estos errores?

Pregunta hecha hace 11 años, 5 meses, 2 días - Por devdynamo


11 Respuestas:

  • Digamos que tiene el siguiente código:

    // a.cpp
    int get() { return 0; }
    
    // b.cpp
    int get(); // usually, one doesn't write this directly, but gets these
               // declarations from included header files
    int x = get();
    

    Cuando se compilan b.cpp, el compilador simplemente supone que get() símbolo definido en alguna partePero aún no le importa dónde. La fase de enlace es responsable de encontrar el símbolo y vincular correctamente los archivos del objeto producidos a partir de a.cpp y b.cpp.

    Si a.cpp no definió get, usted conseguiría un error de enlace diciendo "referencia no definida" o "símbolo externo no resuelto".

    C++ Wording estándar

    Compilar un programa C+ tiene lugar en varias fases especificadas en [lex.phases], el último de los cuales es relevante:

    9. Todas las referencias de entidad externa se resuelven. Los componentes de la biblioteca están vinculados a satisfacer referencias externas a entidades no definidas en la traducción actual. Toda la salida de este traductor se recoge en una imagen del programa que contiene información necesaria para la ejecución en su entorno de ejecución.

    See Respuesta de Keith Thompson para un resumen de estas fases.

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

    Errores de Linker en la práctica

    Si utiliza 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 a las que se enlaza, y los símbolos exportados se proporcionan para las bibliotecas que usan eso .lib (si la hay).

    Existen mecanismos similares para otros compiladores/ plataformas.

    Mensajes comunes de error error LNK2001, error LNK1120, error LNK2019 para Microsoft Visual Studio y undefined reference to símboloName para 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
    

    Causas comunes

    Respondida el Sep 24, 2012 a las 22:35 - por codesculptor

    Votos positivos: 0 | Votos negativos: 0

  • Miembros de clase:

    Un puro virtual El destructor necesita una implementación.

    Declarar un destructor puro todavía requiere que usted lo defina (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 es destruido implícitamente, por lo que se requiere una definición.

    virtual los métodos deben ser implementados o definidos como puros.

    Esto es similar a no-virtual métodos sin definición, con el razonamiento añadido que la declaración pura genera un dummy vtable y usted podría obtener el error de enlace 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, declarar X::foo() como puro:

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

    Non-virtual miembros de clase

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

    struct A
    { 
        ~A();
    };
    

    A continuación se produciría el error:

    A a;      //destructor undefined
    

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

    struct A
    { 
        ~A() {}
    };
    

    o fuera:

    A::~A() {}
    

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

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

    Un error común se olvida de 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 los datos deben definirse fuera de la clase en un 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
    

    Un inicializador puede ser proporcionado para un static const data member of integral or enumeration type within the class definition; however, odr-use of this member will still require a namespace scope definition as described above. C++11 permite la inicialización dentro de la clase para todos static const miembros de datos.

    Respondida el Sep 24, 2012 a las 22:40 - por binarybrainiac

    Votos positivos: 0 | Votos negativos: 0

  • No vincularse con bibliotecas apropiadas o archivos de objetos o compilar archivos de implementación

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

    Under gcc especificaría todos los archivos de objetos que deben estar conectados juntos en la línea de comandos, o compilar los archivos de implementación juntos.

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

    -l... debe ser el derecho de cualquier persona .o/.c/.cpp archivos.

    El libraryName aquí está el nombre desnudo de la biblioteca, sin adiciones específicas de la plataforma. Así que, por ejemplo, en los archivos de la biblioteca de Linux se suelen llamar libfoo.so pero sólo escribirías -lfoo. En Windows ese mismo archivo podría llamarse foo.libPero 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 -l o -L.

    Para Xcode: Agregue el Usuario Header Search Paths - confiar agregue la Biblioteca Search Path - confiar arrastrar y soltar la referencia de la biblioteca real en la carpeta del proyecto.

    Under MSVS, los archivos añadidos a un proyecto automáticamente tienen sus archivos de objetos conectados y un lib archivo se generaría (en uso común). Para usar los símbolos en un proyecto separado, necesidad de incluir el lib archivos en la configuración del proyecto. Esto se hace en la sección Linker de las propiedades del proyecto, en Input -> Additional Dependencies. (el camino hacia el lib archivo debe ser añadidos Linker -> General -> Additional Library Directories) Al utilizar una biblioteca de terceros que se proporciona con una lib archivo, la falta de hacerlo generalmente resulta en el error.

    También puede suceder que te olvides de añadir el archivo a la compilación, en cuyo caso el archivo objeto no será generado. In gcc añadiría los archivos a la línea de comandos. In MSVS añadir el archivo al proyecto lo hará compilar automáticamente (aunque los archivos pueden, manualmente, ser excluidos individualmente de la compilación).

    En la programación de Windows, el signo narrativo de que no viniste una biblioteca necesaria es que el nombre del símbolo no resuelto comienza con __imp_. Busque el nombre de la función en la documentación, y debe decir qué biblioteca necesita usar. Por ejemplo, MSDN pone la información en una caja en la parte inferior de cada función en una sección llamada "Library".

    Respondida el Sep 24, 2012 a las 22:50 - por htmlhero

    Votos positivos: 0 | Votos negativos: 0

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

    Una declaración variable típica es

    extern int x;
    

    Como esta es sólo una declaración, una Definición individual es necesario. 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
    

    Se aplican observaciones similares 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 implemente exactamente coincida con la que declaró. Por ejemplo, usted puede tener clasificadores cv desajustados:

    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 deficiencias incluyen

    • Función/variable declarada en un espacio de nombres, definido en otro.
    • Función/variable declarada miembro de clase, definida como global (o viceversa).
    • Tipo de retorno de funciones, número de parámetro y tipos, y convención de llamadas no todos están de acuerdo exactamente.

    El mensaje de error del compilador le dará a menudo la declaración completa de la variable o función que fue declarada pero nunca definida. Compare estrechamente con la definición que proporcionó. Asegúrese de que todos los detalles coincidan.

    Respondida el Sep 24, 2012 a las 22:58 - por debugdynamo46a0

    Votos positivos: 0 | Votos negativos: 0

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

    El orden en que las bibliotecas están vinculadas DOES importa si las bibliotecas dependen una de la otra. En general, si la biblioteca A depende de la biblioteca BEntonces libA DEBE antes libB en las banderas de enlace.

    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
    

    Compile:

    $ 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 repetir otra vez, la orden DOS ¡ importa!

    Respondida el Sep 24, 2012 a las 23:06 - por algorithmwhiz

    Votos positivos: 0 | Votos negativos: 0

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

    La función (o variable) void foo() fue definido en un programa C y usted intenta utilizarlo en un programa C++:

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

    El enlace C++ espera que se manglen los nombres, por lo que tiene que declarar la función como:

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

    Equivalentemente, en lugar de ser definido en un programa C, la función (o variable) void foo() se definió en C++ pero con enlace C:

    extern "C" void foo();
    

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

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

    extern "C" {
        #include "cheader.h"
    }
    

    Respondida el Sep 24, 2012 a las 23:14 - por codejuggernaut

    Votos positivos: 0 | Votos negativos: 0

  • lo que es un "símbolo externo no definido y no resuelto"

    Intentaré explicar lo que es un "símbolo externo no definido o no resuelto".

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

    Por ejemplo, tenemos un 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 de objetos

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

    Después de la fase del ensamblador tenemos un archivo de objeto, que contiene cualquier símbolo a exportar. Mira los 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

    Así que, 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

    Enlace de nuestros archivos de objetos

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

    y ejecutarlo

    $ ./prog
    123
    

    Linker ve símbolos exportados y lo vincula. Ahora intentamos descomponer 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 sólo construimos el archivo de objeto, el enlace no se hace todavía. Trate de 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 nombre local_var_ 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
    

    Así que, hemos visto que no hay etiquetas para el nombre local_var_, por eso Linker no lo ha encontrado. Pero somos hackers :) y podemos arreglarlo. Abra src1.s en su editor de texto y cambie

    .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
    

    i.e. 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 hemos fijado su valor a 456789. Trate de construir un archivo objeto de él

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

    ok, ver salida de readelf (símbolos)

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

    local_var_name tiene Bind GLOBAL (fue LOCAL)

    enlace

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

    y ejecutarlo

    $ ./prog 
    123456789
    

    Vale, lo hackearemos :)

    Por lo tanto, como resultado - un "no definido de referencia / error de símbolo externo no resuelto" ocurre cuando el linker no puede encontrar símbolos globales en los archivos del objeto.

    Respondida el Sep 24, 2012 a las 23:21 - por codergeek24

    Votos positivos: 0 | Votos negativos: 0

  • Si todo lo demás falla, recompilar.

    Recientemente pude deshacerme de un error externo no resuelto en Visual Studio 2012 simplemente recompilando el archivo ofensivo. Cuando reconstruí, el error se fue.

    Esto suele ocurrir cuando dos (o más) bibliotecas tienen una dependencia cíclica. Biblioteca Un intento de utilizar símbolos en B.lib y la biblioteca B intenta utilizar símbolos de A.lib. Tampoco existen para empezar. Cuando intentas compilar A, el paso del enlace fallará porque no puede encontrar B.lib. A.lib será generado, pero no dll. Luego compila B, que tendrá éxito y generará B.lib. Re-compilación Un testamento ahora funciona porque B.lib ahora se encuentra.

    Respondida el Sep 24, 2012 a las 23:27 - por devgeek

    Votos positivos: 0 | Votos negativos: 0

  • Las implementaciones de plantilla no son visibles.

    Las plantillas no especializadas deben tener sus definiciones visibles para todas las unidades de traducción que las usen. Eso significa que no puedes separar la definición de una plantilla a un archivo de implementación. Si usted debe separar la implementación, el trabajo habitual es tener un impl archivo que incluye al final del encabezado que declara la plantilla. Una situación común es:

    template
    struct X
    {
        void foo();
    };
    
    int main()
    {
        X x;
        x.foo();
    }
    
    //differentImplementationFile.cpp
    template
    void X::foo()
    {
    }
    

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

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

    Para mayor explicación y otra solución posible (explicación instantánea) ver esta pregunta y respuesta.

    Respondida el Sep 24, 2012 a las 23:37 - por codesculptor

    Votos positivos: 0 | Votos negativos: 0

  • Un error de referencia/símbolo externo no resuelto ocurre cuando un programa intenta utilizar una función, variable u objeto externo que ha sido declarado pero no ha sido definido o implementado en ningún lugar del código. Esto puede ocurrir, por ejemplo, cuando se usa una función de una librería externa pero no se ha vinculado correctamente al compilar el programa.

    Causas comunes de este tipo de errores pueden incluir:

    • Falta de implementación de una función o variable declarada en un archivo de cabecera.
    • No haber incluido correctamente los archivos de código fuente que contienen la implementación de las funciones o variables requeridas.
    • Problemas de configuración en el proceso de compilación que impiden la correcta vinculación de los archivos objeto.

    Para corregir y prevenir estos errores, puedes seguir estos pasos:

    • Asegúrate de que todas las funciones y variables declaradas en los archivos de cabecera estén implementadas en algún lugar del código fuente.
    • Verifica que estás incluyendo correctamente los archivos de código fuente que contienen la implementación de las funciones o variables requeridas.
    1. Re

    Respondida el Sep 25, 2012 a las 22:32 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • Errores de Referencia/Símbolo Externo No Resuelto

    Los errores de referencia o símbolo externo no resuelto ocurren cuando el compilador o vinculador no puede encontrar una función o variable definida en otro módulo o biblioteca.

    Causas Comunes:

    Fallos ortográficos o mayúsculas/minúsculas incorrectas: Asegúrate de que los nombres de las funciones y variables coincidan exactamente con sus definiciones. Funciones o variables no declaradas: Verifica que las funciones y variables estén declaradas en el ámbito correcto y que sean visibles para el módulo que las está llamando. Inclusión de archivos de cabecera faltantes: Los archivos de cabecera contienen declaraciones de funciones y variables, por lo que es crucial incluir los archivos de cabecera necesarios. Bibliotecas faltantes: Si una función o variable se define en una biblioteca, asegúrate de vincularla con el código compila. Incompatibilidad de versiones: Las diferentes versiones de las bibliotecas pueden contener cambios en las firmas de las funciones o los nombres de las variables, lo que lleva a errores de resolución.

    Corrección y Prevención:

    Verifica la ortografía y las mayúsculas/minúsculas: Revisa cuidadosamente los nombres de las funciones y variables. Declara funciones y variables: Asegúrate de que las funciones y variables se declaran en el alcance correcto. Incluye archivos de cabecera: Incluye todos los archivos de cabecera necesarios para acceder a las definiciones de funciones y variables. Vincula con bibliotecas: Vincula tu código con las bibliotecas que contienen las definiciones de las funciones y variables que utilizas. Comprueba las versiones de la biblioteca: Asegúrate de que estás utilizando versiones compatibles de bibliotecas. Utiliza herramientas de depuración: Utiliza herramientas de depuración como depuradores o analizadores estáticos para identificar y resolver errores de resolución. Documentación de revisión: Consulta la documentación de la biblioteca para obtener información sobre la sintaxis y las firmas de las funciones.

    Respondida el Sep 25, 2012 a las 22:47 - por Gemini

    Votos positivos: 0 | Votos negativos: 0