Your linkage consumes libraries before the object files that refer to them
- You are trying to compile and link your program with the GCC toolchain.
- Your linkage specifies all of the necessary libraries and library search paths
- If
libfoo
depends onlibbar
, then your linkage correctly putslibfoo
beforelibbar
. - Your linkage fails with
undefined reference to
something errors. - But all the undefined somethings are declared in the header files you have
#include
d 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 yourself
my_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
my_lib.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
eg1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
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 with libmy_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 library libz
eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%sn",zlibVersion());
return 0;
}
Compile your program:
$ gcc -c -o eg2.o eg2.c
Try to link your program with libz
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 involving pkg-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 after 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 2 pkg-config
variation correctly:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
The explanation
Reading is optional from here on.
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 something
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 1, with static library my_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 in libmy_lib.a
.
There is only object file in libmy_lib.a
, namely my_lib.o
, and there’s only one thing defined
in my_lib.o
, namely the function hw
.
The linker will decide that your program needs my_lib.o
if and only if it already knows that
your program refers to hw
, 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 for hw
.
If that is true, then the linker will extract a copy of my_lib.o
from the library and
add it to your program. Then, your program contains a definition for hw
, so
its references to hw
are resolved.
When you try to link the program like:
$ gcc -o eg1 -L. -lmy_lib eg1.o
the linker has not added eg1.o
to the program when it sees
-lmy_lib
. Because at that point, it has not seen eg1.o
.
Your program does not yet make any references to hw
: it
does not yet make any references at all, because all the references it makes
are in eg1.o
.
So the linker does not add my_lib.o
to the program and has no further
use for libmy_lib.a
.
Next, it finds eg1.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 to hw
, and does not contain a definition of hw
; but
there is nothing left in the linkage sequence that could provide the missing
definition. The reference to hw
ends up unresolved, and the linkage fails.
Second, example 2, with shared library libz
A shared library isn’t an archive of object files or anything like it. It’s
much more like a program that doesn’t have a main
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 an as-needed 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 by libz
If that is true, then the linker will not copy any chunks out of libz
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 of
libz
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 in
libz
, 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 by libz
,
namely the function zlibVersion
, which is referred to just once, in eg2.c
.
If the linker adds that reference to your program, and then finds the definition
exported by libz
, the reference is resolved
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 no references to anything
in the program: they are all in eg2.o
, which has not yet been seen. So the
linker decides it has no use for libz
. When it reaches eg2.o
, adds it to the program,
and then has undefined reference to zlibVersion
, the linkage sequence is finished;
that reference is unresolved, and the linkage fails.
Lastly, the pkg-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 2
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 libraries as-needed.
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 the as-needed 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 runtime, 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 the as-needed
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 compile eg1.c
first, and then link the resulting
object file with libmy_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 you 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 do 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 use eg1.o
. We see:
/tmp/ccQk1tvs.o: In function `main'
instead of:
eg1.o: In function `main':
See also
The order in which interdependent linked libraries are specified is wrong
Putting interdependent libraries in the wrong order is just one way
in which you can get files that need definitions of things coming
later in the linkage than the files that provide the definitions. Putting libraries before the
object files that refer to them is another way of making the same mistake.
Мы видели и устраняли множество исключений при кодировании на любом языке программирования. Ссылка Undefined — это одно из тех исключений, которые происходят в основном в языке C, поскольку название предполагает, что это произойдет, когда в скрипте отсутствует определение какой-либо функции. Таким образом, эта статья предложит вам несколько простых методов устранения этой ошибки, начиная с ее создания. Обязательно войдите в систему из системы Linux и откройте терминал, чтобы начать реализацию, с помощью метода ярлыка «Ctrl + Alt + T».
Примечание. Мы уже установили компилятор gcc в нашу работающую операционную систему.
Пример 01:
Одной из популярных причин ошибки undefined reference может быть проблема с именем функции. Итак, в этом примере мы увидим, как ошибка может быть сгенерирована и разрешена с использованием имени функции. Мы создадим новый файл «exp.c», чтобы добавить к нему наш код C в терминале оболочки.
$ трогать exp.c
Файл успешно создан в домашнем каталоге. Чтобы открыть этот файл, нам понадобится несколько приложений-редакторов. Поэтому мы использовали редактор Nano, чтобы открыть его.
$ нано exp.c
Мы сохранили наш код C простым, чтобы наши пользователи могли его легко понять. Мы включили заголовочный файл в начало кода. После этого была определена функция main () для вывода информации о том, что мы сейчас работаем над основным методом. Заклинания в названиях функций неверны. Таким образом, он должен отображать неопределенную ссылку на ошибку при компиляции кода. Мы сохранили наш файл кода и вернулись в оболочку.
Убедитесь, что в вашей системе Linux настроен компилятор C. Здесь мы использовали вечный компилятор GCC для компиляции вышеуказанного кода C. После компиляции компилятором GCC мы получили неопределенную ссылку на ошибку main (), как и ожидалось.
$ gcc exp.c
Когда мы выполнили файл, он говорит, что такого файла или каталога нет.
$ ./а. выход
Давайте исправим код и изменим имя функции на main (), как показано на рисунке ниже.
Таким образом, на этот раз, когда вы скомпилируете и запустите файл, он будет работать без ошибок, то есть с неопределенной ссылкой на функцию.
$ gcc exp.c
./а. выход
Пример 02:
Приведем еще один пример той же ошибки. Итак, откройте тот же файл и добавьте стандартный заголовок ввода-вывода. После этого был объявлен прототип функции для метода show (). В конце концов, был определен основной метод для печати некоторого текста и вызова функции show (), которая была объявлена ранее.
После компиляции мы получили ошибку как неопределенную ссылку на show (). Это связано с тем, что метод show () был только объявлен, но не определен в коде.
$ gcc exp.c
Чтобы устранить эту ошибку, мы еще раз обновили код. После основного метода добавлено определение функции метода show (). Этот метод содержит внутри себя один оператор печати.
Когда мы скомпилировали файл, ошибка была устранена, и код был успешно выполнен.
$ gcc exp.c
./а. выход
Пример 03:
Другая ошибка, которую делают большинство пользователей при программировании, — это игнорирование чувствительности синтаксиса языка к регистру. Это также может привести к возникновению ошибки undefined reference. Итак, давайте рассмотрим эту проблему на этом примере. До сих пор мы использовали тот же код. Мы только что использовали другой синтаксис регистра для имен функций, то есть show и Show. Определение функции содержит имя с маленькой буквы, а вызов функции — с заглавной буквы. Сохраните этот код.
Использовал компилятор GCC для компиляции файла exp.c. Ошибка: неопределенная ссылка на функцию show () появилась в оболочке терминала, как и предполагалось.
$ gcc exp.c
Чтобы решить эту ошибку, просто откройте файл и сделайте имя функции таким же, как в ее определении и вызове функции. Итак, мы использовали show (), то есть маленькие имена кейсов, чтобы идти дальше. Сохраните этот обновленный код, чтобы скомпилировать его.
Когда мы скомпилировали код, он, наконец, успешно скомпилировался без каких-либо ошибок. Таким образом, выполнение этого файла работает нормально, как показано на изображении ниже.
$ gcc exp.c
./а. выход
Заключение:
В этом руководстве мы увидели, как простая ошибка пользователя может привести к возникновению ссылки на неопределенную ошибку на функцию. Чтобы устранить эту ошибку, мы реализовали простые методы на иллюстрациях, чтобы сделать их понятными. Мы надеемся, что эта статья наилучшим образом соответствует вашим требованиям.
Определение
Данная ошибка означает, что в процессе компоновки программы, компоновщик не смог найти определение некоторой сущности, на которую есть ссылка (попытка использования) в программе.
К таким сущностям может относиться, например, функция или переменная.
Причины и решения
Возможных причин появления ошибки может быть несколько и это зависит от того, что представляет из себя собираемый проект. Всё множество ситуаций можно разбить на две большие группы:
Используются сторонние библиотеки
-
Не указана необходимая (статическая) библиотека для компоновщика.
Например, к проекту подключен только
*.h
файл с объявлениями, но отсутствует код реализации, обычно это*.lib
или*.a
файлы (в зависимости от используемой системы).
Требуется явно подключить библиотеку к проекту.
-
Для Visual C++ это можно сделать добавлением следующей строки прямо в код:
#pragma comment(lib, "libname.lib")
-
Для
gcc/clang
требуется указать файл через ключ-l
(эль) -
Для Qt в .pro файле нужно использовать переменную
LIBS
:LIBS += -L[путь_к_библиотеке] -l[имя_библиотеки]
-
Для системы сборки
cmake
естьtarget_link_libraries
.
-
Библиотека указана, но необходимая сущность, например, класс или функция фактически не экспортируется из библиотеки. Под Windows это может возникать из-за отсуствия
__declspec(dllexport)
перед сущностью. Обычно это решается макросами. Данная ситуация чаще всего характерна именно для библиотек, находящихся в процессе разработки, и доступных для модификации самому разработчику, нежели для каких-то стабильных версий действительно внешних для проекта библиотек. После разрешения экспортирования библиотеку, конечно же, нужно пересобрать перед непосредственным использованием проблемной сущности. -
Библиотека указана, но не совпадает разрядность библиотеки и компилируемого кода.
В общем случае, разрядность собираемого проекта (приложения или библиотеки) должна совпадать с разрядностью используемой сторонней библиотеки. Обычно производители библиотек предоставляют возможность выбора 32 или 64 бит версию использовать. Если библиотека поставляется в исходных кодах и собирается отдельно от текущего проекта, нужно также выбрать правильную разрядность.
-
Библиотека указана, но она собрана для другой (не совместимой) ОС.
Например при сборке проекта в Windows осуществляется попытка использовать бинарный файл, собранный для Linux. В данном случае нужно использовать файлы, подходящие для вашей ОС.
-
Библиотека указана, но она собрана другим компилятором, не совместимым с используемым.
Объектные файлы, полученные путем сборки C++ кода разными компиляторами для одной и той же ОС могут быть бинарно несовместимы друг с другом. Требуется использовать совместимые (скорее всего и вовсе одинаковые) компиляторы.
-
Библиотека указана, и собрана тем же компилятором, что и основной проект, но используются разные версии Run-Time библиотек.
Например, для Visual C++ возможна ситуация, когда библиотека собрана с ключом
/MDd
, а основной проект с/MTd
. Требуется задать ключи так, чтобы использовались одинаковые версии Run-Time библиотек.
Сторонние библиотеки не используются
-
Просто отсутствует определение функции.
void f(int); // всего лишь объявление. Нет `тела` функции int main(){ f(42); // undefined reference to `f(int)' }
Требуется добавить определение функции
f
:void f(int) { // тело функции }
Может быть ещё частный случай с ошибкой вида:
undefined reference to `vtable for <имя_класса>`
Такая ошибка возникает, если объявленная виртуальная функция класса, не являющаяся чистой (
=0
), не содержит реализации.class C { virtual void f(int); };
Нужно такую реализацию добавить. Если это делается вне класса, надо не забыть указать имя проблемного класса, иначе это будет просто свободная функция, которая не устраняет указанную проблему:
void C::f(int) { // виртуальная функция-член вне определения класса // тело функции } void f(int) { // свободная функция, не устраняющая проблему // тело функции }
Аналогичная ситуация может возникать при использовании пространств имён, когда объявлении функции находится в пространстве имён:
// В заголовочном файле namespace N { void f(int); };
а при реализации указать это пространство имён забыли:
// В файле реализации void f(int) { // функция в глобальном пространстве имён, не устраняющая проблему // тело функции } namespace N { void f(int) { // функция в нужном пространстве имён // тело функции } } // конец пространства имён
Стоит заметить, что C++ разрешает перегрузку функций (существование одноимённых функций, но с разным набором параметров), и в этом случае важно, чтобы сигнатуры функций при объявлении и определении совпадали. Например, вместо объявленной
void f(int)
была реализована другая:void f(const char*) { // const char* вместо int // тело функции }
При вызове
f(42)
будет ошибка:undefined reference to `f(int)'
Наличие связки шаблонного класса и дружественной функции также может приводить к ошибке. Например:
template <class T> struct C { friend void f(C<T>); // объявляет *НЕ*шаблонную дружественную функцию }; template <class T> // определяет шаблонную функцию void f(C<T>) { } int main() { C<int> c; f(c); // undefined reference to `f(C<int>)' }
Чтобы объявить шаблонную дружественную функцию, требуется добавить указание шаблонности:
template <class T> struct C { template <class V> // добавили шаблонность для friend функции friend void f(C<T>); };
Важно, что имя шаблонного параметра для дружественной функции должно отличаться от имени параметра шаблонного класса
T
, т.к. иначе будет ошибка о сокрытии имени. В частном случае имя класса можно вовсе не указывать, и оставитьtemplate <class>
. Но это не всегда будет правильным решением, т.к. фактически могла потребоваться дружественная специализация шаблона функции, а не сама шаблонная функция. Подробнее об использовании дружественных функций в шаблонном классе можно ознакомиться здесь. -
Отсутствует определение статической переменной класса.
struct S { static int i; }; int main() { S s; s.i = 42; // undefined reference to `S::i' }
Нужно добавить определение (выделить память) переменной:
int S::i;
-
Неправильная реализация шаблонного кода.
Например, реализация шаблонного кода помещена в
*.cpp
файл, хотя она должна находиться полностью в подключаемом*.h
файле. Это требуется по той причине, что компилятор обрабатывает каждый модуль независимо, и в момент инстанцирования шаблона (подстановки конкретного типа) код его реализации должен быть виден. При этом если возможные типы шаблона известны заранее, можно произвести инстанцирование сразу рядом с телом шаблона и не предоставлять его наружу в исходном коде заголовочного файла. Пример:// unit.h #pragma once template <class T> T f(T); // объявление шаблона без реализации
// unit.cpp #include "unit.h" template <class T> T f(T t) { return t + t; } // реализация шаблона template int f<int>(int); // явное инстанцирование для int template double f<double>(double); // явное инстанцирование для double
// main.cpp #include "unit.h" int main() { f(2); // ok int f(1.5); // ok double f('a'); // undefined reference to `char f<char>(char)' }
-
Файл с кодом не был скомпилирован.
Например, в случае использования make-файла не было прописано правило построения файла, а в случае использования IDE типа Visual Studio
*.cpp
файл не добавлен в список файлов проекта. -
Виртуальная функция в базовом классе не объявлена как
= 0
(pure-virtual).struct B { void virtual f(); }; struct D : B { void f() {} }; int main() { D d; }
При использовании иерархии классов функция в базовом классе, не имеющая реализации должна быть помечена как «чистая»:
struct B { void virtual f() = 0; };
-
Имя не имеет внешнего связывания.
Например, есть объявление функции
f
в модулеА
и даже ее реализация в модулеB
, но реализация помечена какstatic
:// A.cpp void f(); int main() { f(); // undefined reference to `f()' } // B.cpp static void f() {}
Аналогичная ситуация может возникнуть при использовании безымянного пространства имен:
// B.cpp namespace { void f() {} }
Или даже при наличии
inline
у функции:// B.cpp inline void f() {}
Каталог статей
- 1. Найдите расположение файла библиотеки в Linux.
- 2. Как просмотреть функции, содержащиеся в библиотеке
- 3. Раздражающая неопределенная ссылка на
- 4. Решение проблемы gcc «неопределенная ссылка на»
- 5. Ошибка динамической библиотеки линковки gcc в Linux: разница между LIBRARY_PATH и LD_LIBRARY_PATH
1. Найдите расположение файла библиотеки в Linux.
ldconfig -p | grep libcrypto
2. Как просмотреть функции, содержащиеся в библиотеке
- Используйте команду nm системы Linux. Это просто. У меня возникла проблема, когда я его использовал:
(1) Например, nm lib_a.so。
Итак, введите следующую команду:
nm -Do lib_a.so
Вы можете увидеть библиотечные функции внутри.
(2)nm libyolo.so | grep 'compute_box_iou'
U compute_box_iou
Примечание: значение U само по себе является неопределенным значением undefined
Описание:
(1) Вот.Файл so представляет собой файл библиотеки динамической компоновки. Файл эльфийский(Executable and Linkable Format)Тип файла, есть две таблицы символов, ".symtab "и".dynsym”。
(2) Просмотр таблицы динамических символов ".дынсым ", плюс-Вариант D;
(3)“.дынсым "только держать".глобальные символы в "symtab"(global symbols);
(4)nm lib_a.так просмотрено ".таблица символов для "symtab"
- ldd + исполняемый файл, ldd + so библиотека
(1)
#ldd main
linux-gate.so.1 => (0xb809c000)
libc.so.6 +> /lib/tls/i686/cmov/libc.so,6 (0x7f0c000)
(2)
# ldd /usr/lib/aarch64-linux-gnu/libSM.so
linux-vdso.so.1 => (0x0000ffffb1b70000)
libICE.so.6 => /usr/lib/aarch64-linux-gnu/libICE.so.6 (0x0000ffffb1ae0000)
libuuid.so.1 => /lib/aarch64-linux-gnu/libuuid.so.1 (0x0000ffffb1ac0000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffb1970000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffb1b80000)
3. Раздражающая неопределенная ссылка на
- Обратите внимание на мысли:
Это вызвано неправильным использованием указанного ниже макроса защиты!
#ifndef ZW_STACK_ARRAY
#define ZW_STACK_ARRAY
Спасибо zwlist.Этот макрос в h был определен, когда вы находитесь в zwlist.c содержит zwlist.Через час
#ifndef ZW_STACK_ARRAY больше не действует, все коды в zwlist.c напрямую игнорируются
4. Решение проблемы gcc «неопределенная ссылка на»
- (1) Связанный целевой файл (.o) отсутствует при связывании
Тестовый код выглядит следующим образом:
Затем скомпилируйте.
gcc -c test.c
gcc –c main.c
Получить два .o файл, один главный.о, один тест.о, то связываем .o Получить исполняемую программу:
gcc -o main main.o
В это время вы обнаружите, что сообщается об ошибке:
main.o: In function `main':
main.c:(.text+0x7): undefined reference to `test'
collect2: ld returned 1 exit status
Это наиболее типичная ошибка неопределенной ссылки, поскольку обнаруживается, что файл реализации определенной функции не может быть найден при связывании. В этом случае проверьте.o файл содержит тест()Реализация функции,
Так что, если вы сделаете ссылку таким образом, все будет хорошо.
gcc -o main main.o test.o
- (2) Связанные файлы библиотеки (.a / .so) отсутствуют при компоновке
Сначала скомпилируйте test.c в статическую библиотеку(.a)файл
gcc -c test.c
ar -rc test.a test.o
На данный момент у нас есть файл test.a. Начинаем компилировать main.c
gcc -c main.c
В это время создается файл main.o, а затем мы связываемся с помощью следующей команды, чтобы получить исполняемую программу.
gcc -o main main.o
Вы обнаружите, что компилятор сообщил об ошибке:
/tmp/ccCPA13l.o: In function `main':
main.c:(.text+0x7): undefined reference to `test'
collect2: ld returned 1 exit status
Основная причина в том, что тест не может быть найден()Файл реализации функции, потому что тест()Реализация функции находится в статической библиотеке test.a,
Следовательно, при компоновке вам необходимо добавить библиотеку test.a после этого и изменить команду компоновки на следующую форму.
gcc -o main main.o ./test.a // Примечание: ./ - это путь к test.a
- (3) Другой файл библиотеки используется в файле связанной библиотеки.
Как видно из рисунка выше, main.c вызывает функцию test.c, а test.c вызывает функцию fun.c.
Сначала мы компилируем fun.c, test.c и main.c для создания файлов .o.
gcc -c func.c
gcc -c test.c
gcc -c main.c
Затем test.c и func.c упаковываются в файлы статической библиотеки.
ar –rc func.a func.o
ar –rc test.a test.o
На данный момент мы готовы связать main.o как исполняемую программу, потому что наш main.c содержит тест()Вызов,
Следовательно, при компоновке в качестве файла нашей библиотеки следует использовать test.a. Команда компоновки выглядит следующим образом.
gcc -o main main.o test.a
В это время компилятор по-прежнему сообщает об ошибке следующим образом:
test.a(test.o): In function `test':
test.c:(.text+0x13): undefined reference to `func'
collect2: ld returned 1 exit status
Другими словами, при компоновке мы обнаружили, что наш test.a называется func()Функция, соответствующей реализации не найдено.
Из этого мы обнаружили, что нам все еще нужно добавить файл библиотеки, на который ссылается test.a, для успешного связывания, поэтому команда выглядит следующим образом.
gcc -o main main.o test.a func.a
- (4) Последовательность связывания нескольких файлов библиотеки
Такого рода проблемы также очень скрыты, и вы можете почувствовать себя сбитым с толку, если не изучите их внимательно.
Мы по-прежнему возвращаемся к вопросам, обсуждаемым в разделе 3. В конце, если мы изменим порядок связанных библиотек, посмотрим, что произойдет?
gcc -o main main.o func.a test.a
Мы получим следующую ошибку.
test.a(test.o): In function `test':
test.c:(.text+0x13): undefined reference to `func'
collect2: ld returned 1 exit status
Следовательно, нам нужно обращать внимание на порядок зависимости между библиотеками при предоставлении зависимых библиотек в команде компоновки.
Библиотеки, которые зависят от других библиотек, должны быть размещены перед зависимыми библиотеками, чтобы действительно избежать ошибок неопределенных ссылок и полной компиляции и компоновки.
Чем больше вы называете чужую библиотеку более высокого уровня, тем больше вы ставите ее впереди.
- (5) Связывание библиотеки языка C с кодом C ++.
Если ваш файл библиотеки создается кодом c, вы также столкнетесь с проблемой неопределенной ссылки при компоновке библиотечных функций в коде c ++.
Ниже приводится пример.
Сначала напишите файл библиотеки версии языка C:
Скомпилировать и упаковать как статическую библиотеку: test.a
gcc -c test.c
ar -rc test.a test.o
На данный момент у нас есть файл test.a. Далее мы начинаем писать файл C ++ main.cpp
Затем скомпилируйте main.cpp для создания исполняемой программы:
g++ -o main main.cpp test.a
Вы обнаружите ошибку:
/tmp/ccJjiCoS.o: In function `main':
main.cpp:(.text+0x7): undefined reference to `test()'
collect2: ld returned 1 exit status
Причина в том, что main.cpp - это код C ++, который вызывает функцию библиотеки языка C, поэтому его нельзя найти при компоновке.
Решение: в main.cpp включите файл заголовка, связанный с библиотекой языка c test.a, и добавьте extern "C"Заявление может быть.
Например, модифицированный main.cpp выглядит следующим образом:
g++ -o main main.cpp test.a
Повторная компиляция обнаружит, что проблема была успешно решена.
5. Ошибка динамической библиотеки линковки gcc в Linux: разница между LIBRARY_PATH и LD_LIBRARY_PATH
- При компиляции Linux gcc путь поиска динамической библиотеки Последовательность пути поиска динамической библиотеки следующая(Обратите внимание, что он не будет выполнять рекурсивный поиск в своих подкаталогах):
1. Параметр -L в командах компиляции и компоновки gcc;
2. LIBRARY_PATH переменных среды gcc (несколько путей разделяются двоеточиями);
3. Каталог динамических библиотек Gcc по умолчанию: / lib: / usr / lib: usr / lib64: / usr / local / lib.
- Когда Linux gcc связан, для создания исполняемых двоичных файлов путь поиска динамической библиотеки выглядит следующим образом:
1. Путь поиска динамической библиотеки, указанный при компиляции целевого кода: путь поиска динамической библиотеки, указанный с помощью параметров -Wl, rpath и include, таких как gcc -Wl,
-rpath, include -L. -ldltest hello.c, путь будет найден при запуске файла`./include`;
2. Переменная среды LD_LIBRARY_PATH (несколько путей разделяются двоеточиями);
3. Абсолютный путь к динамической библиотеке, указанный в файле конфигурации в каталоге /etc/ld.so.conf.d/ (действителен через ldconfig, обычно используется для пользователей без полномочий root);
4. Каталог динамических библиотек Gcc по умолчанию: / lib: / usr / lib: usr / lib64: / usr / local / lib и т. Д.
- Справка:
gcc "undefined reference to" Метод решения проблемы
https://blog.csdn.net/qq_16542775/article/details/81353841
C++ Использовать библиотеку динамической компоновки xx.Ошибка неопределенной ссылки возникает, когда
https://blog.csdn.net/yaoqi_isee/article/details/69948506
Раздражающая неопределенная ссылка на...
https://bbs.csdn.net/topics/330055272
Найдите расположение файла библиотеки в Linux
https://blog.csdn.net/fanhenghui/article/details/89378785
"undefined reference to" Метод решения проблемы
https://blog.csdn.net/aiwoziji13/article/details/7330333
Как просмотреть функции, содержащиеся в библиотеке
https://blog.csdn.net/ostar_liang/article/details/14161799
"undefined reference to" Краткое описание проблемы и решение
https://segmentfault.com/a/1190000006049907?utm_source=tuicool&utm_medium=referral
Проблема порядка ссылок библиотеки gcc
https://www.cnblogs.com/ironx/p/4939508.html
Ошибка динамической библиотеки Linux gcc link: разница между LIBRARY_PATH и LD_LIBRARY_PATH
https://www.cnblogs.com/icxy/p/7943996.html
h_wolf 11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
||||||||||||
1 |
||||||||||||
неопределенная ссылка09.10.2013, 16:47. Показов 13761. Ответов 18 Метки нет (Все метки)
Некомпилируется проект. Не могу понять где ошибка. prata-10-2-head.h Кликните здесь для просмотра всего текста
Prata-10-2.cpp Кликните здесь для просмотра всего текста
main.cpp Кликните здесь для просмотра всего текста
error: Кликните здесь для просмотра всего текста
F:lessonsPrataCodeBlocksPrata-10-2prata-10-2-head.h|1|warning: extra tokens at end of #ifndef directive [enabled by default]|
0 |
Programming Эксперт 94731 / 64177 / 26122 Регистрация: 12.04.2006 Сообщений: 116,782 |
09.10.2013, 16:47 |
18 |
249 / 219 / 63 Регистрация: 30.07.2013 Сообщений: 465 |
|
09.10.2013, 16:55 |
2 |
Дефис — недопустимый символ для имени макроса. Исправьте это, дальше видно будет.
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
09.10.2013, 16:58 [ТС] |
3 |
Fyret, Картина не поменялась. Добавлено через 1 минуту
0 |
249 / 219 / 63 Регистрация: 30.07.2013 Сообщений: 465 |
|
09.10.2013, 17:17 |
4 |
Или в имени файла тоже дефисы нельзя? Не знаю про такое ограничение, так что скорее всего можно. Спрошу на всякий случай: Prata-10-2.cpp компилируется? полученный объектный файл линкуется?
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
09.10.2013, 17:19 [ТС] |
5 |
Fyret, Как в Code::Blocks проверить линкуется ли? Влогах я ошибок в нем не видел. собственно весь лог в спойлере error.
0 |
249 / 219 / 63 Регистрация: 30.07.2013 Сообщений: 465 |
|
09.10.2013, 17:33 |
6 |
Про Code::Blocks не знаю. Но если файл — часть проекта, должен компилироваться и линковаться.
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 09:23 [ТС] |
7 |
Все три файла относятся к одно проекту, лежат в одной директории. Добавлено через 15 часов 48 минут
0 |
26 / 26 / 13 Регистрация: 13.04.2013 Сообщений: 79 |
|
10.10.2013, 09:48 |
8 |
Рабочий проект в аттаче.
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 10:11 [ТС] |
9 |
tzeentch, это конечно очень здорово, но не могли бы Вы поведать где был баг? Добавлено через 1 минуту
0 |
Ilot 2013 / 1342 / 382 Регистрация: 16.05.2013 Сообщений: 3,463 Записей в блоге: 6 |
||||
10.10.2013, 10:16 |
10 |
|||
То что я нашел:
Ну и так по мелочи…
1 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 10:18 [ТС] |
11 |
Ilot, Ок, а остальные ошибки?
0 |
26 / 26 / 13 Регистрация: 13.04.2013 Сообщений: 79 |
|
10.10.2013, 10:24 |
12 |
где был баг? Вы не добавили СРР файл в проект, и он не компилировался => ошибка undefined reference, которая возникает когда вызывается обьявленная, но не определенная функция. Я добавил его в проект и исправил несколько мелких синтаксических ошибок, часть из них описал Ilot, все сейчас не помню.
1 |
Ilot 2013 / 1342 / 382 Регистрация: 16.05.2013 Сообщений: 3,463 Записей в блоге: 6 |
||||
10.10.2013, 10:39 |
13 |
|||
Ilot, Ок, а остальные ошибки? ХЗ Исправлял по ходу дела. Например мой компилятор не одобряет определение статической константы в классе пришлось делать через перечисления.
Впринципе смотрите на что у вас компилятор ругается. Он мужЫк умный его надо слушать.
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 11:00 [ТС] |
14 |
Он мужЫк умный его надо слушать. Таки я его и слушаю ) tzeentch, все три файло были в проекте! )) В том то и дело. Может быть сбой линковщика или IDE?
0 |
alsav22 5496 / 4891 / 831 Регистрация: 04.06.2011 Сообщений: 13,587 |
||||||||||||||||||||
10.10.2013, 11:03 |
15 |
|||||||||||||||||||
а остальные ошибки? errors: 1)
2)
3)
4)
warnings:
1 |
26 / 26 / 13 Регистрация: 13.04.2013 Сообщений: 79 |
|
10.10.2013, 11:08 |
16 |
tzeentch, все три файло были в проекте! )) В том то и дело. Может быть сбой линковщика или IDE? Я не видел ваш проект. Я скопипастил код, который вы предоставили, и заставил его работать. Сбой линковщика — простите, что?? Он ясно вам говорит судя по еррорлогу — отсутсвует определение класса Person. Вот сбой IDE возможен, точнее, неверное понимание работы с ней. Если бы вы выложили проект целиком, то можно было бы понять, в чем там ошибка… телепатией я, к сожалению, не владею.
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 12:27 [ТС] |
17 |
tzeentch, link
Код C++ А как тогда определить аргумент по умолчанию?
0 |
tzeentch 26 / 26 / 13 Регистрация: 13.04.2013 Сообщений: 79 |
||||
10.10.2013, 12:30 |
18 |
|||
А как тогда определить аргумент по умолчанию? Только в прототипе в h-файле, в cpp не надо. Пример:
0 |
11 / 11 / 2 Регистрация: 24.01.2013 Сообщений: 212 |
|
10.10.2013, 12:31 [ТС] |
19 |
Только в прототипе в h-файле, в cpp не надо. Понял, спасибо. Как-то не подумал об этом.
0 |
Хочу написать класс для матриц, но почему то вылетает ошибка линковки — Неопределённая ссылка на «matrix::matrix(int, int, std::vector >&&)
Вот main.cpp:
#include "matrix.hpp"
#include <vector>
int main()
{
std::vector<int> vec{1,2,3,4};
matrix<int> mat(2,2,std::move(vec));
return 0;
}
Вот matrix.cpp
#include "matrix.hpp"
#include <algorithm>
template<typename Type>
matrix<Type>::matrix(int m,int n,std::vector<Type>&&data){
if(m*n!=data.size())
{
throw "Error: Bad Size";
}
auto curentPos=data.begin();
for(int i=0;i<m;i++){
std::copy(curentPos,curentPos+n,this->data[i].begin);
curentPos+=n;
}
}
Вот matrix.hpp
#ifndef MATRIX
#define MATRIX
#include <vector>
template<typename Type>
class matrix{
private:
int m;
int n;
std::vector<std::vector<Type>> data;
public:
matrix(int m,int n,std::vector<Type>&&data);
};
#endif /* MATRIX */
А вот Makefile:
all: compile
rm {main.o,matrix.o}
compile: main.o matrix.o
clang++ main.o matrix.o -std=c++11 -O -o main
main.o: source/main.cpp
clang++ source/main.cpp -I/home/dima/Project/CPP/app/ -c
matrix.o: source/matrix.cpp
clang++ source/matrix.cpp -I/home/dima/Project/CPP/app/ -c
Каковы undefined ссылки/нерешенные внешние ошибки символов? Каковы распространенные причины и способы их устранения/предотвращения?
Не стесняйтесь редактировать/добавлять свои собственные.
Ответ 1
Компиляция программы на С++ выполняется в несколько этапов, как указано 2.2 (кредиты для Кейта Томпсона для справки):
Приоритет среди правил синтаксиса перевода определяется следующими фазами [см. сноску].
- Символы физического исходного файла сопоставляются в соответствии с реализацией в соответствии с базовым набором символов источника (ввод символов новой строки для индикаторов конца строки), если необходимо. [СНиП]
- Каждый экземпляр символа обратной косой черты (), за которым сразу следует символ новой строки, удаляется, сплайсируя физические строки источника образуют логические строки источника. [СНиП]
- Исходный файл разбивается на токены предварительной обработки (2.5) и последовательности символов пробела (включая комментарии). [СНиП]
- Выполняются предпроцессорные директивы, расширяются макрокоманды и выполняются операторные выражения _Pragma. [СНиП]
- Каждый элемент набора символов в символьном литерале или строковый литерал, а также каждая escape-последовательность и имя универсального символа в символьном литерале или неровном строковом литерале преобразуется в соответствующий член набора символов выполнения; [СНиП]
- Связанные токены строковых литералов конкатенированы.
- Символы белого пространства, разделяющие токены, уже не являются значимыми. Каждый токен предварительной обработки преобразуется в токен. (2.7). в результате токены синтаксически и семантически анализируются и переведено как единица перевода. [СНиП]
- Переводные единицы перевода и единицы экземпляра объединяются следующим образом: [SNIP]
- Все ссылки на внешние сущности разрешены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на объекты, не определенные в текущий перевод. Весь такой переводчик выводится в образ программы, который содержит информацию, необходимую для выполнения в ее среда выполнения. (акцент мой)
[footnote] Реализации должны вести себя так, как если бы эти отдельные фазы происходили, хотя на практике разные фазы могли складываться вместе.
Указанные ошибки возникают на этом последнем этапе компиляции, чаще всего называемом связыванием. Это в основном означает, что вы собрали кучу файлов реализации в объектные файлы или библиотеки, и теперь вы хотите заставить их работать вместе.
Скажите, что вы определили символ 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 для GCC.
Код:
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;
}
создаст следующие ошибки с помощью GCC:
/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status
и аналогичные ошибки с 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
Общие причины включают:
- Невыполнение ссылок на соответствующие библиотеки/объектные файлы или компиляцию файлов реализации
- Объявлено и undefined переменная или функция.
- Общие проблемы с членами класса
- Невозможно реализовать варианты шаблонов.
- Символы были определены в программе на языке C и используются в коде С++.
- Неправильно импортировать/экспортировать методы/классы по модулям /dll. (Спецификация MSVS)
- Зависимость круговой библиотеки
- undefined ссылка на` WinMain @16 ‘
- Заказ взаимозависимой библиотеки
- Несколько исходных файлов с таким же именем
- Мистификация или отсутствие расширения .lib при использовании
#pragma
(Microsoft Visual Studio) - Проблемы с друзьями-шаблонами
- Непоследовательные
UNICODE
определения
Ответ 2
Члены класса:
Деструктор pure virtual
нуждается в реализации.
Объявление деструктора по-прежнему требует определения его (в отличие от обычной функции):
struct X
{
virtual ~X() = 0;
};
struct Y : X
{
~Y() {}
};
int main()
{
Y y;
}
//X::~X(){} //uncomment this line for successful definition
Это происходит потому, что деструкторы базового класса вызывается, когда объект уничтожается неявно, поэтому требуется определение.
virtual
методы должны быть реализованы или определены как чистые.
Это похоже на методы не virtual
без определения, с добавлением аргументов, что
чистая декларация генерирует фиктивный vtable, и вы можете получить ошибку компоновщика без использования функции:
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;
};
Члены класса <<20 >
Некоторые члены должны быть определены, даже если они не используются явно:
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-использование этого элемента по-прежнему потребует определения области пространства имен, как описано выше. С++ 11 позволяет инициализировать внутри класса для всех членов static const
.
Ответ 3
Отказ от ссылки на соответствующие библиотеки/объектные файлы или компиляцию файлов реализации
Обычно каждая единица перевода генерирует объектный файл, содержащий определения символов, определенных в этой единице перевода.
Чтобы использовать эти символы, вы должны привязать их к этим объектным файлам.
В разделе gcc вы должны указать все объектные файлы, которые должны быть связаны вместе в командной строке, или скомпилировать файлы реализации вместе.
g++ -o test objectFile1.o objectFile2.o -lLibraryName
libraryName
— это просто голое имя библиотеки, без добавления к платформе. Так, например, в файлах библиотеки Linux обычно называют libfoo.so
, но вы должны писать только -lfoo
. В Windows этот же файл можно назвать foo.lib
, но вы должны использовать тот же аргумент. Возможно, вам придется добавить каталог, в котором эти файлы можно найти, используя -L‹directory›
. Обязательно не записывайте пробел после -l
или -l
.
Для XCode: добавьте пути поиска заголовка пользователя → добавьте путь поиска библиотеки → перетащите фактическую ссылку на библиотеку в папку проекта.
В разделе MSVS файлы, добавленные в проект, автоматически связывают их объектные файлы и генерируется файл lib
(обычно используется). Чтобы использовать символы в отдельном проекте, вы должны
необходимо указать файлы lib
в настройках проекта. Это делается в разделе Linker свойств проекта, в Input -> Additional Dependencies
. (путь к файлу lib
должен быть
добавлено в Linker -> General -> Additional Library Directories
). При использовании сторонней библиотеки, которая поставляется с файлом lib
, отказ в этом обычно приводит к ошибке.
Также может случиться так, что вы забудете добавить файл в компиляцию, и в этом случае объектный файл не будет сгенерирован. В gcc вы должны добавить файлы в командную строку. В MSVS добавление файла в проект заставит его скомпилировать его автоматически (хотя файлы могут, вручную, быть отдельно исключены из сборки).
В программировании Windows контрольный знак, что вы не связывали нужную библиотеку, состоит в том, что имя неразрешенного символа начинается с __imp_
. Посмотрите имя функции в документации, и она должна сказать, какую библиотеку вам нужно использовать. Например, MSDN помещает информацию в поле внизу каждой функции в разделе «Библиотека».
Ответ 4
Объявлено, но не определено переменная или функция.
Типичное объявление переменной
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)
Другие примеры несоответствий включают
- Функция/переменная, объявленная в одном пространстве имен, определенном в другом.
- Функция/переменная, объявленная как член класса, определяемая как глобальная (или наоборот).
- Тип возвращаемого значения функции, номер и типы параметров и соглашение о вызове не все точно согласны.
Сообщение об ошибке из компилятора часто дает вам полное объявление переменной или функции, которая была объявлена, но не определена. Сравните его с определением, которое вы указали. Убедитесь, что каждая деталь соответствует.
Ответ 5
Порядок, в котором указаны взаимозависимые связанные библиотеки, неверен.
Порядок, в котором связаны библиотеки, имеет значение, если библиотеки зависят друг от друга. В общем случае, если библиотека 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
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
Итак, повторим еще раз, порядок ДОЛЖЕН!
Ответ 6
что такое undefined ссылка/неразрешенный внешний символ
Я попытаюсь объяснить, что такое «undefined ссылка/неразрешенный внешний символ».
note: я использую 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%dn", global_var_name, local_var_name);
printf("%dn", 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%dn", 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
to
.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
ok, см. вывод на экран (символы)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
теперь local_var_name имеет Bind GLOBAL (был локальным)
ссылка
$ g++ src1.o src2.o -o prog
и запустите его
$ ./prog
123456789
ok, мы его взломаем:)
Итак, в результате — «undefined ссылка/неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.
Ответ 7
Символы были определены в программе на языке C и используются в коде С++.
Функция (или переменная) void foo()
была определена в программе на C, и вы пытаетесь использовать ее в программе на С++:
void foo();
int main()
{
foo();
}
Компилятор С++ ожидает, что имена будут искажены, поэтому вы должны объявить эту функцию как:
extern "C" void foo();
int main()
{
foo();
}
Эквивалентно, вместо определения в программе на C, функция (или переменная) void foo()
была определена в С++, но с C-связью:
extern "C" void foo();
и вы пытаетесь использовать его в С++-программе с С++-связью.
Если вся библиотека включена в заголовочный файл (и была скомпилирована как код C); включение должно быть следующим:
extern "C" {
#include "cheader.h"
}
Ответ 8
Если все остальное не удается, перекомпилируйте.
Недавно я смог избавиться от нерешенной внешней ошибки в Visual Studio 2012, просто перекомпилировав нарушивший файл. Когда я перестроил, ошибка исчезла.
Это обычно происходит, когда две (или более) библиотеки имеют циклическую зависимость. Библиотека A пытается использовать символы в B.lib и библиотеке B пытается использовать символы из A.lib. Ничего не существует для начала. Когда вы пытаетесь скомпилировать A, шаг ссылки завершится неудачно, потому что он не может найти B.lib. A.lib будет сгенерирован, но не будет dll. Затем вы компилируете B, который будет успешным и сгенерирует B.lib. Повторная компиляция A теперь будет работать, потому что теперь найден B.lib.
Ответ 9
Неправильно импортировать/экспортировать методы/классы по модулям /dll (специфичный для компилятора).
MSVS требует указать, какие символы экспортировать и импортировать с помощью __declspec(dllexport)
и __declspec(dllimport)
.
Эта двойная функциональность обычно получается с помощью макроса:
#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif
Макрос THIS_MODULE
будет определен только в модуле, который экспортирует функцию. Таким образом, декларация:
DLLIMPEXP void foo();
расширяется до
__declspec(dllexport) void foo();
и сообщает компилятору экспортировать функцию, так как текущий модуль содержит свое определение. Если включить объявление в другом модуле, он будет расширяться до
__declspec(dllimport) void foo();
и сообщает компилятору, что определение находится в одной из библиотек, к которым вы привязались (см. также 1)).
Вы можете подобрать классы импорта/экспорта:
class DLLIMPEXP X
{
};
Ответ 10
Это одно из самых запутанных сообщений об ошибках, которые каждый программист VС++ видел снова и снова. Давайте сначала сделаем чёткость.
а. Что такое символ?
Короче говоря, символ — это имя. Это может быть имя переменной, имя функции, имя класса, имя typedef или что-либо кроме тех имен и знаков, которые принадлежат языку С++. Это пользователь, определенный или введенный библиотекой зависимостей (другой пользовательский).
В. Что такое внешний?
В VС++ каждый исходный файл (.cpp,.c и т.д.) Рассматривается как единица перевода, компилятор компилирует по одному модулю за раз и генерирует один объектный файл (.obj) для текущей единицы перевода. (Обратите внимание, что каждый заголовочный файл, включенный в этот исходный файл, будет предварительно обработан и будет рассматриваться как часть этой единицы перевода). Все внутри единицы перевода считается внутренним, все остальное считается внешним. В С++ вы можете ссылаться на внешний символ, используя ключевые слова, такие как extern
, __declspec (dllimport)
и т.д.
С. Что такое «разрешение»?
Resolve — термин времени связывания. Во время компоновки линкер пытается найти внешнее определение для каждого символа в объектных файлах, которые не могут найти свое определение внутри. Объем этого процесса поиска включает:
- Все объектные файлы, сгенерированные во время компиляции
- Все библиотеки (.lib), явно или неявно
указанные в качестве дополнительных зависимостей этого строительного приложения.
Этот процесс поиска называется разрешением.
Д. Наконец, почему Unresolved External Symbol?
Если компоновщик не может найти внешнее определение для символа, который не имеет внутреннего определения, он сообщает об ошибке неразрешенного внешнего символа.
E. Возможные причины LNK2019: ошибка неразрешенного внешнего символа.
Мы уже знаем, что эта ошибка связана с тем, что компоновщик не смог найти определение внешних символов, возможные причины могут быть отсортированы как:
- Определение существует
Например, если у нас есть функция foo, определенная в a.cpp:
int foo()
{
return 0;
}
В b.cpp мы хотим вызвать функцию foo, поэтому добавим
void foo();
объявить функцию foo() и вызвать ее в другом теле функции, скажем bar()
:
void bar()
{
foo();
}
Теперь, когда вы создадите этот код, вы получите ошибку LNK2019, жалуясь, что foo — неразрешенный символ. В этом случае мы знаем, что foo() имеет свое определение в a.cpp, но отличается от того, которое мы вызываем (другое возвращаемое значение). Это так, что определение существует.
- Определение не существует
Если мы хотим вызвать некоторые функции в библиотеке, но библиотека импорта не добавляется в дополнительный список зависимостей (установленный из: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency
) вашего проекта. Теперь компоновщик сообщит LNK2019, поскольку определение не существует в текущей области поиска.
Ответ 11
Реализации шаблонов не отображаются.
Неспециализированные шаблоны должны иметь свои определения для всех единиц перевода, которые их используют. Это означает, что вы не можете отделить определение шаблона
к файлу реализации. Если вы должны отделить реализацию, обычным обходным решением является наличие файла impl
, который вы укажете в конце заголовка, который
объявляет шаблон. Общей ситуацией является:
template<class T>
struct X
{
void foo();
};
int main()
{
X<int> x;
x.foo();
}
//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}
Чтобы исправить это, вы должны переместить определение X::foo
в файл заголовка или какое-либо место, видимое для единицы перевода, которая его использует.
Специализированные шаблоны могут быть реализованы в файле реализации, и реализация не должна быть видимой, но специализация должна быть объявлена ранее.
Для дальнейшего объяснения и другого возможного решения (явное создание экземпляра) см. этот вопрос и ответ.
Ответ 12
неопределенная ссылка на [email protected]
или аналогичную «необычную» main()
точку main()
точки входа (особенно для visual-studio).
Возможно, вы пропустили выбор правильного типа проекта с помощью вашей реальной среды IDE. IDE может захотеть связать, например, проекты приложений Windows с такой функцией точки входа (как указано в недостающей ссылке выше), вместо обычно используемого int main(int argc, char** argv);
подпись.
Если ваша среда IDE поддерживает проекты Plain Console, вы можете выбрать этот тип проекта, а не проект приложения Windows.
Здесь case1 и case2 обрабатываются более подробно из реальной проблемы.
Ответ 13
Также, если вы используете сторонние библиотеки, убедитесь, что у вас есть правильные 32/64 битные файлы.
Ответ 14
Microsoft предлагает #pragma
ссылаться на нужную библиотеку во время ссылки;
#pragma comment(lib, "libname.lib")
В дополнение к пути библиотеки, включая каталог библиотеки, это должно быть полное имя библиотеки.
Ответ 15
Пакет Visual Studio NuGet необходимо обновить для новой версии набора инструментов
У меня просто возникла проблема с подключением libpng с Visual Studio 2013. Проблема в том, что в файле пакета были только библиотеки для Visual Studio 2010 и 2012.
Правильное решение — надеяться, что разработчик выпустит обновленный пакет, а затем обновит его, но это сработало для меня, взломав дополнительную настройку для VS2013, указав на файлы библиотеки VS2012.
Я отредактировал пакет (в папке packages
внутри каталога решений), найдя packagenamebuildnativepackagename.targets
и внутри этого файла, скопировав все разделы v110
. Я изменил v110
на v120
в только поля условия, очень осторожно оставляя пути имени файла как v110
. Это просто позволило Visual Studio 2013 связать с библиотеками в 2012 году, и в этом случае он сработал.
Ответ 16
Предположим, что у вас есть большой проект, написанный на С++, который содержит тысячу файлов .cpp и тысячу файлов .h. И пусть говорит, что проект также зависит от десяти статических библиотек. Пусть говорит, что мы в Windows, и мы строим наш проект в Visual Studio 20xx. Когда вы нажимаете Ctrl + F7 Visual Studio, чтобы начать компиляцию всего решения (предположим, что в решении есть только один проект)
Что означает компиляция?
- Поиск в Visual Studio в файл .vcxproj и начните компилировать каждый файл с расширением .cpp. Порядок компиляции undefined. Поэтому вы не должны предполагать, что файл main.cpp скомпилирован первым.
- Если файлы .cpp зависят от дополнительных файлов .h, чтобы найти символы
которые могут или не могут быть определены в файле .cpp - Если существует один .cpp файл, в котором компилятор не смог найти один символ, ошибка времени компилятора вызывает сообщение Symbol x не может быть найдено
- Для каждого файла с расширением .cpp создается объектный файл .o, а также Visual Studio записывает вывод в файл с именем ProjectName.Cpp.Clean.txt, который содержит все объектные файлы, которые должны обрабатываться компоновщиком.
Второй этап компиляции выполняется Linker.Linker должен объединить весь объектный файл и построить окончательно вывод (который может быть исполняемым или библиотекой)
Шаги по связыванию проекта
- Разберите все объектные файлы и найдите определение, которое было объявлено только в заголовках (например: Код одного метода класса, как указано в предыдущих ответах, или событие инициализация статической переменной, которая является членом внутри класса )
- Если один из символов не найден в объектных файлах, он также выполняется в дополнительных библиотеках. Для добавления новой библиотеки в проект Свойства конфигурации → Каталоги VС++ → Библиотечные каталоги, и здесь вы указали дополнительную папку для поиска библиотек и Свойства конфигурации → Linker → Вход для указав имя библиотеки.
-Если Linker не смог найти символ, который вы пишете в одном .cpp, он поднимает ошибку времени компоновщика, которая может звучать как
error LNK2001: unresolved external symbol "void __cdecl foo(void)" ([email protected]@YAXXZ)
Наблюдение
- Когда Linker найдет один символ, он не ищет в других библиотеках для него
- Порядок связывания библиотек имеет значение.
- Если Linker находит внешний символ в одной статической библиотеке, он включает в себя символ на выходе проекта. Однако если библиотека является общей (динамической), он не включает в себя код (символы) на выходе, а Run- Могут произойти сбой времени.
Как решить эту ошибку
Ошибка времени компилятора:
- Убедитесь, что ваш синтаксический проект синтаксиса написан правильно.
Ошибка времени компоновщика
- Определите весь свой символ, который вы объявляете в своих файлах заголовков.
- Используйте
#pragma once
, чтобы компилятор не включал один заголовок, если он уже был включен в текущий .cpp, которые скомпилированы - Убедитесь, что ваша внешняя библиотека не содержит символов, которые могут вступать в конфликт с другими символами, определенными в ваших файлах заголовков.
- При использовании шаблона убедитесь, что вы включили определение каждой функции шаблона в файл заголовка, чтобы компилятор мог создать соответствующий код для любых экземпляров.
Ответ 17
Ошибка в компиляторе /IDE
У меня недавно была эта проблема, и оказалось, что это была ошибка
Ответ 18
Используйте компоновщик, чтобы помочь диагностировать ошибку
Большинство современных линкеров включают подробный вариант, который печатается в различной степени;
- Вызов ссылки (командной строки),
- Данные о том, какие библиотеки включены в этап ссылки,
- Расположение библиотек,
- Используемые пути поиска.
Для gcc и clang; вы обычно добавляете -v -Wl,--verbose
или -v -Wl,-v
в командную строку. Более подробную информацию можно найти здесь:
- Linux ld man page.
- LLVM страница компоновщика.
- «Введение в GCC» глава 9.
Для MSVC в командную строку линии добавляется /VERBOSE
(в частности, /VERBOSE:LIB
).
- Страница MSDN на
/VERBOSE
вариант компоновщика.
Ответ 19
Связанный .lib файл связан с .dll
У меня была такая же проблема. Скажем, у меня есть проекты MyProject и TestProject. Я эффективно связал файл lib для MyProject с TestProject. Однако этот файл lib был создан, так как была построена DLL для MyProject. Кроме того, я не содержал исходный код для всех методов в MyProject, а только доступ к точкам входа в DLL.
Чтобы решить эту проблему, я создал MyProject как LIB и связал TestProject с этим .lib файлом (скопируйте вложенный файл .lib в папку TestProject). Затем я смогу снова создать MyProject как DLL. Он компилируется, поскольку lib, с которым связан TestProject, содержит код для всех методов в классах MyProject.
Ответ 20
Поскольку люди, похоже, обращаются к этому вопросу, когда речь идет о ошибках компоновщика, я собираюсь добавить это здесь.
Одна из возможных причин ошибок компоновщика с GCC 5.2.0 заключается в том, что по умолчанию теперь выбрана новая библиотека ABI libstdС++.
Если вы получаете ошибки компоновщика о undefined ссылках на символы, которые включают типы в пространстве имен std:: __ cxx11 или теге [abi: cxx11], то это, вероятно, указывает на то, что вы пытаетесь связать вместе файлы объектов, которые были скомпилированы с помощью различные значения для макроса _GLIBCXX_USE_CXX11_ABI. Это обычно происходит при подключении к сторонней библиотеке, которая была скомпилирована с более старой версией GCC. Если сторонняя библиотека не может быть перестроена с новым ABI, вам нужно будет перекомпилировать свой код со старым ABI.
Итак, если вы вдруг получите ошибки компоновщика при переключении на GCC после 5.1.0, это будет проверкой.
Ответ 21
Обертка вокруг GNU ld, которая не поддерживает сценарии компоновщика
Некоторые .so файлы на самом деле скрипты компоновщика GNU ld, например. libtbb.so — это текстовый файл ASCII с этим содержимым:
INPUT (libtbb.so.2)
Некоторые более сложные сборки могут не поддерживать это. Например, если вы включаете -v в параметры компилятора, вы можете видеть, что mainwin gcc wrapper mwdip сбрасывает компоновщики script командных файлов в расширенном списке результатов библиотек для ссылки. Простая работа заключается в замене файла командной строки компоновщика script копией файла вместо (или символической ссылки), например
cp libtbb.so.2 libtbb.so
Или вы можете заменить аргумент -l полным путем .so, например. вместо -ltbb
do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
Ответ 22
Подружиться с шаблонами…
С учетом фрагмента кода типа шаблона с оператором (или функцией) друга;
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
operator<<
объявляется как функция без шаблона. Для каждого типа T
, используемого с Foo
, должен быть не templated operator<<
. Например, если объявлен тип Foo<int>
, тогда должна существовать реализация оператора следующим образом:
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
Так как он не реализован, компоновщик не находит его и приводит к ошибке.
Чтобы исправить это, вы можете объявить оператор шаблона перед типом Foo
, а затем объявить его другом, соответствующим экземпляром. Синтаксис немного неудобен, но выглядит следующим образом:
// 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
}
Приведенный выше код ограничивает дружбу оператора соответствующим экземпляром Foo
, т.е. экземпляр operator<< <int>
ограничен доступом к частным членам экземпляра Foo<int>
.
Альтернативы включают:
-
Позволяя дружбе распространяться на все экземпляры шаблонов, следующим образом:
template <typename T> class Foo { template <typename T1> friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a); // ... };
-
Или реализация для
operator<<
может быть выполнена внутри внутри определения класса;template <typename T> class Foo { friend std::ostream& operator<<(std::ostream& os, const Foo& a) { /*...*/ } // ... };
Обратите внимание, что когда объявление оператора (или функции) появляется только в классе, имя не доступно для «нормального» поиска, только для зависимого от аргумента поиска, от cppreference;
Имя, объявленное ранее в объявлении друга в классе или классе, шаблон X становится членом самого внутреннего охватывающего пространства имен X, но недоступен для поиска (кроме зависимого от аргумента поиска, который учитывает X), если соответствующее объявление на область пространства имен предоставляется…
Далее читаются друзья шаблонов в cppreference и Часто задаваемые вопросы по С++.
Список кодов, показывающий методы выше.
В качестве побочного примечания к образцу неудачного кода; g++ предупреждает об этом следующим образом
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)
Ответ 23
Ваша ссылка потребляет библиотеки перед файлами объектов, которые относятся к ним
- Вы пытаетесь скомпилировать и связать свою программу с инструментальной цепочкой GCC.
- В вашей ссылке указаны все необходимые библиотеки и пути поиска библиотек
- Если
libfoo
зависит отlibbar
, тогда ваша ссылка правильно помещаетlibfoo
доlibbar
. - Ваша связь с ошибками
undefined reference to
не работает. - Но все undefined somethings объявлены в файлах заголовков, которые у вас есть
#include
d и фактически определены в библиотеках, которые вы связываете.
Примеры в C. Они также могут быть С++
Минимальный пример, связанный с созданной вами статической библиотекой
my_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
my_lib.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
eg1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
Вы создаете свою статическую библиотеку:
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
Вы компилируете свою программу:
$ gcc -I. -c -o eg1.o eg1.c
Вы пытаетесь связать его с libmy_lib.a
и сбой:
$ 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
Тот же результат, если вы выполните компиляцию и ссылку за один шаг, например:
$ 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
Минимальный пример, включающий общую системную библиотеку, библиотеку сжатия libz
eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%sn",zlibVersion());
return 0;
}
Скомпилируйте свою программу:
$ gcc -c -o eg2.o eg2.c
Попробуйте связать свою программу с libz
и выполните сбой:
$ 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
То же самое, если вы компилируете и связываете за один раз:
$ 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
И вариант на примере 2 с участием pkg-config
:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
Что вы делаете неправильно?
В последовательности объектных файлов и библиотек, которые вы хотите связать, чтобы сделать свой
вы размещаете библиотеки перед объектными файлами, которые ссылаются на
их. Вы должны поместить библиотеки после того, как файлы объектов, которые ссылаются
к ним.
Правильно ссылайтесь на пример 1:
$ gcc -o eg1 eg1.o -L. -lmy_lib
Success
$ ./eg1
Hello World
Правильно ссылайтесь на пример 2:
$ gcc -o eg2 eg2.o -lz
Success
$ ./eg2
1.2.8
Правильно ссылайтесь на пример 2 pkg-config
:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
Объяснение
Чтение необязательно здесь.
По умолчанию команда привязки, сгенерированная GCC, в вашем дистрибутиве,
расходует файлы в ссылке слева направо
последовательность командной строки. Когда он обнаруживает, что файл ссылается на что-то
и не содержит определения для него, будет искать определение
в файлах дальше вправо. Если в конечном итоге оно найдет определение,
ссылка разрешена. Если какие-либо ссылки остаются неразрешенными в конце,
связь не выполняется: компоновщик не ищет назад.
Сначала пример 1, со статической библиотекой my_lib.a
Статическая библиотека — это индексированный архив объектных файлов. Когда компоновщик
находит -lmy_lib
в последовательности связей и вычисляет, что это относится
к статической библиотеке ./libmy_lib.a
, она хочет знать, ваша программа
требуется любой из объектных файлов в libmy_lib.a
.
В libmy_lib.a
есть только объектный файл, а именно my_lib.o
, и только одна вещь определена
в my_lib.o
, а именно: функция hw
.
Компонент решает, что ваша программа нуждается в my_lib.o
тогда и только тогда, когда она уже знает, что
ваша программа относится к hw
, в одном или нескольких объектных файлах, которые уже есть
добавлен в программу, и что ни один из файлов объектов, которые он уже добавил
содержит определение для hw
.
Если это так, то компоновщик извлечет из библиотеки my_lib.o
копию
добавьте его в свою программу. Затем ваша программа содержит определение для hw
, поэтому
его ссылки на hw
разрешены.
Когда вы пытаетесь связать программу следующим образом:
$ gcc -o eg1 -L. -lmy_lib eg1.o
компоновщик не добавил eg1.o
к программе, когда видит
-lmy_lib
. Потому что в этот момент он не видел eg1.o
.
Ваша программа еще не ссылается на hw
: it
пока не делает никаких ссылок, потому что все ссылки, которые он делает
находятся в eg1.o
.
Таким образом, компоновщик не добавляет my_lib.o
в программу и не имеет дальнейшего
используйте для libmy_lib.a
.
Затем он находит eg1.o
и добавляет его в программу. Объектный файл в
последовательность ссылок всегда добавляется в программу. Теперь программа делает
ссылка на hw
и не содержит определения hw
; но
в последовательности связей нет ничего, что могло бы обеспечить отсутствие
определение. Ссылка на hw
заканчивается неразрешенной, и связь не выполняется.
Во-вторых, пример 2, с общей библиотекой libz
Общая библиотека не является архивом объектных файлов или чего-либо подобного. Это
гораздо больше похожа на программу, у которой нет функции main
и
вместо этого предоставляет множество других символов, которые он определяет, так что другие
программы могут использовать их во время выполнения.
Многие дистрибутивы Linux сегодня настраивают свою привязку GCC так, чтобы ее языковые драйверы (gcc
, g++
, gfortran
и т.д.)
проинструктировать системный компоновщик (ld
) для связывания разделяемых библиотек по мере необходимости.
У вас есть один из этих дистрибутивов.
Это означает, что, когда компоновщик находит -lz
в последовательности связей и выясняет, что это относится
к общей библиотеке (скажем) /usr/lib/x86_64-linux-gnu/libz.so
, она хочет знать, есть ли какие-либо ссылки, которые она добавила к вашей программе, которые еще не определены, имеют определения, которые экспортируются с помощью libz
Если это так, то компоновщик не будет копировать любые куски из libz
и
добавьте их в свою программу; вместо этого он просто обработает код вашей программы
так что: —
-
Во время выполнения загрузчик системной программы загрузит копию
libz
в
тот же процесс, что и ваша программа, когда он загружает копию вашей программы, для ее запуска. -
Во время выполнения, когда ваша программа ссылается на то, что определено в
libz
, эта ссылка использует определение, экспортированное копиейlibz
в
тот же процесс.
Ваша программа хочет ссылаться только на одну вещь, которая имеет определение, экспортированное libz
,
а именно функцию zlibVersion
, которая упоминается только один раз, в eg2.c
.
Если компоновщик добавляет эту ссылку к вашей программе, а затем находит определение
экспортируется libz
, ссылка разрешена
Но когда вы пытаетесь связать программу, например:
gcc -o eg2 -lz eg2.o
порядок событий неверен точно так же, как в примере 1.
В момент, когда компоновщик находит -lz
, ссылок на что-либо нет
в программе: все они находятся в eg2.o
, который еще не был замечен. Итак
компоновщик решает, что он не нужен для libz
. Когда он достигнет eg2.o
, добавляет его в программу,
и затем имеет undefined ссылку на zlibVersion
, последовательность связей закончена;
эта ссылка не решена, и связь не удалась.
Наконец, вариант pkg-config
примера 2 имеет теперь очевидное объяснение.
После расширения оболочки:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
становится:
gcc -o eg2 -lz eg2.o
который снова является примером 2.
Я могу воспроизвести проблему в примере 1, но не в примере 2
Связь:
gcc -o eg2 -lz eg2.o
отлично подходит для вас!
(Или: эта связь работала хорошо для вас, скажем, Fedora 23, но не работает на Ubuntu 16.04)
Это потому, что дистрибутив, на котором работает ссылка, является одним из тех,
не настраивает свою привязку GCC для связывания разделяемых библиотек по мере необходимости.
В тот же день для UNIX-подобных систем было нормально связывать статические и общие
библиотеки по разным правилам. Статические библиотеки в последовательности связей были связаны
по мере необходимости, объясняется в примере 1, но разделяемые библиотеки были связаны безоговорочно.
Такое поведение экономично в режиме ссылки, потому что линкер не должен обдумывать
нужна ли библиотека совместно используемой библиотеке: если она является общей библиотекой,
ссылку. Большинство библиотек в большинстве связей являются общими библиотеками. Но есть и недостатки: —
-
Это неэкономично во время выполнения, поскольку это может привести к тому, что общие библиотеки будут
загружаются вместе с программой, даже если они не нужны. -
Различные правила привязки для статических и разделяемых библиотек могут вводить в заблуждение
к незнакомым программистам, которые могут не знать, есть ли-lfoo
в их связи
будет разрешено до/some/where/libfoo.a
или/some/where/libfoo.so
,
и может не понимать разницу между общими и статическими библиотеками
в любом случае.
Этот компромисс привел сегодня к раскольнической ситуации. Некоторые дистрибутивы
изменили свои правила привязки GCC для разделяемых библиотек, чтобы, по мере необходимости,
принцип применяется ко всем библиотекам. Некоторые дистрибутивы застряли со старым
путь.
Почему я все еще получаю эту проблему, даже если я компилирую-ссылку в то же время?
Если я просто сделаю:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
Конечно, gcc должен сначала скомпилировать eg1.c
, а затем связать полученный результат
объектный файл с libmy_lib.a
. Итак, как он не может знать этот объектный файл
если он выполняет привязку?
Поскольку компиляция и привязка к одной команде не меняют
порядок последовательности связей.
Когда вы запустите команду выше, gcc
выяснит, что вы хотите компилировать +
связь. Таким образом, за кулисами, он генерирует команду компиляции и запускает
он генерирует команду привязки и запускает ее, как если бы вы запускали
две команды:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
Таким образом, связь не работает так же, как если бы вы выполняли эти две команды.
только разница, которую вы замечаете при ошибке, заключается в том, что gcc сгенерировал
временный файл объекта в файле компиляции + ссылка, потому что вы не говорите об этом
использовать eg1.o
. Мы видим:
/tmp/ccQk1tvs.o: In function `main'
вместо:
eg1.o: In function `main':
См. также
Неверный порядок, в котором указаны взаимозависимые связанные библиотеки
Включение взаимозависимых библиотек в неправильном порядке — это всего лишь один способ
в котором вы можете получить файлы, требующие определения
позже в ссылке, чем файлы, предоставляющие определения. Помещение библиотек до
объектные файлы, которые относятся к ним, являются еще одним способом сделать ту же ошибку.
Ответ 24
Когда ваши пути включения различаются
Ошибки компоновщика могут произойти, если заголовочный файл и связанная с ним общая библиотека (файл .lib) не синхронизируются. Позволь мне объяснить.
Как работают линкеры? Линкер соответствует объявлению функции (объявленному в заголовке) с его определением (в общей библиотеке) путем сравнения их подписи. Вы можете получить ошибку компоновщика, если компоновщик не найдет определение функции, которое идеально подходит.
Возможно ли получить ошибку компоновщика, даже если объявление и определение, похоже, совпадают? Да! Они могут выглядеть одинаково в исходном коде, но это действительно зависит от того, что видит компилятор. По существу, вы можете столкнуться с такой ситуацией:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
Обратите внимание, что, хотя обе декларации функций выглядят одинаково в исходном коде, но они действительно различаются в зависимости от компилятора.
Вы можете спросить, как это получается в такой ситуации? Включить пути, конечно! Если при компиляции разделяемой библиотеки путь include ведет к header1.h
, и вы в конечном итоге используете header2.h
в своей собственной программе, вы оставите царапины в своем заголовке, задаваясь вопросом, что произошло (каламбур).
Пример того, как это может произойти в реальном мире, объясняется ниже.
Дальнейшая разработка с примером
У меня есть два проекта: graphics.lib
и main.exe
. Оба проекта зависят от common_math.h
. Предположим, что библиотека экспортирует следующую функцию:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
И затем вы идете вперед и включаете библиотеку в свой собственный проект.
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
Boom! Вы получаете ошибку компоновщика, и вы не знаете, почему это не удается. Причина в том, что в общей библиотеке используются разные версии одного и того же include common_math.h
(я сделал это очевидным здесь в этом примере, включив другой путь, но это может быть не всегда так очевидно. Возможно, путь include отличается в настройки компилятора).
Обратите внимание, что в этом примере компоновщик сказал бы вам, что не смог найти draw()
, когда на самом деле вы знаете, что он явно экспортируется библиотекой. Вы могли часами царапать себе голову, думая, что пошло не так. Дело в том, что компоновщик видит другую подпись, потому что типы параметров немного отличаются. В этом примере, vec3
является другим типом в обоих проектах в отношении компилятора. Это может произойти из-за того, что они состоят из двух немного разных файлов include (возможно, включенные файлы поступают из двух разных версий библиотеки).
Отладка компоновщика
DUMPBIN — ваш друг, если вы используете Visual Studio. Я уверен, что другие компиляторы имеют другие подобные инструменты.
Процесс выполняется следующим образом:
- Обратите внимание на странное искаженное имя, указанное в ошибке компоновщика. (например, draw @graphics @XYZ).
- Выгрузить экспортированные символы из библиотеки в текстовый файл.
- Найдите экспортированный символ, представляющий интерес, и обратите внимание, что измененное имя отличается.
- Обратите внимание на то, почему искаженные имена оказались разными. Вы могли бы видеть, что типы параметров различны, хотя они выглядят одинаково в исходном коде.
- Причина, почему они разные. В приведенном выше примере они различаются из-за разных файлов include.
[1] По проекту я имею в виду набор исходных файлов, которые связаны друг с другом для создания либо библиотеки, либо исполняемого файла.
РЕДАКТИРОВАТЬ 1: переписать первый раздел, который будет легче понять. Пожалуйста, прокомментируйте ниже, чтобы сообщить мне, нужно ли что-то еще исправлять. Спасибо!
Ответ 25
Непоследовательные определения UNICODE
Конструкция Windows UNICODE построена с TCHAR
и т.д., которая определяется как wchar_t
и т.д. Когда не строится с UNICODE
, определенным как build с TCHAR
, определенным как char
и т.д. Эти UNICODE
и _UNICODE
определяет все «T
» типы строк; LPTSTR
, LPCTSTR
и их лося.
Создание одной библиотеки с UNICODE
, определенной и попытки связать ее в проекте, где UNICODE
не определен, приведет к ошибкам компоновщика, поскольку в определении TCHAR
будет несовпадение; char
против wchar_t
.
Обычно ошибка включает в себя функцию a с производным типом char
или wchar_t
, это может также включать std::basic_string<>
и т.д. При просмотре затронутой функции в коде часто будет ссылка на TCHAR
или std::basic_string<TCHAR>
и т.д. Это контрольный знак того, что этот код изначально предназначался как для UNICODE, так и для многобайтового символа ( или «узкий» ).
Чтобы исправить это, создайте все необходимые библиотеки и проекты с последовательным определением UNICODE
(и _UNICODE
).
-
Это можно сделать либо с помощью:
#define UNICODE #define _UNICODE
-
Или в настройках проекта;
Свойства проектa > Общие > Значения по умолчанию > Набор символов
-
Или в командной строке;
/DUNICODE /D_UNICODE
Альтернатива применима также, если UNICODE не предназначен для использования, убедитесь, что определения не установлены, и/или многосимвольная настройка используется в проектах и последовательно применяется.
Не забывайте быть совместимыми между сборками «Release» и «Debug».
Ответ 26
Очистить и восстановить
«Чистый» сборщик может удалить «мертвую древесину», которая может быть оставлена лежащей вокруг предыдущих сборок, неудачными сборками, неполными сборками и другими проблемами сборки, связанными с сборкой.
В общем случае IDE или сборка будет включать в себя некоторую форму «чистой» функции, но это может быть неправильно настроено (например, в ручном make файле) или может завершиться неудачей (например, промежуточные или результирующие двоичные файлы доступны только для чтения).
После завершения «очистки» убедитесь, что «чистый» преуспел, и все созданные промежуточные файлы (например, автоматический make файл) были успешно удалены.
Этот процесс можно рассматривать как окончательный вариант, но часто является хорошим первым шагом; особенно если недавно был добавлен код, связанный с ошибкой (локально или из исходного репозитория).
Ответ 27
Отсутствует «extern» в const
объявления/определения переменных (только С++)
Для людей, приезжающих с C, может быть неожиданностью, что в С++ глобальные переменные const
имеют внутреннюю (или статическую) связь. В C это не так, поскольку все глобальные переменные неявно extern
(т.е. Когда отсутствует ключевое слово static
).
Пример:
// 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
}
Правило
— использовать заголовочный файл и включать его в файлы file2.cpp и file1.cpp
extern const int test;
extern int test2;
В качестве альтернативы можно было объявить переменную const
в файле file1.cpp с явным extern
Ответ 28
Несмотря на то, что это довольно старые вопросы с несколькими принятыми ответами, я хотел бы поделиться тем, как устранить ошибку скрытого «неопределенная ссылка на».
Разные версии библиотек
Я использовал псевдоним для ссылки на std::filesystem::path
: файловая система находится в стандартной библиотеке начиная с С++ 17, но моя программа должна была также компилироваться в С++ 14, поэтому я решил использовать переменный псевдоним:
#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
Допустим, у меня есть три файла: main.cpp, file.h, file.cpp:
- file.h #include & lt;экспериментальный :: файловая система> и содержит код выше
- file.cpp, реализация file.h, #include «file.h»
- main.cpp #include & lt;файловая система> и «file.h«
Обратите внимание на различные библиотеки, используемые в main.cpp и file.h. Поскольку main.cpp # include’d «file.h» после & lt;файловой системы>, используемой там версией файловой системы была версия С++ 17., Я использовал для компиляции программы с помощью следующих команд:
$ g++ -g -std=c++17 -c main.cpp
→ компилирует main.cpp в main.o
$ g++ -g -std=c++17 -c file.cpp
→ компилирует file.cpp и file.h в file.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs
→ связывает main.o и file.o
Таким образом, любая функция, содержащаяся в file.o и используемая в main.o, для которой требовал path_t
, выдавала ошибки «неопределенная ссылка», поскольку main.o ссылался на std::filesystem::path
, но file.o — std::experimental::filesystem::path
.
Разрешение
Чтобы это исправить, мне просто нужно было изменить & lt; экспериментальный :: файловая система> в file.h на & lt; файловую систему>.
Ответ 29
При связывании с разделяемыми библиотеками убедитесь, что используемые символы не скрыты.
Поведение gcc по умолчанию состоит в том, что все символы видны. Однако, когда единицы перевода построены с опцией -fvisibility=hidden
, только функции/символы, отмеченные __attribute__ ((visibility ("default")))
являются внешними в результирующем общем объекте.
Вы можете проверить, являются ли символы, которые вы ищете, внешними, вызывая:
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
скрытые/локальные символы отображаются с помощью nm
с символом нижнего регистра, например t
вместо «T для кодового раздела:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
Вы также можете использовать nm
с опцией -C
для демонстрации имен (если использовался C++).
Подобно Windows-dll, можно было бы отметить публичные функции с помощью определения, например DLL_PUBLIC
определенного как:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
Что примерно соответствует версии Windows/MSVC:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
Более подробную информацию о видимости можно найти в wiki.
Когда блок перевода скомпилирован с -fvisibility=hidden
результирующие символы имеют по-прежнему внешнюю связь (показан с типом символа верхнего регистра на nm
) и могут быть использованы для внешней связи без проблем, если объектные файлы становятся частью статических библиотек. Связь становится локальной только тогда, когда объектные файлы связаны с общей библиотекой.
Чтобы найти, какие символы в объектном файле скрыты, выполните:
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
Ответ 30
Разные архитектуры
Вы можете увидеть сообщение как:
library machine type 'x64' conflicts with target machine type 'X86'
В этом случае это означает, что доступные символы для другой архитектуры, чем та, для которой вы компилируете.
В Visual Studio это происходит из-за неправильной «Платформы», и вам нужно либо выбрать подходящую, либо установить правильную версию библиотеки.
В Linux это может быть связано с неправильной папкой библиотеки (например, с использованием lib
вместо lib64
).
В MacOS есть возможность отправить обе архитектуры в одном файле. Может быть, что ссылка ожидает, что обе версии будут там, но только одна есть. Это также может быть проблема с неправильной папкой lib
/lib64
которой находится библиотека.
Ваша ссылка потребляет библиотеки перед файлами объектов, которые относятся к ним
- Вы пытаетесь скомпилировать и связать свою программу с инструментальной цепочкой GCC.
- В вашей ссылке указаны все необходимые библиотеки и пути поиска библиотек
- Если
libfoo
зависит отlibbar
, тогда ваша ссылка правильно помещаетlibfoo
доlibbar
. - Ваша связь с ошибками
undefined reference to
не работает. - Но все undefined somethings объявлены в файлах заголовков, которые у вас есть
#include
d и фактически определены в библиотеках, которые вы связываете.
Примеры в C. Они также могут быть С++
Минимальный пример, связанный с созданной вами статической библиотекой
my_lib.c
#include "my_lib.h"
#include <stdio.h>
void hw(void)
{
puts("Hello World");
}
my_lib.h
#ifndef MY_LIB_H
#define MT_LIB_H
extern void hw(void);
#endif
eg1.c
#include <my_lib.h>
int main()
{
hw();
return 0;
}
Вы создаете свою статическую библиотеку:
$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o
Вы компилируете свою программу:
$ gcc -I. -c -o eg1.o eg1.c
Вы пытаетесь связать его с libmy_lib.a
и сбой:
$ 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
Тот же результат, если вы выполните компиляцию и ссылку за один шаг, например:
$ 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
Минимальный пример, включающий общую системную библиотеку, библиотеку сжатия libz
eg2.c
#include <zlib.h>
#include <stdio.h>
int main()
{
printf("%sn",zlibVersion());
return 0;
}
Скомпилируйте свою программу:
$ gcc -c -o eg2.o eg2.c
Попробуйте связать свою программу с libz
и выполните сбой:
$ 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
То же самое, если вы компилируете и связываете за один раз:
$ 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
И вариант на примере 2 с участием pkg-config
:
$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
Что вы делаете неправильно?
В последовательности объектных файлов и библиотек, которые вы хотите связать, чтобы сделать свой
вы размещаете библиотеки перед объектными файлами, которые ссылаются на
их. Вы должны поместить библиотеки после того, как файлы объектов, которые ссылаются
к ним.
Правильно ссылайтесь на пример 1:
$ gcc -o eg1 eg1.o -L. -lmy_lib
Success
$ ./eg1
Hello World
Правильно ссылайтесь на пример 2:
$ gcc -o eg2 eg2.o -lz
Success
$ ./eg2
1.2.8
Правильно ссылайтесь на пример 2 pkg-config
:
$ gcc -o eg2 eg2.o $(pkg-config --libs zlib)
$ ./eg2
1.2.8
Объяснение
Чтение необязательно здесь.
По умолчанию команда привязки, сгенерированная GCC, в вашем дистрибутиве,
расходует файлы в ссылке слева направо
последовательность командной строки. Когда он обнаруживает, что файл ссылается на что-то
и не содержит определения для него, будет искать определение
в файлах дальше вправо. Если в конечном итоге оно найдет определение,
ссылка разрешена. Если какие-либо ссылки остаются неразрешенными в конце,
связь не выполняется: компоновщик не ищет назад.
Сначала пример 1, со статической библиотекой my_lib.a
Статическая библиотека — это индексированный архив объектных файлов. Когда компоновщик
находит -lmy_lib
в последовательности связей и вычисляет, что это относится
к статической библиотеке ./libmy_lib.a
, она хочет знать, ваша программа
требуется любой из объектных файлов в libmy_lib.a
.
В libmy_lib.a
есть только объектный файл, а именно my_lib.o
, и только одна вещь определена
в my_lib.o
, а именно: функция hw
.
Компонент решает, что ваша программа нуждается в my_lib.o
тогда и только тогда, когда она уже знает, что
ваша программа относится к hw
, в одном или нескольких объектных файлах, которые уже есть
добавлен в программу, и что ни один из файлов объектов, которые он уже добавил
содержит определение для hw
.
Если это так, то компоновщик извлечет из библиотеки my_lib.o
копию
добавьте его в свою программу. Затем ваша программа содержит определение для hw
, поэтому
его ссылки на hw
разрешены.
Когда вы пытаетесь связать программу следующим образом:
$ gcc -o eg1 -L. -lmy_lib eg1.o
компоновщик не добавил eg1.o
к программе, когда видит
-lmy_lib
. Потому что в этот момент он не видел eg1.o
.
Ваша программа еще не ссылается на hw
: it
пока не делает никаких ссылок, потому что все ссылки, которые он делает
находятся в eg1.o
.
Таким образом, компоновщик не добавляет my_lib.o
в программу и не имеет дальнейшего
используйте для libmy_lib.a
.
Затем он находит eg1.o
и добавляет его в программу. Объектный файл в
последовательность ссылок всегда добавляется в программу. Теперь программа делает
ссылка на hw
и не содержит определения hw
; но
в последовательности связей нет ничего, что могло бы обеспечить отсутствие
определение. Ссылка на hw
заканчивается неразрешенной, и связь не выполняется.
Во-вторых, пример 2, с общей библиотекой libz
Общая библиотека не является архивом объектных файлов или чего-либо подобного. Это
гораздо больше похожа на программу, у которой нет функции main
и
вместо этого предоставляет множество других символов, которые он определяет, так что другие
программы могут использовать их во время выполнения.
Многие дистрибутивы Linux сегодня настраивают свою привязку GCC так, чтобы ее языковые драйверы (gcc
, g++
, gfortran
и т.д.)
проинструктировать системный компоновщик (ld
) для связывания разделяемых библиотек по мере необходимости.
У вас есть один из этих дистрибутивов.
Это означает, что, когда компоновщик находит -lz
в последовательности связей и выясняет, что это относится
к общей библиотеке (скажем) /usr/lib/x86_64-linux-gnu/libz.so
, она хочет знать, есть ли какие-либо ссылки, которые она добавила к вашей программе, которые еще не определены, имеют определения, которые экспортируются с помощью libz
Если это так, то компоновщик не будет копировать любые куски из libz
и
добавьте их в свою программу; вместо этого он просто обработает код вашей программы
так что: —
-
Во время выполнения загрузчик системной программы загрузит копию
libz
в
тот же процесс, что и ваша программа, когда он загружает копию вашей программы, для ее запуска. -
Во время выполнения, когда ваша программа ссылается на то, что определено в
libz
, эта ссылка использует определение, экспортированное копиейlibz
в
тот же процесс.
Ваша программа хочет ссылаться только на одну вещь, которая имеет определение, экспортированное libz
,
а именно функцию zlibVersion
, которая упоминается только один раз, в eg2.c
.
Если компоновщик добавляет эту ссылку к вашей программе, а затем находит определение
экспортируется libz
, ссылка разрешена
Но когда вы пытаетесь связать программу, например:
gcc -o eg2 -lz eg2.o
порядок событий неверен точно так же, как в примере 1.
В момент, когда компоновщик находит -lz
, ссылок на что-либо нет
в программе: все они находятся в eg2.o
, который еще не был замечен. Итак
компоновщик решает, что он не нужен для libz
. Когда он достигнет eg2.o
, добавляет его в программу,
и затем имеет undefined ссылку на zlibVersion
, последовательность связей закончена;
эта ссылка не решена, и связь не удалась.
Наконец, вариант pkg-config
примера 2 имеет теперь очевидное объяснение.
После расширения оболочки:
gcc -o eg2 $(pkg-config --libs zlib) eg2.o
становится:
gcc -o eg2 -lz eg2.o
который снова является примером 2.
Я могу воспроизвести проблему в примере 1, но не в примере 2
Связь:
gcc -o eg2 -lz eg2.o
отлично подходит для вас!
(Или: эта связь работала хорошо для вас, скажем, Fedora 23, но не работает на Ubuntu 16.04)
Это потому, что дистрибутив, на котором работает ссылка, является одним из тех,
не настраивает свою привязку GCC для связывания разделяемых библиотек по мере необходимости.
В тот же день для UNIX-подобных систем было нормально связывать статические и общие
библиотеки по разным правилам. Статические библиотеки в последовательности связей были связаны
по мере необходимости, объясняется в примере 1, но разделяемые библиотеки были связаны безоговорочно.
Такое поведение экономично в режиме ссылки, потому что линкер не должен обдумывать
нужна ли библиотека совместно используемой библиотеке: если она является общей библиотекой,
ссылку. Большинство библиотек в большинстве связей являются общими библиотеками. Но есть и недостатки: —
-
Это неэкономично во время выполнения, поскольку это может привести к тому, что общие библиотеки будут
загружаются вместе с программой, даже если они не нужны. -
Различные правила привязки для статических и разделяемых библиотек могут вводить в заблуждение
к незнакомым программистам, которые могут не знать, есть ли-lfoo
в их связи
будет разрешено до/some/where/libfoo.a
или/some/where/libfoo.so
,
и может не понимать разницу между общими и статическими библиотеками
в любом случае.
Этот компромисс привел сегодня к раскольнической ситуации. Некоторые дистрибутивы
изменили свои правила привязки GCC для разделяемых библиотек, чтобы, по мере необходимости,
принцип применяется ко всем библиотекам. Некоторые дистрибутивы застряли со старым
путь.
Почему я все еще получаю эту проблему, даже если я компилирую-ссылку в то же время?
Если я просто сделаю:
$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
Конечно, gcc должен сначала скомпилировать eg1.c
, а затем связать полученный результат
объектный файл с libmy_lib.a
. Итак, как он не может знать этот объектный файл
если он выполняет привязку?
Поскольку компиляция и привязка к одной команде не меняют
порядок последовательности связей.
Когда вы запустите команду выше, gcc
выяснит, что вы хотите компилировать +
связь. Таким образом, за кулисами, он генерирует команду компиляции и запускает
он генерирует команду привязки и запускает ее, как если бы вы запускали
две команды:
$ gcc -I. -c -o eg1.o eg1.c
$ gcc -o eg1 -L. -lmy_lib eg1.o
Таким образом, связь не работает так же, как если бы вы выполняли эти две команды.
только разница, которую вы замечаете при ошибке, заключается в том, что gcc сгенерировал
временный файл объекта в файле компиляции + ссылка, потому что вы не говорите об этом
использовать eg1.o
. Мы видим:
/tmp/ccQk1tvs.o: In function `main'
вместо:
eg1.o: In function `main':
См. также
Неверный порядок, в котором указаны взаимозависимые связанные библиотеки
Включение взаимозависимых библиотек в неправильном порядке — это всего лишь один способ
в котором вы можете получить файлы, требующие определения
позже в ссылке, чем файлы, предоставляющие определения. Помещение библиотек до
объектные файлы, которые относятся к ним, являются еще одним способом сделать ту же ошибку.