Что такое неопределенная ссылка / неразрешенная внешняя ошибка символа и как ее исправить?

Что такое неопределенные ссылки / неразрешенные ошибки внешних символов? Каковы общие причины и как их исправить / предотвратить?

Не стесняйтесь редактировать / добавлять свои собственные.

1286

Решение

Компиляция программы на C ++ происходит в несколько этапов, как указано в 2,2 (кредиты Кита Томпсон для справки):

Приоритет среди синтаксических правил перевода определяется следующими этапами [см. сноску].

  1. Физические символы исходного файла отображаются, в зависимости от реализации, в основной исходный набор символов
    (введение символов новой строки для индикаторов конца строки), если
    необходимо. [СНиП]
  2. Каждый экземпляр символа обратной косой черты (\), за которым сразу следует символ новой строки, удаляется, объединяя строки физического источника в
    сформировать логические строки источника. [СНиП]
  3. Исходный файл разлагается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии). [СНиП]
  4. Выполняются директивы предварительной обработки, расширяются вызовы макросов и выполняются выражения унарного оператора _Pragma. [СНиП]
  5. Каждый элемент исходного набора символов в символьном литерале или строковом литерале, а также каждая escape-последовательность и универсальное имя-символа
    в символьном литерале или не необработанном строковом литерале, преобразуется в
    соответствующий член набора символов выполнения; [СНиП]
  6. Литеральные токены смежных строк объединяются.
  7. Пробелы, разделяющие токены, больше не имеют значения. Каждый токен предварительной обработки преобразуется в токен. (2.7).
    полученные токены синтаксически и семантически анализируются и
    переводится как единица перевода. [СНиП]
  8. Переведенные единицы перевода и единицы реализации объединяются следующим образом: [СНиП]
  9. Все ссылки на внешние объекты разрешены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на объекты, не определенные в
    текущий перевод. Все такие выходные данные переводчика собираются в
    образ программы, который содержит информацию, необходимую для выполнения в своем
    среда исполнения.
    (акцент мой)

[Примечание] Реализации должны вести себя так, как если бы эти отдельные фазы происходили, хотя на практике разные фазы могут складываться вместе.

Указанные ошибки возникают на последнем этапе компиляции, который чаще всего называют связыванием. По сути, это означает, что вы скомпилировали кучу файлов реализации в объектные файлы или библиотеки и теперь хотите, чтобы они работали вместе.

Скажем, вы определили символ a в a.cpp, Сейчас, b.cpp объявленный этот символ и использовал его. Перед установкой ссылки просто предполагается, что этот символ был определен где-то, но это все равно где. Фаза связывания отвечает за поиск символа и правильное связывание его с b.cpp (ну, собственно, к объекту или библиотеке, которые его используют).

Если вы используете Microsoft Visual Studio, вы увидите, что проекты генерируют .lib файлы. Они содержат таблицу экспортируемых символов и таблицу импортированных символов. Импортированные символы сопоставляются с библиотеками, с которыми вы ссылаетесь, а экспортируемые символы предоставляются для библиотек, которые используют .lib (если есть).

Подобные механизмы существуют для других компиляторов / платформ.

Распространенные сообщения об ошибках error LNK2001, error LNK1120, error LNK2019 за Microsoft Visual Studio а также undefined reference to SymbolName за НКУ.

Код:

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

будет генерировать следующие ошибки с НКУ:

/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

и подобные ошибки с 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

Общие причины включают в себя:

+748

Другие решения

Члены класса:

Чистый virtual деструктор нуждается в реализации.

Чтобы объявить деструктор чистым, вы все равно должны его определить (в отличие от обычной функции):

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

Это происходит потому, что деструкторы базового класса вызываются, когда объект уничтожается неявно, поэтому требуется определение.

virtual методы должны быть либо реализованы, либо определены как чистые.

Это похоже наvirtual методы без определения, с дополнительным обоснованием того, что
Чистое объявление генерирует фиктивную таблицу, и вы можете получить ошибку компоновщика, не используя функцию:

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

