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

10 minutos de lectura

1647680228 761 ¿Que es un error de referencia no definidasimbolo externo no
Luciano Grigore

¿Qué son los errores de referencia no definida/símbolo externo sin resolver? ¿Cuáles son las causas comunes y cómo solucionarlas/prevenirlas?

  • @LuchianGrigore ‘siéntase libre de agregar una respuesta’ Preferí agregar el enlace relevante (en mi humilde opinión) su respuesta principal, si desea permitirlo.

    – πάντα ῥεῖ

    3 de marzo de 2014 a las 22:36

  • @jave.web: Si bien eso sucede, el programador suele notar que no tiene this puntero y sin 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 calificado.

    – Ben Voigt

    27 de abril de 2015 a las 22:06

  • @jave.web: Este fue exactamente mi problema. ¡Gracias! Soy nuevo en cpp, pero por lo que puedo decir, estaba teniendo exactamente el problema que, según Ben Voigt, era bastante raro. Creo que su solución sería una gran respuesta.

    – RoG

    20 de octubre de 2016 a las 6:42


  • Pueden ser útiles, al igual que muchas respuestas a preguntas que se marcan como demasiado generales.

    –Albert van der Horst

    16 de agosto de 2019 a las 19:07

  • Me gustaría ver un ejemplo reproducible mínimo como algo que le pedimos a la mayoría de los nuevos usuarios, sinceramente. No quiero decir nada con eso, es solo que no podemos esperar que las personas sigan las reglas que no nos imponemos a nosotros mismos.

    – Danilo

    8 sep 2019 a las 18:54


1647680228 761 ¿Que es un error de referencia no definidasimbolo externo no
Luciano Grigore

Miembros de la clase:

Un puro virtual destructor necesita una implementación.

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

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

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.

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

Esto es similar a no-virtual métodos sin definición, con el razonamiento adicional de que la declaración pura genera una vtable ficticia y es posible que obtenga 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, declara X::foo() tan puro:

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

No-virtual miembros de la clase

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

struct A
{ 
    ~A();
};

Lo siguiente daría el error:

A a;      //destructor undefined

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

struct A
{ 
    ~A() {}
};

o afuera:

A::~A() {}

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

Todos los métodos de 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 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

Se puede proporcionar un inicializador 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 describe anteriormente. C++ 11 permite la inicialización dentro de la clase para todos static const miembros de datos.

  • Solo pensé que tal vez quiera enfatizar que hacer ambas cosas es posible, y el dtor no es en realidad una excepción. (no es obvio a partir de su redacción a primera vista).

    – Deduplicador

    20 sep 2014 a las 19:26

  • Sería bueno si pudiera cubrir explícitamente el error común de gcc main.c en lugar de gcc main.c other.c (lo que los principiantes suelen hacer antes de que sus proyectos se vuelvan tan grandes como para crear archivos .o).

    –MM

    4 de noviembre de 2019 a las 20:43


1647680228 761 ¿Que es un error de referencia no definidasimbolo externo no
Luciano Grigore

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

Una declaración de variable típica es

extern int x;

Como esto es sólo una declaración, una definición única se necesita 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

Comentarios 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 implemente coincida exactamente con la que declaró. Por ejemplo, es posible que tenga calificadores de cv que no coincidan:

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 devolución de la función, el número y los tipos de parámetros y la convención de llamadas no coinciden exactamente.

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áralo de cerca con la definición que proporcionaste. Asegúrate de que todos los detalles coincidan.

  • En VS, los archivos cpp coinciden con los del encabezado #includes no agregado al directorio de origen también se incluye en la categoría de definiciones faltantes.

    –Laurie Stearn

    30 de marzo de 2018 a las 12:18

1647680229 594 ¿Que es un error de referencia no definidasimbolo externo no
Comunidad

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

El orden en que se vinculan las bibliotecas SÍ importa si las bibliotecas dependen unas de otras. En general, si la biblioteca A depende de la biblioteca Bluego libA DEBER aparecer antes 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 LO HACE ¡importar!

  • En VS, los archivos cpp coinciden con los del encabezado #includes no agregado al directorio de origen también se incluye en la categoría de definiciones faltantes.

    –Laurie Stearn

    30 de marzo de 2018 a las 12:18

¿Qué es una “referencia indefinida/símbolo externo no resuelto”?

Intentaré explicar qué es una “referencia indefinida/símbolo externo no resuelto”.

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

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 de ensamblador, tenemos un archivo de objeto que contiene los símbolos para 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 la salida porque no importan

Entonces, vemos los siguientes 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

Vincular nuestros archivos de objetos

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

y ejecutarlo

$ ./prog
123

Linker 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 de objeto

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

OK (sin errores), porque solo creamos un archivo de objeto, la vinculación aún no se ha realizado. 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 el resultado 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

Entonces, hemos visto que no hay una etiqueta para local_var_name, es por eso que el enlazador 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

para

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

es decir, deberías tener como a continuación

    .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. Intente crear un archivo de objeto a partir de él.

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

ok, vea la salida readelf (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, ocurre una “referencia indefinida/error de símbolo externo no resuelto” cuando el enlazador no puede encontrar símbolos globales en los archivos de objetos.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad