Вопрос по c++, c++-faq, linker-errors, undefined-reference, unresolved-external – What is an undefined reference/unresolved external symbol error and how do I fix it

1320

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

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

One вещь, чтобы рассмотреть вопрос о включении в том, как иметь дело с «неопределенными виртуальными таблицами» и «неопределенных TypeInfo» ошибок, в частности, (так как они менее очевидны, чем неопределенные функции или переменные). Jeremiah Willcock
Я маркировкаэтот вопро чтобы быть возможным обманом этого. Но, пройдя все ваши (блестящие) ответы, я не вижу здесь описанного случая. Я понимаю, что это конкретно о том, как язь устанавливает типа проекта и ее зависимости сцепления. Но это, например, часто задает вопрос, я думаю, что это стоило бы покрыто (возможно только со ссылкой на другой подходящий боян) здесь. Если это уже, и я просто не заметил его, забыть об этой заявке / за комментарий. πάντα ῥεῖ
@ LuchianGrigore Стесняйтесь добавить ответ " Я предпочел, чтобы добавить соответствующую ссылку (ИМХО) свой первичный ответ, если вы хотите, чтобы позволить. πάντα ῥεῖ
Довольно частая ошибка в том, что вы определяете функцию как отдельную и забываете селектор класса (например,A::) в вашем .Cpp файл:You сделать это (неправильно): void myFunc() { /* do stuff */ } Вместо этого (справа): void A::myFunc() { /* do stuff */ } jave.web
If это происходит с вами, с сигналом Qt, вы, скорее всего, забыл макрос Q_OBJECT. ManuelSchneid3r

Ваш Ответ

30   ответов
762

как это указано на 2,2 (благодарность Кита Томпсону за справку):

The приоритет среди синтаксических правил перевода определяется следующими фазами [См сноска].

Физические символы исходного файла отображаются, в зависимости от реализации, в базовый исходный набор символов (ввод символов новой строки для индикаторов конца строки), если это необходимо. [СНиП]Each экземпляр символа обратной косой черты (\), непосредственно за которым следует символ новой строки удаляется, сращивания линий физических источников для формирования логических строк исходного текста. [СНиП] Исходный файл разбит на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии). [СНиП]ыполняются директивы @Preprocessing, расширяются вызовы макросов и выполняются выражения унарного оператора _Pragma. [СНиП]Each набор элементов источником символов в символьный литерал или строковый литерал, а также друг с управляющая последовательность и универсальный-символьного имени в литерой или несырьевого строковый литерал, превращают до соответствующего элемента набора исполнения символов ; [СНиП] Смежные строковые литеральные токены объединяются.е @ Пробельные символы, разделяющие лексемы, перестали быть значимыми. Каждый предварительной обработки маркер превращается в знак. (2.7). Полученные маркеры синтаксически и семантически проанализированы и переведены в качестве единицы перевода. [СНиП]Translated единицы перевода и конкретизация агрегаты объединены следующим образом: [СНиП]All внешние ссылки на объекты будут решены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на объекты, не определенные в текущем переводе. Весь такой вывод транслятора собирается в образ программы, который содержит информацию, необходимую для выполнения в среде выполнения. (Курсив мой)

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

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

Say вы определили символa вa.cpp. В настоящее время,b.cpp Объявлен Этот символ и использовать его. Перед установкой ссылки просто предполагается, что этот символ был определенгде-т, Но это еще не все равно, где. Фаза связывания отвечает за поиск символа и правильное связывание его сb.cpp (Ну, на самом деле к объекту или библиотеки, которая использует его).

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

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

Common сообщения об ошибках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;
}

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

/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

and подобные ошибки сMicrosoft Visual Studio:

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

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

Failure в связи с соответствующими библиотеками / объектных файлов или компиляции файлов реализацииDeclared и неопределенной переменной или функции. Общие проблемы с членами типа классаTemplate реализации не видно.Symbols были определены в программе C и используется в коде C ++.Incorrectly импорт / метода экспорта / классов для различных модулей / DLL. (МСВС специфические)Circular библиотека зависимостьundefined ссылки на `WinMain @ 16Interdependent библиотека заказ Несколько исходных файлов с тем же именем Неверное или не включенное расширение .lib при использовании#pragma (Microsoft Visual Studio)Problems с шаблоном друзьямиInconsistentUNICODE Определения
@ MirroredFate есть ли способ, чтобы получить лучшие ошибки компоновщика VS? Например, чтобы это выглядело как gcc. TankorSmash
олько @@ TankorSmash Если. Я думаю, что вы можете изменить вывод с помощьюэт, Но я не пробовал. MirroredFate
Personally, я думаю, что сообщения об ошибках линкер МС так же, как для чтения, как ошибки ССЗ. Кроме того, они имеют то преимущество, в том числе и оба так исказил и unmangled имен неразрешенного внешний. Имея искаженное имя может быть полезно, когда вы должны смотреть на библиотеки или объектных файлов непосредственно увидеть, что может быть проблема (например, вызывающий несоответствие конвенции). Кроме того, я не уверен, что версия MSVC производится ошибка здесь, но более новые версии включают в себя имя (как терзал и unmangled) функций со ссылкой на неразрешенный внешний символ. Michael Burr
David Drysdale написал отличную статью о том, как работают линкеры: Руководство для начинающего по линкерам. Учитывая тему данного вопроса, то я подумал, что это может оказаться полезным. Pressacco
А как определить где мой случай ?! Stackoverflow.com / вопросы / 32915615 / ... Aleksey Kontsevich
164
Class Участники:A чистыйvirtual Деструктор нуждается в реализации.

чтобы определить его (в отличие от обычной функции):

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

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

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

This похож на не-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
}

For, чтобы это работало, объявитьX::foo() Чист:

struct X
{
    virtual void foo() = 0;
};
Номер дляvirtual Ученики

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

struct A
{ 
    ~A();
};

The следующие даст ошибку:

A a;      //destructor undefined

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

struct A
{ 
    ~A() {}
};

or снаружи:

A::~A() {}

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

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

A распространенная ошибка является забыв уточнить имя:
struct A
{
   void foo();
};

void foo() {}

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

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

void A::foo() {}
static элементы данных должны быть определены вне класса вsingle ЕП:
struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

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

The последней строке этот ответ неправильный, в-классе декларация никогда не является определением. (Определения не нужны для статических членов, которые не используются в odr, что является общим для интегральных констант времени компиляции) Ben Voigt
Not, что эта часть - C ++ 11 позволяет инициализацию внутри класса для всехstatic const Элементы данных правильно. [Class.static.data] / 3 говорит, что вы должны пометить член статических данныхconstexpr, Если они не являются интегральной или перечисления типа. Praetorian
No необходимо определить какой-либо не виртуальную функцию, которую вы никогда не использовать. Кроме того, нет необходимости, чтобы определить какие-либо виртуальную функцию, если вы никогда не создает объект класса, и вызывать его из производного класса, который вы на самом деле создать экземпляр. Кроме того, можно определить все чистые виртуальные функции. Deduplicator
@ Deduplicator «потребность» против «должны». Педантично, не чисто виртуальные функции должны быть определены (хотя, как уже упоминалось, некоторые компиляторы не будут жаловаться, пока вы их называете, но некоторые будет). Я не думаю, что я сказал, что вы не можете определить чистые виртуал. Luchian Grigore
@ Deduplicator в разделе «Виртуальная функция, объявленная в классе должно быть определено, или объявлены чистый (10,4) в этом классе, или как, но не диагностики не требуется» (10.3 Виртуальные функции) - если только это не было изменено в C ++ 14 Luchian Grigore
102
Failure в связи с соответствующими библиотеками / объектных файлов или компиляции файлов реализации

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

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

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

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

Для XCode: Добавьте пользователя Header Пути поиска -> добавить Библиотека Путь поиска -> перетаскивания фактическую ссылку на библиотеку в папке проекта

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

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

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

94
Declared но не определяет переменную или функцию.

A декларация типичная переменная

extern int x;

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

int x;

For Например, следующий код будет генерировать сообщение об ошибке:

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

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

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

Be осторожным, чтобы функция, которую вы реализовать в точности совпадает с одним вы объявлен. Например, у вас может быть несоответствие 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)

Other примеры несоответствия включают

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

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

@ Raymond я ушел из названия функции допускают ошибки, поскольку это достаточно очевидно. Что касается имен параметров - то, что Luchian Grigore
People спрашивает, оunresolved внешних связей с ошибками имен, Так что это не совсем очевидно. (Не уверен, что вы имеете в виду об именах параметров. Имена параметров не являются частью типа.) Raymond Chen
@ RaymondChen уже охвачены Stackoverflow.com / а / 12574420/673730 Luchian Grigore
In VS, CPP файлы, соответствующие тем, в заголовке#includes не Добавил В исходную папку, также подпадает под категорию отсутствующих определений. Laurie Stearn
77
The порядок, в котором задаются взаимозависимые связаны библиотеки является неправильным.

в котором связаны библиотеки, имеет значение, зависят ли библиотеки друг от друга. В общем, если библиотека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;
};

Create библиотеки:

$ 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

So повторить еще раз, заказ ЛИ дело

I Любопытно, что в моем случае я был один объектный файл, который зависит от общей библиотеки. Я должен был изменить файл сборки и поместить в библиотекуПОСЛ Объект с GCC 4.8.4 на Debian. На Centos 6.5 с ССЗ 4.4 Makefile работал без проблем. Marco Sulla
-Wl, - пуско-группа .....- Ш., - в конце группа решает эту проблему user2672165
68

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

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

note: я использовать г ++ и Linux, и все примеры из-за этого

For пример у нас есть какой-то код

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

Make объектные файлы

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

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

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

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

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

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

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

Link наши объектные файлы

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

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

$ ./prog
123

Linker видит экспортируемые символы и связывает его. Сейчас мы пытаемся раскомментируйте строки в 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);
}

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

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

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

$ 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

It произошел потому, что наша 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:
; ...

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

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

ok см readelf вывода (символы)

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

Now local_var_name имеет Bind GLOBAL (был LOCAL)

ссылк

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

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

$ ./prog 
123456789

ok, мы взломать его:)

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

64
Symbols были определены в программе C и используется в коде C ++.

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

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

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

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

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

extern "C" void foo();

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

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

extern "C" {
    #include "cheader.h"
}
Or наоборот, если вы создали библиотеку C, правило хорошо, чтобы защитить файл заголовок (ы) с помощью окружающей всех общих заявлений с#ifdef __cplusplus [\n] extern"C" { [\n] #endif а также#ifdef __cplusplus [\n] } [\n] #endif ([\n] Быть возвращение в реальном перевозке, но я не могу написать это правильно в комментарии). Bentoy13
As в приведенном выше комментарии, секция «создание смешанных языков заголовки типа» здесь помогло: Oracle.com / technetwork / статьи / серверы-хранилища-DEV / ... zanbri
61

If все остальное терпит неудачу, перекомпиляция.

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

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

Correct - это происходит, когда библиотеки имеют циклическую зависимость Luchian Grigore
I расширен от вашего ответа и связан в основном один. Благодарность Luchian Grigore
52
Incorrectly импорт / метода экспорта / классов через модули / DLL (конкретные компилятор).

__declspec(dllexport) а также__declspec(dllimport).

This двойной функциональностью, как правило, получают за счет использования макроса:

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

The макроTHIS_MODULE Будет определяться только в модуле, что экспорт функции. Таким образом, декларация:

DLLIMPEXP void foo();

expands на

__declspec(dllexport) void foo();

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

__declspec(dllimport) void foo();

and сообщает компилятор, что определение находится в одной из библиотек, связанных с (также см 1)).

You может точно также импорт / экспорт классы:

class DLLIMPEXP X
{
};
To быть завершена, то ответ следует отметить GCC-хvisibility И для Windows.def Файлы, так как они также влияют на имя символа и присутствие. rubenvb
@ rubenvb я не использовал.def Файлы в возрасте. Вы можете добавить ответ или отредактировать этот. Luchian Grigore
50
Template реализации не видно.

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

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

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

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

To это исправить, необходимо вынести определение оX::foo В файл заголовка или какое-то место видимого к единице перевода, который его использует.

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

For подробные объяснения и другого возможного решения (явное экземпляра) смthis вопрос и ответ на.

Further информация для «шаблоны должны быть определены в заголовке» можно найти в Stackoverflow.com / вопросы / 495021 PlasmaHH
50

которые каждый VC ++ программисты видели, снова и снова. Давайте делать вещи ясность первой.

A. Что такое символ? Короче говоря, символ есть имя. Это может быть имя переменной, имя функции, имя класса, имя ЬурейеЕ, или что-нибудь, кроме этих имен и знаков, которые принадлежат к языку C ++. Она определяется пользователем или введены с помощью библиотеки зависимостей (еще один определенный пользователем).

B. Что из себя? В VC ++, каждый исходный файл (.cpp, .c и др.), Рассматриваются как единое целое перевода, компилятор компилирует одну единицы за один раз, и генерировать один объектный файл (.obj) для текущей единицы трансляции. (Обратите внимание, что каждый файл заголовок, что этот источник файл будет предварительно обработан и будет рассмотрен в рамках этой единицы трансляции) все в пределах одной единицы перевода рассматриваются как внутренние, а все остальное рассматриваются как внешнее. В C ++, вы можете вставить ссылку на внешний символ, используя ключевые слова, какextern, __declspec (dllimport) и так далее

C. Что такое «решимость»? Разрешить это термин связывания времени. В связывающее время, попытки компоновщика, чтобы найти внешнее определение для каждого символа в объектных файлах, которые не могут найти свое определение внутри. Масштабы этого процесса поиска в том числе:

All объектных файлов, которые генерируются при составлении времениAll библиотеки (.lib), которые прямо или косвенно указано в качестве дополнительных зависимостей этого строительного применения.

This процесс поиска называется решимость.

D. И, наконец, почему неразрешенный внешний символ? Если компоновщик не может найти внешнее определение для символа, который не имеет внутреннего определения, он сообщает об ошибке «Неразрешенный внешний символ».

E. Возможные причины LNK2019: Неразрешенный внешний символ ошибки. Мы уже знаем, что эта ошибка из-за компоновщик не удалось найти определение внешних символов, то возможные причины могут быть отсортированы как:

Definition существует

For Например, если у нас есть функция под названием Foo определено в a.cpp:

int foo()
{
    return 0;
}

In b.cpp мы хотим, чтобы вызвать функцию Foo, поэтому мы добавляем

void foo();

to объявляют функции Foo (), и называют его в другом теле функции, скажемbar():

void bar()
{
    foo();
}

Now при создании этого кода вы получите сообщение об ошибке LNK2019 жалуются, что Foo является нерешенной символ. В этом случае, мы знаем, что Foo () имеет свое определение в a.cpp, но отличается от того, который мы называем (разные возвращаемого значения). Это тот случай, когда определение существует.

Definition не существует

Если мы хотим вызвать некоторые функции в библиотеке, но библиотека импорта не добавляется в дополнительный список зависимостей (устанавливается из:Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) От настройки проекта. Теперь компоновщик сообщит LNK2019 так как определение не существует в текущем контексте поиска.

Countryman ваш ответ был хорош stackprogramer
37

неопределенная ссылка на[email protected] или похожие Необычным main() Ссылки точки входа (специально для Визуально-студия).

You, возможно, пропустили, чтобы выбрать правильный тип проекта с вашей фактической IDE. IDE может связать, например, Приложение Windows проекты до такой точки входа функции (как указано в недостающей ссылке выше), вместо того, чтобы часто используемыйint main(int argc, char** argv); Подпись.

If вашего IDE опораонсольные проекты @Plain Вы можете выбрать этот тип проекта, вместо того, чтобы в проекте приложения окна.

ЗдесьСлучай а также Вариант 2 Обработано более подробно с Aреальный ми проблема.

Не поможет, но может указать наэтот вопро А также тот факт, что это чаще всего вызваны не имея основную функцию на все, чем не иметьWinMain. Действительные C ++ программе нужнаmain. chris
34

что вы правильно 32/64 битные бинарников

33

Microsoft предложения A#pragma Ссылаться на правильную библиотеку во время связи;

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

In дополнение к библиотеке пути, включая каталог библиотеки, то это должно быть полное имя библиотеки.

31

Пакет Visual Studio NuGet необходимо обновить до новой версии набора инструментов

I только что эту проблему пытаются связать Libpng с Visual Studio 2013. Проблема в том, что файл пакета только имели библиотеки для Visual Studio 2010 и 2012

Правильное решение - надеяться, что разработчик выпустит обновленный пакет, а затем обновит его, но он сработал для меня, взломав дополнительную настройку для VS2013, указывая на файлы библиотеки VS2012.

I отредактировали пакет (вpackages Папку в директории продукта позволяет), находяpackagename\build\native\packagename.targets И внутри этого файла, копирование всехv110 разделы. Я изменилv110 вv120 вThe состояние поля только Быть очень осторожным, чтобы оставить пути имен файлов все какv110. Это просто позволил Visual Studio 2013, чтобы ссылка на библиотеки в 2012 году, и в этом случае, это сработало.

This кажется чрезмерно специфическим - возможно, новый поток был бы лучшим местом для этого ответа Luchian Grigore
@ LuchianGrigore: Я хочу, чтобы получить возможность отправлятьВо, Как этот вопрос был именно этот вопрос, но он был отмечен как дубликат этого вопроса, так что я не мог ответить на него есть. Таким образом, я отправил свой ответ здесь вместо. Malvineous
That вопрос уже имеет принятый ответ. Он отмечен как дубликат, так как общая причина указана выше. Что случилось бы, если бы у нас был ответ здесь для каждого proble с библиотекой, которая не входит? Luchian Grigore
@ LuchianGrigore: эта проблема не характерна для библиотеки, она затрагивает все библиотеки, использующие систему управления пакетами Visual Studio. Я только что произошло, чтобы найти другой вопрос, потому что мы оба были проблемы с Libpng. Я тоже была такая же проблема (с тем же раствором) для libxml2, libiconv и GLEW. Это речь идет о проблеме с системой управления пакетами Visual Studio, и мой ответ объясняет причину и предлагает решение. Кто-то видел «неразрешенный внешний» и предположил, что это стандартная проблема линкер, когда на самом деле это проблема управления пакетами. Malvineous
30

написанный на C ++, который имеет тысячи файлов .cpp и тысячи .h files.And давайте говорит, что проект также зависит от десяти библиотек статических. Давайте говорит, что мы на Windows, и мы строим наш проект в Visual Studio 20xx. При нажатии Ctrl + F7 Visual Studio, чтобы начать собирать весь раствор (предположим, что есть только один проект решения)

Что это значит компиляции?

оиск @Visual студии в файл .Vcxproj И начать собирать каждый файл, который имеет расширение .cpp. Порядок компиляции undefined.So вы не должны считать, что файл main.cpp собран первый Если файлы .cpp зависят от дополнительных файлов .h, чтобы найти символы, которые могут или не могут быть определены в файле .cppIf существует один CPP-файл, в котором компилятор не может найти один символ,compiler ошибка времени Поднимает сообщениеSymbol й не может быть найдены Для каждого файла с расширением .cpp генерируется объектный файл .o, а также Visual Studio записывает вывод в файл с именем ProjectName.Cpp.Clean.txt, Который содержит все объектные файлы, которые должны быть обработаны с помощью линкера.

The Второй этап компиляции осуществляется Linker.Linker должны объединить все файлы объектов и сборку, наконец, выход (который может быть исполняемый файл или библиотеку)

Steps В Привязка проекта

Parse все объектные файлы и найти определение, которое было только заявленный в заголовке (например: код одного из методов класса, как указано в предыдущих ответах, или событие инициализацию статической переменной, которая является членом внутри класса) Если один символ не может быть найден в объектных файлах, он также ищется в дополнительных библиотеках. Для добавления новой библиотеки в проектConfiguration свойства -> VC ++ Справочники ->Library Каталоги И здесь вы указали дополнительную папку для поиска библиотек иConfiguration свойства -> Linker -> Input Для указания имени библиотеки. -Если линкер не может найти символ, который вы пишете в одном .cpp он поднимаетремя @linker ошибка, Который может звучать какerror LNK2001: unresolved external symbol "void __cdecl foo(void)" ([email protected]@YAXXZ)

Наблюдение

Как только компоновщик найдет один символ, который он не ищет в других библиотеках,The порядок, связывающая библиотекиимеет значени. Если Linker находит внешний символ в одной статической библиотеке, он включает этот символ в выходные данные проекта. Однако, если библиотека является общей (динамической), он не включает код (символы) в выходные данные, но Run-Time Сбой может произойти

How чтобы решить такого рода ошибки

Compiler Time Error:

Make, что вы написать C ++ проект синтаксических правильно.

Linker Ошибка времени

Define всю свою символ, который Вы заявляете в файлах заголовка Используйте#pragma once За то, что компилятор не включать один заголовок, если он был уже включен в текущем .cpp которого составляютсяMake том, что внешняя библиотека не содержит символы, которые могут войти в конфликт с другими символами, заданными в файлах заголовки Когда вы будете использовать шаблон, чтобы убедиться, что вы включили определение каждой функции шаблона в файле заголовок для позволяя компилятор генерировать соответствующий код для какой-либо конкретизации.
Is не ваш ответ является специфическим для Visual Studio? Вопрос не определяет какие-либо инструментов IDE / компилятор, так что делает ваш ответ бесполезным для невизуальной-студии части. Victor Polevoy
Вы правы . Но каждый в каждом IDE процессе компиляции / связывания делается немного differently.But файлы обрабатываются точно так же (даже г ++ сделать то же самое, когда речь разобрать флаги ..) user4272649
The проблема на самом деле не о среде, а об ответе на связь проблемы. Проблемы со связыванием связаны не с IDE, а с компилятором и процессом сборки. Victor Polevoy
Yes.But процесс сборки / связь делается в г ++ / Visual Studio (компилятор, представленной Microsoft для VS) / Затмение / Net Beans так же, как user4272649
26
A ошибке в компилятор / IDE

It была ошибка в Visual Studio Express, 2013. Мне пришлось удалить исходный файл из проекта и повторно добавить его, чтобы преодолеть эту ошибку.

Steps попробовать, если вы считаете, что это может быть ошибка в компилятор / IDE:

Очистить проект (некоторые IDE имеют возможность сделать это, вы также можете сделать это вручную, удалив объектные файлы)Try начать новый проект, скопировав весь исходный код от оригинала.
Believing, что ваши инструменты разбиты, скорее всего, будет направлять вас в сторону от истинной причины. Именно поэтому гораздо более вероятно, что вы сделали ошибку, чем компилятор вызвал проблему. Очистка вашего решения или повторное создание конфигурации сборки может исправить ошибки сборки, но это не означает, что в компиляторе есть ошибка. Связанные между собой «Оказалось, что это ошибка» не подтверждается Microsoft и не воспроизводим. JDiMatteo
@ JDiMatteo На этот вопрос есть 21 ответ, и поэтому значительное количество ответов не будет «вероятным» решением. Если вы закрыли все ответы, которые под вашей вероятностью порога, то эта страница эффективно становится бесполезной, так как большинство распространенных случаев легко обнаружить в любом случае. developerbmw
23

Linked .lib файл связан с .dll

У меня была такая же проблема. Скажем, у меня есть проекты MyProject и TestProject. Я действительно связан файл Библиотека для MyProject к TestProject. Тем не менее, это Lib файл был произведен, как был построен DLL для MyProject. Кроме того, я не содержит исходный код для всех методов в MyProject, а только доступ к точкам входа в библиотеки DLL.

Чтобы решить эту проблему, я построил MyProject как LIB и связал TestProject с этим .lib-файлом (я копирую вставленный сгенерированный .lib-файл в папку TestProject). Затем я могу строить заново MyProject как DLL. Оно заключается в составлении, так как либерал, к которому TestProject связан ли содержать код для всех методов классов в MyProject.

23
USE линкера, чтобы помочь диагностировать ошибку

что выводит в той или иной степени;

Link Вызов (командная строка),Data о том, что библиотеки включены в стадии линии,The расположение библиотек,Search пути используется.

For GCC и звон; Вы, как правило, добавить-v -Wl,--verbose или-v -Wl,-v В командной строку. Более подробную информацию можно найти здесь

Linuxld человек страницы.LLVMlinker страницы. «Введение в GCC»chapter 9.

For MSVC,/VERBOSE (особенно/VERBOSE:LIB) Добавляется в командной ссылка линии.

The странице MSDN на/VERBOSE опция линкера.
18

кажется, должны быть направлены на этот вопрос, когда речь идет об ошибке компоновщика, я собираюсь добавить это здесь.

Одной из возможных причин ошибок компоновщика в GCC 5.2.0 является то, что теперь по умолчанию выбрана новая библиотека ABI libstdc ++.

If вы получите ошибки компоновщика о неопределенных ссылках на символы, которые включают типы в станде :: __ cxx11 имена или тег [аби: cxx11], то это, вероятно, означает, что вы пытаетесь объектными файлы ссылка вместе, которые были скомпилированы с разными значениями свойств _GLIBCXX_USE_CXX11_ABI макрос. Это обычно происходит, когда ссылки на библиотеку третьей стороны, что было составлен с более старой версией GCC. Если сторонняя библиотека не может быть перестроена с новым ABI, вам нужно будет перекомпилировать ваш код со старым ABI.

So если вдруг получаю ошибки компоновщика при переключении на GCC после 5.1.0, это будет вещь, чтобы проверить.

17

A обертка GNU LD, который не поддерживает компоновщик скрипты

Some .so файлы на самом делеGNU л.д. компоновщик скрипты, например Libtbb.so Файл представляет собой ASCII текстовый файл с этим содержимым:

INPUT (libtbb.so.2)

Some более сложная сборка может не поддерживать это. Например, если вы включите -v в опциях компилятора, вы увидите, чтоmainwin GCC обертка mwdip отбрасывает командные файлы сценария компоновщика в подробный список вывода библиотек для ссылки. Простой обходной путь - заменить вместо этого входной командный файл сценария компоновщика на копию файла (или символическую ссылку), например,

cp libtbb.so.2 libtbb.so

Or можно заменить аргумент -l с указанием полного пути из .so, например, вместо того-ltbb делать/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

16
Befriending шаблоны ...

unction)

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

Theoperator<< is being declared as a non-template function. For every typeT used withFoo, there needs to be a non-templatedoperator<<. For example, if there is a typeFoo<int> declared, then there must be an operator implementation as follows

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

Since it is not implemented, the linker fails to find it and results in the error

To correct this, you can declare a template operator before theFoo type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows

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

The above code limits the friendship of the operator to the corresponding instantiation ofFoo, i.e. theoperator<< <int> instantiation is limited to access the private members of the instantiation ofFoo<int>.

Alternatives include

Allowing the friendship to extend to all instantiations of the templates, as follows

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

Or, the implementation for theoperator<< can be done inline inside the class definition

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

Заметк, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, fromcppreferenc;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided..

There is further reading on template friends atcppreferenc и C ++ FAQ.

Code listing showing the techniques abov.

As a side note to the failing code sample; g++ warns about this as follow

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)

14
Your linkage consumes libraries before the object files that refer to theYou are trying to compile and link your program with the GCC toolchainYour linkage specifies all of the necessary libraries and library search pathЕслиlibfoo depends onlibbar, then your linkage correctly putslibfoo доlibbar.Your linkage fails withundefined reference to что-т errorsBut all the undefinedчто-тs are declared in the header files you have#included and are in fact defined in the libraries that you are linking

Examples are in C. They could equally well be C+

A minimal example involving a static library you built yoursel

my_lib.

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

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

my_lib.

#ifndef MY_LIB_H
#define MT_LIB_H

extern void hw(void);

#endif

eg1.

#include <my_lib.h>

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

You build your static library

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

You compile your program

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

You try to link it withlibmy_lib.a and fail

$ 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

The same result if you compile and link in one step, like

$ 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
A minimal example involving a shared system library, the compression librarylibz

eg2.

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

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

Compile your program

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

Try to link your program withlibz and fail

$ 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

Same if you compile and link in one go

$ 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

And a variation on example 2 involvingpkg-config:

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

In the sequence of object files and libraries you want to link to make your program, you are placing the libraries before the object files that refer to them. You need to place the librariesпосл the object files that refer to them

Link example 1 correctly

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

Success

$ ./eg1 
Hello World

Link example 2 correctly

$ gcc -o eg2 eg2.o -lz

Success

$ ./eg2 
1.2.8

Link the example 2pkg-config variation correctly

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

Reading is optional from here o.

By default, a linkage command generated by GCC, on your distro, consumes the files in the linkage from left to right in commandline sequence. When it finds that a file refers toчто-т and does not contain a definition for it, to will search for a definition in files further to the right. If it eventually finds a definition, the reference is resolved. If any references remain unresolved at the end, the linkage fails: the linker does not search backwards

First,example , with static librarymy_lib.a

A static library is an indexed archive of object files. When the linker finds-lmy_lib in the linkage sequence and figures out that this refers to the static library./libmy_lib.a, it wants to know whether your program needs any of the object files inlibmy_lib.a.

There is only object file inlibmy_lib.a, namelymy_lib.o, and there's only one thing defined inmy_lib.o, namely the functionhw.

The linker will decide that your program needsmy_lib.o if and only if it already knows that your program refers tohw, in one or more of the object files it has already added to the program, and that none of the object files it has already added contains a definition forhw.

If that is true, then the linker will extract a copy ofmy_lib.o from the library and add it to your program. Then, your program contains a definition forhw, so its references tohw являютсяresolve.

When you try to link the program like

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

the linkerhas not adde eg1.o to the progra when it sees-lmy_lib. Because at that point, it has not seeneg1.o. Your program does not yet make any references tohw: it does not yet make any referencesвообщ, because all the references it makes are ineg1.o.

So the linker does not addmy_lib.o to the program and has no further use forlibmy_lib.a.

Next, it findseg1.o, and adds it to be program. An object file in the linkage sequence is always added to the program. Now, the program makes a reference tohw, and does not contain a definition ofhw; but there is nothing left in the linkage sequence that could provide the missing definition. The reference tohw ends upunresolve, and the linkage fails

Second,example , with shared librarylibz

A shared library isn't an archive of object files or anything like it. It's much more like aprogra that doesn't have amain function and instead exposes multiple other symbols that it defines, so that other programs can use them at runtime

Many Linux distros today configure their GCC toolchain so that its language drivers gcc,g++,gfortran etc) instruct the system linker ld) to link shared libraries on anas-neede basis. You have got one of those distros

This means that when the linker finds-lz in the linkage sequence, and figures out that this refers to the shared library (say)/usr/lib/x86_64-linux-gnu/libz.so, it wants to know whether any references that it has added to your program that aren't yet defined have definitions that are exported bylibz

If that is true, then the linker willн copy any chunks out oflibz and add them to your program; instead, it will just doctor the code of your program so that:

At runtime, the system program loader will load a copy oflibz into the same process as your program whenever it loads a copy of your program, to run it

At runtime, whenever your program refers to something that is defined inlibz, that reference uses the definition exported by the copy oflibz in the same process

Your program wants to refer to just one thing that has a definition exported bylibz, namely the functionzlibVersion, which is referred to just once, ineg2.c. If the linker adds that reference to your program, and then finds the definition exported bylibz, the reference isresolve

But when you try to link the program like

gcc -o eg2 -lz eg2.o

the order of events is wrong in just the same way as with example 1. At the point when the linker finds-lz, there areе @ нет references to anything in the program: they are all ineg2.o, which has not yet been seen. So the linker decides it has no use forlibz. When it reacheseg2.o, adds it to the program, and then has undefined reference tozlibVersion, the linkage sequence is finished; that reference is unresolved, and the linkage fails

Lastly, thepkg-config variation of example 2 has a now obvious explanation. After shell-expansion

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

becomes

gcc -o eg2 -lz eg2.o

which is just example 2 again

I can reproduce the problem in example 1, but not in example

The linkage

gcc -o eg2 -lz eg2.o

works just fine for you

(Or: That linkage worked fine for you on, say, Fedora 23, but fails on Ubuntu 16.04

That's because the distro on which the linkage works is one of the ones that does not configure its GCC toolchain to link shared librariesas-neede.

Back in the day, it was normal for unix-like systems to link static and shared libraries by different rules. Static libraries in a linkage sequence were linked on theas-neede basis explained in example 1, but shared libraries were linked unconditionally

This behaviour is economical at linktime because the linker doesn't have to ponder whether a shared library is needed by the program: if it's a shared library, link it. And most libraries in most linkages are shared libraries. But there are disadvantages too:

It is uneconomical at Выполнения, because it can cause shared libraries to be loaded along with a program even if doesn't need them

The different linkage rules for static and shared libraries can be confusing to inexpert programmers, who may not know whether-lfoo in their linkage is going to resolve to/some/where/libfoo.a or to/some/where/libfoo.so, and might not understand the difference between shared and static libraries anyway

This trade-off has led to the schismatic situation today. Some distros have changed their GCC linkage rules for shared libraries so that theas-neede principle applies for all libraries. Some distros have stuck with the old way

Why do I still get this problem even if I compile-and-link at the same time

If I just do

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

surely gcc has to compileeg1.c first, and then link the resulting object file withlibmy_lib.a. So how can it not know that object file is needed when it's doing the linking

Because compiling and linking with a single command does not change the order of the linkage sequence

When you run the command above,gcc figures out that you want compilation + linkage. So behind the scenes, it generates a compilation command, and runs it, then generates a linkage command, and runs it, as ifт had run the two commands

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

So the linkage fails just as it does if youделат run those two commands. The only difference you notice in the failure is that gcc has generated a temporary object file in the compile + link case, because you're not telling it to useeg1.o. We see

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

вместо того

eg1.o: In function `main':
Смотрите такж

The order in which interdependent linked libraries are specified is wron

Putting interdependent libraries in the wrong order is just one way in which you can get files thatнужн definitions of things coming later in the linkage than the files thatprovid the definitions. Putting libraries before the object files that refer to them is another way of making the same mistake

13
InconsistentUNICODE definition

TCHAR etc. being defined aswchar_t etc. When not building withUNICODE defined as build withTCHAR defined aschar etc. TheseUNICODE а также_UNICODE defines affect all the"T" string type; LPTSTR, LPCTSTR and their elk

Building one library withUNICODE defined and attempting to link it in a project whereUNICODE is not defined will result in linker errors since there will be a mismatch in the definition ofTCHAR; char противwchar_t.

The error usually includes a function a value with achar илиwchar_t derived type, these could includestd::basic_string<> etc. as well. When browsing through the affected function in the code, there will often be a reference toTCHAR илиstd::basic_string<TCHAR> etc. This is a tell-tale sign that the code was originally intended for both a UNICODE and a Multi-Byte Character (or "narrow") build

To correct this, build all the required libraries and projects with a consistent definition ofUNICODE (а также_UNICODE).

This can be done with either

#define UNICODE
#define _UNICODE

Or in the project settings

Project Properties > General > Project Defaults > Character Se

Or on the command line