Чтобы это работало, объявляем X::foo() как чистый:

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

не-virtual ученики

Некоторые члены должны быть определены, даже если они не используются явно:

struct A
{
~A();
};

Следующее приведет к ошибке:

A a;      //destructor undefined

Реализация может быть встроенной в самом определении класса:

struct A
{
~A() {}
};

или снаружи:

A::~A() {}

Если реализация находится за пределами определения класса, но в заголовке, методы должны быть помечены как inline предотвратить множественное определение.

Все используемые методы-члены должны быть определены, если они используются.

Распространенная ошибка — забыть назвать название:

struct A
{
void foo();
};

void foo() {}

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

Определение должно быть

void A::foo() {}

static члены данных должны быть определены вне класса в единая единица перевода:

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

Инициализатор может быть предоставлен для static const член данных целочисленного или перечислимого типа в определении класса; однако для использования odr этого члена все равно потребуется определение области имен, как описано выше. C ++ 11 позволяет инициализацию внутри класса для всех static const члены данных.

159

Ошибка связывания с соответствующими библиотеками / объектными файлами или компиляцией файлов реализации

Обычно каждая единица перевода генерирует объектный файл, который содержит определения символов, определенных в этой единице перевода.
Чтобы использовать эти символы, вы должны ссылаться на эти объектные файлы.

Под НКУ Вы должны указать все объектные файлы, которые должны быть связаны вместе в командной строке, или скомпилировать файлы реализации вместе.

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

libraryName Вот только голое название библиотеки, без специфических для платформы дополнений. Так, например в Linux файлы библиотеки обычно называются libfoo.so но ты бы только написал -lfoo, В Windows этот же файл может называться foo.lib, но вы бы использовали тот же аргумент. Возможно, вам придется добавить каталог, где эти файлы могут быть найдены с помощью -L‹directory›, Убедитесь, что не написали пробел после -l или же -L,

За XCode: Добавить пути поиска в заголовке пользователя -> добавить путь поиска в библиотеке -> перетащить фактическую ссылку на библиотеку в папку проекта.

Под МСВС, файлы, добавленные в проект, автоматически связывают свои объектные файлы и lib файл будет создан (в общем использовании). Чтобы использовать символы в отдельном проекте, вы бы
необходимо включить lib файлы в настройках проекта. Это делается в разделе Linker свойств проекта, в Input -> Additional Dependencies, (путь к lib файл должен быть
добавлено в Linker -> General -> Additional Library Directories) При использовании сторонней библиотеки, которая поставляется с lib файл, неспособность сделать это обычно приводит к ошибке.

Также может случиться, что вы забудете добавить файл в компиляцию, и в этом случае объектный файл не будет сгенерирован. В НКУ вы бы добавили файлы в командную строку. В МСВС добавление файла в проект автоматически скомпилирует его (хотя файлы можно вручную исключить из сборки по отдельности).

В программировании Windows контрольным признаком того, что вы не связали необходимую библиотеку, является то, что имя неразрешенного символа начинается с __imp_, Посмотрите название функции в документации, и там должно быть указано, какую библиотеку вам нужно использовать. Например, MSDN помещает информацию в поле внизу каждой функции в разделе «Библиотека».

100

Объявлен, но не определил переменную или функцию.

Типичное объявление переменной

extern int x;

Поскольку это только декларация, единое определение нужно. Соответствующее определение будет:

int x;

Например, следующее может привести к ошибке:

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

Аналогичные замечания относятся к функциям. Объявление функции без ее определения приводит к ошибке:

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

Будьте осторожны, чтобы реализуемая вами функция точно соответствовала той, которую вы объявили. Например, вы могли не соответствовать cv-квалификаторам:

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)

Другие примеры несоответствий включают

  • Функция / переменная объявлена ​​в одном пространстве имен, определена в другом.
  • Функция / переменная объявлена ​​как член класса, определена как глобальная (или наоборот).
  • Тип возвращаемого значения функции, номер параметра и типы, а также соглашение о вызовах не полностью совпадают.