/DUNICODE /D_UNICODE

The alternative is applicable as well, if UNICODE is not intended to be used, make sure the defines are not set, and/or the multi-character setting is used in the projects and consistently applied

Do not forget to be consistent between the "Release" and "Debug" builds as well

12
Clean and rebuil

ng around from previous builds, failed builds, incomplete builds and other build system related build issues

In general the IDE or build will include some form of "clean" function, but this may not be correctly configured (e.g. in a manual makefile) or may fail (e.g. the intermediate or resultant binaries are read-only)

Once the "clean" has completed, verify that the "clean" has succeeded and all the generated intermediate file (e.g. an automated makefile) have been successfully removed

Этоprocess can be seen as a final resort, but is often a good first ste; especially if the code related to the error has recently been added (either locally or from the source repository)

12
When your include paths are differen

library (.lib file) go out of sync. Let me explain

How do linkers work? The linker matches a function declaration (declared in the header) with its definition (in the shared library) by comparing their signatures. You can get a linker error if the linker doesn't find a function definition that matches perfectly

Is it possible to still get a linker error even though the declaration and the definition seem to match? Yes! They might look the same in source code, but it really depends on what the compiler sees. Essentially you could end up with a situation like this

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

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

Note how even though both the function declarations look identical in source code, but they are really different according to the compiler

You might ask how one ends up in a situation like that?Include path of course! If when compiling the shared library, the include path leads toheader1.h and you end up usingheader2.h in your own program, you'll be left scratching your header wondering what happened (pun intended)

An example of how this can happen in the real world is explained below

Further elaboration with an exampl

I have two projects:graphics.lib а такжеmain.exe. Both projects depend oncommon_math.h. Suppose the library exports the following function

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

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

And then you go ahead and include the library in your own project

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

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

Boom! You get a linker error and you have no idea why it's failing. The reason is that the common library uses different versions of the same includecommon_math.h (I have made it obvious here in the example by including a different path, but it might not always be so obvious. Maybe the include path is different in the compiler settings)

Note in this example, the linker would tell you it couldn't finddraw(), when in reality you know it obviously is being exported by the library. You could spend hours scratching your head wondering what went wrong. The thing is, the linker sees a different signature because the parameter types are slightly different. In the example,vec3 is a different type in both projects as far as the compiler is concerned. This could happen because they come from two slightly different include files (maybe the include files come from two different versions of the library)

Debugging the linke

DUMPBIN is your friend, if you are using Visual Studio. I'm sure other compilers have other similar tools

The process goes like this

Note the weird mangled name given in the linker error. (eg. [email protected]@XYZ)Dump the exported symbols from the library into a text fileSearch for the exported symbol of interest, and notice that the mangled name is differentPay attention to why the mangled names ended up different. You would be able to see that the parameter types are different, even though they look the same in the source codeReason why they are different. In the example given above, they are different because of different include files

[1] By project I mean a set of source files that are linked together to produce either a library or an executable

EDIT 1: Rewrote first section to be easier to understand. Please comment below to let me know if something else needs to be fixed. Благодарность

7
Missing "extern" inconst variable declarations/definitions (C++ only

constvariables have internal (or static) linkage. In C this was not the case, as all global variables are implicitlyextern (i.e. when thestatic keyword is missing)

Пример

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

correct would be to use a header file and include it in file2.cppа такж file1.cp

extern const int test;
extern int test2;

Alternatively one could declare theconst variable in file1.cpp with explicitextern

3

ers, I'd like to share how to resolve anobscur "undefined reference to" error

Different versions of librarie

I was using an alias to refer tostd::filesystem::path: filesystem is in the standard library since C++17 but my program needed toalso compile in C++1 so I decided to use a variable alias

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

Let's say I have three files: main.cpp, file.h, file.cpp

file. #include's <experimental::filestyste> and contains the code ab,ovfile.cp, the implementation of file.h, #include's "file."main.cp #include's <filestyste> and "file."

Обратите вниманиеdifferent librarie used in main.cpp and file.h. Since main.cpp #include'd "file." after <filestyste>, the version of filesystem used there wasthe C++17 on. I used to compile the program with the following commands

$ g++ -g -std=c++17 -c main.cpp -> compiles main.cpp to main.
$ g++ -g -std=c++17 -c file.cpp -> compiles file.cpp and file.h to file.
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs -> links main.o and file.

This wayany functio contained in file.o and used in main.o thatrequiredpath_t gave "undefined reference" errors becausemain. referred tostd::filesystem::path ноfile. вstd::experimental::filesystem::path.

Разрешени

To fix this I just needed tochange <experimental::filestystem> in file.h to <filestystem>.

2
When linking against shared libraries, make sure that the used symbols are not hidden

when the translation units are built with option-fvisibility=hidden, only functions/symbols marked with__attribute__ ((visibility ("default"))) are external in the resulting shared object

You can check whether the symbols your are looking for are external by invoking

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

the hidden/local symbols are shown bynm with lowercase symbol type, for examplet instead of `T for code-section

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

Вы также можете использоватьnm with the option-C to demangle the names (if C++ was used)

Similar to Windows-dlls, one would mark public functions with a define, for exampleDLL_PUBLIC defined as

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

DLL_PUBLIC int my_public_function(){
  ...
}

Which roughly corresponds to Windows'/MSVC-version

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

Moreinformation about visibilit can be found on the gcc wiki

When a translation unit is compiled with-fvisibility=hidden the resulting symbols have still external linkage (shown with upper case symbol type bynm) and can be used for external linkage without problem if the object files become part of a static libraries. The linkage becomes local only when the object files are linked into a shared library

To find which symbols in an object file are hidden run

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
Ты должен использоватьnm -CD илиnm -gCD to view external symbols. Также смVisibilit on the GCC wiki jww
0

Different architecture

You may see a message like

library machine type 'x64' conflicts with target machine type 'X86'

In that case, it means that the available symbols are for a different architecture than the one you are compiling for

On Visual Studio, this is due to the wrong "Platform", and you need to either select the proper one or install the proper version of the library

On Linux, it may be due to the wrong library folder (usinglib вместо тогоlib64 for instance)

On MacOS, there is the option of shipping both architectures in the same file. It may be that the link expects both versions to be there, but only one is. It can also be an issue with the wronglib/lib64 folder where the library is picked up

Похожие вопросы