Сообщение об ошибке от компилятора часто дает вам полное объявление переменной или функции, которая была объявлена, но никогда не определялась. Сравните это с приведенным вами определением. Убедитесь, что каждая деталь соответствует.

93

Порядок, в котором указываются взаимозависимые связанные библиотеки, неверен.

Порядок, в котором связаны библиотеки, имеет значение, если библиотеки зависят друг от друга. В общем, если библиотека A зависит от библиотеки B, затем libA ДОЛЖЕН появиться раньше libB в компоновщике флагов.

Например:

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

Создайте библиотеки:

$ 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

Обобщение:

$ 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

Так что повторить еще раз, порядок ДЕЛАЕТ иметь значение!

76

что такое «неопределенная ссылка / неразрешенный внешний символ»

Я попытаюсь объяснить, что такое «неопределенная ссылка / неразрешенный внешний символ».

примечание: я использую g ++ и Linux, и все примеры для него

Например, у нас есть код

// src1.cpp
void print();

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

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

а также

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

Сделать объектные файлы

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

После фазы ассемблера у нас есть объектный файл, который содержит любые символы для экспорта.
Посмотрите на символы

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

Я отклонил некоторые строки из вывода, потому что они не имеют значения

Итак, мы видим следующие символы для экспорта.

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

src2.cpp ничего не экспортирует, и мы не видели его символов

Связать наши объектные файлы

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

и запустить его

$ ./prog
123

Линкер видит экспортированные символы и связывает их. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь

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

и восстановить объектный файл

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

ОК (без ошибок), потому что мы только строим объектный файл, связывание еще не завершено.
Попробуй ссылку

$ 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

Это произошло потому, что наше local_var_name является статическим, то есть оно невидимо для других модулей.
Теперь глубже. Получите вывод фазы перевода

$ 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

Итак, мы видели, что нет метки для local_var_name, поэтому компоновщик не нашел ее. Но мы хакеры 🙂 и мы можем это исправить. Откройте src1.s в вашем текстовом редакторе и измените

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

в

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

то есть вы должны иметь как ниже

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

мы изменили видимость local_var_name и установили его значение на 456789.
Попробуйте построить из него объектный файл

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

хорошо, смотрите вывод readelf (символы)

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

теперь у local_var_name есть Bind GLOBAL (был LOCAL)

ссылка на сайт

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

и запустить его

$ ./prog
123456789

хорошо, мы взломали это 🙂

Таким образом, в результате — «неопределенная ссылка / неразрешенная ошибка внешнего символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.

66

Символы были определены в программе на C и использованы в коде C ++.

Функция (или переменная) void foo() был определен в программе на C, и вы пытаетесь использовать его в программе на C ++:

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

Компоновщик C ++ ожидает искажения имен, поэтому вы должны объявить функцию следующим образом:

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

Эквивалентно, вместо того, чтобы быть определенным в программе на C, функция (или переменная) void foo() был определен в C ++, но с привязкой C:

extern "C" void foo();

и вы пытаетесь использовать его в программе C ++ со связью C ++.

Если вся библиотека включена в заголовочный файл (и была скомпилирована как код C); включение должно быть следующим;

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

Если ничего не помогает, перекомпилируйте.

Недавно я смог избавиться от нерешенной внешней ошибки в Visual Studio 2012, просто перекомпилировав файл-нарушитель. Когда я перестроил, ошибка ушла.

Это обычно происходит, когда две (или более) библиотеки имеют циклическую зависимость. Библиотека A пытается использовать символы из B.lib, а библиотека B пытается использовать символы из A.lib. Ни один не существует, чтобы начать с. Когда вы попытаетесь скомпилировать A, шаг ссылки потерпит неудачу, потому что он не может найти B.lib. A.lib будет сгенерирован, но не dll. Затем вы компилируете B, который преуспеет и сгенерирует B.lib. Перекомпиляция A теперь будет работать, потому что B.lib теперь найден.

60
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector