- C++ Errors: Undefined Reference, Unresolved External Symbol etc.
- Important C++ Errors
- Undefined Reference
- Segmentation Fault (core dumped)
- Unresolved External Symbol
- Conclusion
- Что такое undefined ссылка/неразрешенная внешняя ошибка символов и как ее исправить?
- ОТВЕТЫ
- Ответ 1
- Ответ 2
- Члены класса:
- Деструктор pure virtual нуждается в реализации.
- virtual методы должны быть реализованы или определены как чистые.
- Члены класса
- Общей ошибкой является отказ от названия:
- static члены данных должны быть определены вне класса в единице перевода:
- Ответ 3
- Отказ от ссылки на соответствующие библиотеки/объектные файлы или компиляцию файлов реализации
- Ответ 4
- Объявлено, но не определено переменная или функция.
- Ответ 5
- Порядок, в котором указаны взаимозависимые связанные библиотеки, неверен.
- Ответ 6
- Ответ 7
- Символы были определены в программе на языке C и используются в коде С++.
- Ответ 8
- Ответ 9
- Неправильно импортировать/экспортировать методы/классы по модулям /dll (специфичный для компилятора).
- Ответ 10
- Ответ 11
- Реализации шаблонов не отображаются.
- Ответ 12
- Ответ 13
- Ответ 14
- Ответ 15
- Ответ 16
- Ответ 17
- Ошибка в компиляторе /IDE
- Ответ 18
- Используйте компоновщик, чтобы помочь диагностировать ошибку
- Ответ 19
- Ответ 20
- Ответ 21
- Ответ 22
- Подружиться с шаблонами.
- Ответ 23
- Ваша ссылка потребляет библиотеки перед файлами объектов, которые относятся к ним
- Минимальный пример, связанный с созданной вами статической библиотекой
- Минимальный пример, включающий общую системную библиотеку, библиотеку сжатия libz
- Что вы делаете неправильно?
- Объяснение
- Я могу воспроизвести проблему в примере 1, но не в примере 2
- Почему я все еще получаю эту проблему, даже если я компилирую-ссылку в то же время?
- См. также
- Ответ 24
- Когда ваши пути включения различаются
- Дальнейшая разработка с примером
- Отладка компоновщика
- Ответ 25
- Непоследовательные определения UNICODE
- Ответ 26
- Очистить и восстановить
- Ответ 27
- Отсутствует «extern» в const объявления/определения переменных (только С++)
- Ответ 28
- Разные версии библиотек
- Разрешение
- Ответ 29
- При связывании с разделяемыми библиотеками убедитесь, что используемые символы не скрыты.
- Ответ 30
C++ Errors: Undefined Reference, Unresolved External Symbol etc.
This Tutorial Details the Critical Errors that Programmers often Encounter in C++like Undefined Reference, a Segmentation Fault (core dumped) and Unresolved External Symbol:
We will discuss the most important errors that we often encounter in C++ that are equally critical indeed. Apart from the system and semantic errors and exceptions that occur from time to time, we also get other critical errors that affect the running of programs.
These errors mostly occur towards the end of the program at runtime. Sometimes the program gives proper output and then the error occurs.
What You Will Learn:
Important C++ Errors
In this tutorial, we will discuss three types of errors that are critical from any C++ programmer’s point of view.
- Undefined reference
- Segmentation fault (core dumped)
- Unresolved external symbol
We will discuss the possible causes of each of these errors and along with the precautions that we can take as a programmer to prevent these errors.
Let’s start!!
Undefined Reference
An “Undefined Reference” error occurs when we have a reference to object name (class, function, variable, etc.) in our program and the linker cannot find its definition when it tries to search for it in all the linked object files and libraries.
Thus when the linker cannot find the definition of a linked object, it issues an “undefined reference” error. As clear from definition, this error occurs in the later stages of the linking process. There are various reasons that cause an “undefined reference” error.
We discuss some of these reasons below:
#1) No Definition Provided For Object
This is the simplest reason for causing an “undefined reference” error. The programmer has simply forgotten to define the object.
Consider the following C++ program. Here we have only specified the prototype of function and then used it in the main function.
Output:
So when we compile this program, the linker error that says “undefined reference to ‘func1()’” is issued.
In order to get rid of this error, we correct the program as follows by providing the definition of the function func1. Now the program gives the appropriate output.
Output:
#2) Wrong Definition (signatures don’t match) Of Objects Used
Yet another cause for “undefined reference” error is when we specify wrong definitions. We use any object in our program and its definition is something different.
Consider the following C++ program. Here we have made a call to func1 (). Its prototype is int func1 (). But its definition does not match with its prototype. As we see, the definition of the function contains a parameter to the function.
Thus when the program is compiled, the compilation is successful because of the prototype and function call match. But when the linker is trying to link the function call with its definition, it finds the problem and issues the error as “undefined reference”.
Output:
Thus to prevent such errors, we simply cross-check if the definitions and usage of all the objects are matching in our program.
#3) Object Files Not Linked Properly
This issue can also give rise to the “undefined reference” error. Here, we may have more than one source files and we might compile them independently. When this is done, the objects are not linked properly and it results in “undefined reference”.
Consider the following two C++ programs. In the first file, we make use of the “print ()” function which is defined in the second file. When we compile these files separately, the first file gives “undefined reference” for the print function, while the second file gives “undefined reference” for the main function.
Output:
Output:
The way to resolve this error is to compile both the files simultaneously ( For example, by using g++).
Apart from the causes already discussed, “undefined reference” may also occur because of the following reasons.
#4) Wrong Project Type
When we specify wrong project types in C++ IDEs like the visual studio and try to do things that the project does not expect, then, we get “undefined reference”.
#5) No Library
If a programmer has not specified the library path properly or completely forgotten to specify it, then we get an “undefined reference” for all the references the program uses from the library.
#6) Dependent Files Are Not Compiled
A programmer has to ensure that we compile all the dependencies of the project beforehand so that when we compile the project, the compiler finds all the dependencies and compiles successfully. If any of the dependencies are missing then the compiler gives “undefined reference”.
Apart from the causes discussed above, the “undefined reference” error can occur in many other situations. But the bottom line is that the programmer has got the things wrong and in order to prevent this error they should be corrected.
Segmentation Fault (core dumped)
The error “segmentation fault (core dumped)” is an error that indicates memory corruption. It usually occurs when we try to access a memory that does not belong to the program into consideration.
Here are some of the reasons that cause Segmentation fault error.
#1) Modifying The Constant String
Consider the following program wherein we have declared a constant string. Then we try to modify this constant string. When the program is executed, we get the error shown in the output.
Output:
#2) Dereferencing Pointer
A pointer must point to a valid memory location before we dereference it. In the below program, we see that the pointer is pointing to NULL which means the memory location it’s pointing to is 0 i.e. invalid.
Hence when we dereference it in the next line, we are actually trying to access its unknown memory location. This indeed results in a segmentation fault.
Output:
The next program shows a similar case. In this program also, the pointer is not pointing to valid data. An uninitialized pointer is as good as NULL and hence it also points to unknown memory location. Thus when we try to dereference it, it results in a segmentation fault.
Output:
In order to prevent such errors, we have to ensure that our pointer variables in the program point to valid memory locations always.
#3) Stack Overflow
When we have recursive calls in our program, they eat up all the memory in the stack and cause the stack to overflow. In such cases, we get the segmentation fault as running out of stack memory is also a kind of memory corruption.
Consider the below program where we calculate the factorial of a number recursively. Note that our base condition tests if the number is 0 and then returns 1. This program works perfectly for positive numbers.
But what happens when we actually pass a negative number to a factorial function? Well, as the base condition is not given for the negative numbers, the function does not know where to stop and thus results in a stack overflow.
This is shown in the output below that gives segmentation fault.
Output:
Segmentation fault (core dumped)
Now in order to fix this error, we slightly change the base condition and also specify the case for negative numbers as shown below.
Output:
Now we see that the segmentation fault is taken care of and the program works fine.
Unresolved External Symbol
The unresolved external symbol is a linker error that indicates it cannot find the symbol or its reference during the linking process. The error is similar to “undefined reference” and is issued interchangeably.
We have given two instances below where this error can occur.
#1) When we refer a structure variable in the program that contains a static member.
Output:
In the above program, structure C has a static member s that is not accessible to the outside programs. So when we try to assign it a value in the main function, the linker doesn’t find the symbol and may result in an “unresolved external symbol” or “undefined reference”.
The way to fix this error is to explicitly scope the variable using ‘::’ outside the main before using it.
#2) When we have external variables referenced in the source file, and we have not linked the files that define these external variables.
This case is demonstrated below:
Output:
In general, in case of an “unresolved external symbol”, the compiled code for any object like function fails to find a symbol to which it makes a reference to, maybe because that symbol is not defined in the object files or any of the libraries specified to the linker.
Conclusion
In this tutorial, we discussed some major errors in C++ that are critical and can affect the program flow and might even result in an application crash. We explored all about Segmentation fault, Unresolved external symbol, and Undefined reference in detail.
Although these errors can occur anytime, from the causes that we discussed we know that we can easily prevent them by carefully developing our program.
Источник
Что такое undefined ссылка/неразрешенная внешняя ошибка символов и как ее исправить?
Каковы 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.
создаст следующие ошибки с помощью GCC:
и аналогичные ошибки с Microsoft Visual Studio:
Общие причины включают:
Ответ 2
Члены класса:
Деструктор pure virtual нуждается в реализации.
Объявление деструктора по-прежнему требует определения его (в отличие от обычной функции):
Это происходит потому, что деструкторы базового класса вызывается, когда объект уничтожается неявно, поэтому требуется определение.
virtual методы должны быть реализованы или определены как чистые.
Это похоже на методы не virtual без определения, с добавлением аргументов, что чистая декларация генерирует фиктивный vtable, и вы можете получить ошибку компоновщика без использования функции:
Чтобы это сработало, объявите X::foo() как чистый:
Члены класса
Некоторые члены должны быть определены, даже если они не используются явно:
Ниже приведена ошибка:
Реализация может быть встроенной в самом определении класса:
Если реализация вне определения класса, но в заголовке, методы должны быть помечены как inline , чтобы предотвратить множественное определение.
Все используемые методы-члены должны быть определены, если они используются.
Общей ошибкой является отказ от названия:
Определение должно быть
static члены данных должны быть определены вне класса в единице перевода:
Инициализатор может быть предоставлен для элемента данных static const интегрального или перечисляемого типа в определении класса; однако odr-использование этого элемента по-прежнему потребует определения области пространства имен, как описано выше. С++ 11 позволяет инициализировать внутри класса для всех членов static const .
Ответ 3
Отказ от ссылки на соответствующие библиотеки/объектные файлы или компиляцию файлов реализации
Обычно каждая единица перевода генерирует объектный файл, содержащий определения символов, определенных в этой единице перевода. Чтобы использовать эти символы, вы должны привязать их к этим объектным файлам.
В разделе gcc вы должны указать все объектные файлы, которые должны быть связаны вместе в командной строке, или скомпилировать файлы реализации вместе.
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
Объявлено, но не определено переменная или функция.
Типичное объявление переменной
Поскольку это только объявление, требуется одиночное определение. Соответствующим определением будет:
Например, следующее приведет к ошибке:
Аналогичные замечания относятся к функциям. Объявление функции без ее определения приводит к ошибке:
Будьте осторожны, чтобы выполняемая вами функция точно соответствовала той, которую вы указали. Например, у вас могут быть несоответствующие cv-квалификаторы:
Другие примеры несоответствий включают
- Функция/переменная, объявленная в одном пространстве имен, определенном в другом.
- Функция/переменная, объявленная как член класса, определяемая как глобальная (или наоборот).
- Тип возвращаемого значения функции, номер и типы параметров и соглашение о вызове не все точно согласны.
Сообщение об ошибке из компилятора часто дает вам полное объявление переменной или функции, которая была объявлена, но не определена. Сравните его с определением, которое вы указали. Убедитесь, что каждая деталь соответствует.
Ответ 5
Порядок, в котором указаны взаимозависимые связанные библиотеки, неверен.
Порядок, в котором связаны библиотеки, имеет значение, если библиотеки зависят друг от друга. В общем случае, если библиотека A зависит от библиотеки B , тогда libA ДОЛЖЕН появляется перед libB в флагах компоновщика.
Итак, повторим еще раз, порядок ДОЛЖЕН!
Ответ 6
что такое undefined ссылка/неразрешенный внешний символ
Я попытаюсь объяснить, что такое «undefined ссылка/неразрешенный внешний символ».
note: я использую g++ и Linux, и все примеры для него
Например, у нас есть код
Сделать объектные файлы
После фазы ассемблера мы имеем объектный файл, который содержит любые экспортируемые символы. Посмотрите на символы
Я отклонил некоторые строки из вывода, потому что они не имеют значения
Итак, мы видим следующие символы для экспорта.
src2.cpp ничего не экспортирует, и мы не видели его символов
Свяжите наши объектные файлы
и запустите его
Линкер видит экспортированные символы и связывает его. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь.
и перестроить файл объекта
ОК (нет ошибок), потому что мы только создаем объектный файл, ссылка еще не завершена. Попробуйте связать
Это произошло потому, что наше local_var_name статично, то есть оно не видно для других модулей. Теперь глубже. Получить выход фазы перевода
Итак, мы видели, что для local_var_name нет метки, поэтому компоновщик ее не нашел. Но мы хакеры:), и мы можем это исправить. Откройте src1.s в текстовом редакторе и измените
то есть. вы должны иметь ниже
мы изменили видимость local_var_name и установили его значение 456789. Попробуйте создать из него объектный файл
ok, см. вывод на экран (символы)
теперь local_var_name имеет Bind GLOBAL (был локальным)
и запустите его
ok, мы его взломаем:)
Итак, в результате — «undefined ссылка/неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.
Ответ 7
Символы были определены в программе на языке C и используются в коде С++.
Функция (или переменная) void foo() была определена в программе на C, и вы пытаетесь использовать ее в программе на С++:
Компилятор С++ ожидает, что имена будут искажены, поэтому вы должны объявить эту функцию как:
Эквивалентно, вместо определения в программе на C, функция (или переменная) void foo() была определена в С++, но с C-связью:
и вы пытаетесь использовать его в С++-программе с С++-связью.
Если вся библиотека включена в заголовочный файл (и была скомпилирована как код C); включение должно быть следующим:
Ответ 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) .
Эта двойная функциональность обычно получается с помощью макроса:
Макрос THIS_MODULE будет определен только в модуле, который экспортирует функцию. Таким образом, декларация:
и сообщает компилятору экспортировать функцию, так как текущий модуль содержит свое определение. Если включить объявление в другом модуле, он будет расширяться до
и сообщает компилятору, что определение находится в одной из библиотек, к которым вы привязались (см. также 1)).
Вы можете подобрать классы импорта/экспорта:
Ответ 10
Это одно из самых запутанных сообщений об ошибках, которые каждый программист VС++ видел снова и снова. Давайте сначала сделаем чёткость.
а. Что такое символ? Короче говоря, символ — это имя. Это может быть имя переменной, имя функции, имя класса, имя typedef или что-либо кроме тех имен и знаков, которые принадлежат языку С++. Это пользователь, определенный или введенный библиотекой зависимостей (другой пользовательский).
В. Что такое внешний? В VС++ каждый исходный файл (.cpp,.c и т.д.) Рассматривается как единица перевода, компилятор компилирует по одному модулю за раз и генерирует один объектный файл (.obj) для текущей единицы перевода. (Обратите внимание, что каждый заголовочный файл, включенный в этот исходный файл, будет предварительно обработан и будет рассматриваться как часть этой единицы перевода). Все внутри единицы перевода считается внутренним, все остальное считается внешним. В С++ вы можете ссылаться на внешний символ, используя ключевые слова, такие как extern , __declspec (dllimport) и т.д.
С. Что такое «разрешение»? Resolve — термин времени связывания. Во время компоновки линкер пытается найти внешнее определение для каждого символа в объектных файлах, которые не могут найти свое определение внутри. Объем этого процесса поиска включает:
- Все объектные файлы, сгенерированные во время компиляции
- Все библиотеки (.lib), явно или неявно указанные в качестве дополнительных зависимостей этого строительного приложения.
Этот процесс поиска называется разрешением.
Д. Наконец, почему Unresolved External Symbol? Если компоновщик не может найти внешнее определение для символа, который не имеет внутреннего определения, он сообщает об ошибке неразрешенного внешнего символа.
E. Возможные причины LNK2019: ошибка неразрешенного внешнего символа. Мы уже знаем, что эта ошибка связана с тем, что компоновщик не смог найти определение внешних символов, возможные причины могут быть отсортированы как:
Например, если у нас есть функция foo, определенная в a.cpp:
В b.cpp мы хотим вызвать функцию foo, поэтому добавим
объявить функцию foo() и вызвать ее в другом теле функции, скажем bar() :
Теперь, когда вы создадите этот код, вы получите ошибку LNK2019, жалуясь, что foo — неразрешенный символ. В этом случае мы знаем, что foo() имеет свое определение в a.cpp, но отличается от того, которое мы вызываем (другое возвращаемое значение). Это так, что определение существует.
- Определение не существует
Если мы хотим вызвать некоторые функции в библиотеке, но библиотека импорта не добавляется в дополнительный список зависимостей (установленный из: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency ) вашего проекта. Теперь компоновщик сообщит LNK2019, поскольку определение не существует в текущей области поиска.
Ответ 11
Реализации шаблонов не отображаются.
Неспециализированные шаблоны должны иметь свои определения для всех единиц перевода, которые их используют. Это означает, что вы не можете отделить определение шаблона к файлу реализации. Если вы должны отделить реализацию, обычным обходным решением является наличие файла impl , который вы укажете в конце заголовка, который объявляет шаблон. Общей ситуацией является:
Чтобы исправить это, вы должны переместить определение 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 ссылаться на нужную библиотеку во время ссылки;
В дополнение к пути библиотеки, включая каталог библиотеки, это должно быть полное имя библиотеки.
Ответ 15
Пакет Visual Studio NuGet необходимо обновить для новой версии набора инструментов
У меня просто возникла проблема с подключением libpng с Visual Studio 2013. Проблема в том, что в файле пакета были только библиотеки для Visual Studio 2010 и 2012.
Правильное решение — надеяться, что разработчик выпустит обновленный пакет, а затем обновит его, но это сработало для меня, взломав дополнительную настройку для VS2013, указав на файлы библиотеки VS2012.
Я отредактировал пакет (в папке packages внутри каталога решений), найдя packagename\build\native\packagename.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 в командную строку. Более подробную информацию можно найти здесь:
Для MSVC в командную строку линии добавляется /VERBOSE (в частности, /VERBOSE:LIB ).
Ответ 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 с этим содержимым:
Некоторые более сложные сборки могут не поддерживать это. Например, если вы включаете -v в параметры компилятора, вы можете видеть, что mainwin gcc wrapper mwdip сбрасывает компоновщики script командных файлов в расширенном списке результатов библиотек для ссылки. Простая работа заключается в замене файла командной строки компоновщика script копией файла вместо (или символической ссылки), например
Или вы можете заменить аргумент -l полным путем .so, например. вместо -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2
Ответ 22
Подружиться с шаблонами.
С учетом фрагмента кода типа шаблона с оператором (или функцией) друга;
operator объявляется как функция без шаблона. Для каждого типа T , используемого с Foo , должен быть не templated operator . Например, если объявлен тип Foo , тогда должна существовать реализация оператора следующим образом:
Так как он не реализован, компоновщик не находит его и приводит к ошибке.
Чтобы исправить это, вы можете объявить оператор шаблона перед типом Foo , а затем объявить его другом, соответствующим экземпляром. Синтаксис немного неудобен, но выглядит следующим образом:
Приведенный выше код ограничивает дружбу оператора соответствующим экземпляром Foo , т.е. экземпляр operator ограничен доступом к частным членам экземпляра Foo .
Позволяя дружбе распространяться на все экземпляры шаблонов, следующим образом:
Или реализация для operator может быть выполнена внутри внутри определения класса;
Обратите внимание, что когда объявление оператора (или функции) появляется только в классе, имя не доступно для «нормального» поиска, только для зависимого от аргумента поиска, от cppreference;
Имя, объявленное ранее в объявлении друга в классе или классе, шаблон X становится членом самого внутреннего охватывающего пространства имен X, но недоступен для поиска (кроме зависимого от аргумента поиска, который учитывает X), если соответствующее объявление на область пространства имен предоставляется.
В качестве побочного примечания к образцу неудачного кода; g++ предупреждает об этом следующим образом
warning: friend declaration ‘std::ostream& operator
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
my_lib.h
eg1.c
Вы создаете свою статическую библиотеку:
Вы компилируете свою программу:
Вы пытаетесь связать его с libmy_lib.a и сбой:
Тот же результат, если вы выполните компиляцию и ссылку за один шаг, например:
Минимальный пример, включающий общую системную библиотеку, библиотеку сжатия libz
eg2.c
Скомпилируйте свою программу:
Попробуйте связать свою программу с libz и выполните сбой:
То же самое, если вы компилируете и связываете за один раз:
И вариант на примере 2 с участием pkg-config :
Что вы делаете неправильно?
В последовательности объектных файлов и библиотек, которые вы хотите связать, чтобы сделать свой вы размещаете библиотеки перед объектными файлами, которые ссылаются на их. Вы должны поместить библиотеки после того, как файлы объектов, которые ссылаются к ним.
Правильно ссылайтесь на пример 1:
Правильно ссылайтесь на пример 2:
Правильно ссылайтесь на пример 2 pkg-config :
Объяснение
Чтение необязательно здесь.
По умолчанию команда привязки, сгенерированная 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 разрешены.
Когда вы пытаетесь связать программу следующим образом:
компоновщик не добавил 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 , ссылка разрешена
Но когда вы пытаетесь связать программу, например:
порядок событий неверен точно так же, как в примере 1. В момент, когда компоновщик находит -lz , ссылок на что-либо нет в программе: все они находятся в eg2.o , который еще не был замечен. Итак компоновщик решает, что он не нужен для libz . Когда он достигнет eg2.o , добавляет его в программу, и затем имеет undefined ссылку на zlibVersion , последовательность связей закончена; эта ссылка не решена, и связь не удалась.
Наконец, вариант pkg-config примера 2 имеет теперь очевидное объяснение. После расширения оболочки:
который снова является примером 2.
Я могу воспроизвести проблему в примере 1, но не в примере 2
отлично подходит для вас!
(Или: эта связь работала хорошо для вас, скажем, Fedora 23, но не работает на Ubuntu 16.04)
Это потому, что дистрибутив, на котором работает ссылка, является одним из тех, не настраивает свою привязку GCC для связывания разделяемых библиотек по мере необходимости.
В тот же день для UNIX-подобных систем было нормально связывать статические и общие библиотеки по разным правилам. Статические библиотеки в последовательности связей были связаны по мере необходимости, объясняется в примере 1, но разделяемые библиотеки были связаны безоговорочно.
Такое поведение экономично в режиме ссылки, потому что линкер не должен обдумывать нужна ли библиотека совместно используемой библиотеке: если она является общей библиотекой, ссылку. Большинство библиотек в большинстве связей являются общими библиотеками. Но есть и недостатки: —
Это неэкономично во время выполнения, поскольку это может привести к тому, что общие библиотеки будут загружаются вместе с программой, даже если они не нужны.
Различные правила привязки для статических и разделяемых библиотек могут вводить в заблуждение к незнакомым программистам, которые могут не знать, есть ли -lfoo в их связи будет разрешено до /some/where/libfoo.a или /some/where/libfoo.so , и может не понимать разницу между общими и статическими библиотеками в любом случае.
Этот компромисс привел сегодня к раскольнической ситуации. Некоторые дистрибутивы изменили свои правила привязки GCC для разделяемых библиотек, чтобы, по мере необходимости, принцип применяется ко всем библиотекам. Некоторые дистрибутивы застряли со старым путь.
Почему я все еще получаю эту проблему, даже если я компилирую-ссылку в то же время?
Если я просто сделаю:
Конечно, gcc должен сначала скомпилировать eg1.c , а затем связать полученный результат объектный файл с libmy_lib.a . Итак, как он не может знать этот объектный файл если он выполняет привязку?
Поскольку компиляция и привязка к одной команде не меняют порядок последовательности связей.
Когда вы запустите команду выше, gcc выяснит, что вы хотите компилировать + связь. Таким образом, за кулисами, он генерирует команду компиляции и запускает он генерирует команду привязки и запускает ее, как если бы вы запускали две команды:
Таким образом, связь не работает так же, как если бы вы выполняли эти две команды. только разница, которую вы замечаете при ошибке, заключается в том, что gcc сгенерировал временный файл объекта в файле компиляции + ссылка, потому что вы не говорите об этом использовать eg1.o . Мы видим:
См. также
Включение взаимозависимых библиотек в неправильном порядке — это всего лишь один способ в котором вы можете получить файлы, требующие определения позже в ссылке, чем файлы, предоставляющие определения. Помещение библиотек до объектные файлы, которые относятся к ним, являются еще одним способом сделать ту же ошибку.
Ответ 24
Когда ваши пути включения различаются
Ошибки компоновщика могут произойти, если заголовочный файл и связанная с ним общая библиотека (файл .lib) не синхронизируются. Позволь мне объяснить.
Как работают линкеры? Линкер соответствует объявлению функции (объявленному в заголовке) с его определением (в общей библиотеке) путем сравнения их подписи. Вы можете получить ошибку компоновщика, если компоновщик не найдет определение функции, которое идеально подходит.
Возможно ли получить ошибку компоновщика, даже если объявление и определение, похоже, совпадают? Да! Они могут выглядеть одинаково в исходном коде, но это действительно зависит от того, что видит компилятор. По существу, вы можете столкнуться с такой ситуацией:
Обратите внимание, что, хотя обе декларации функций выглядят одинаково в исходном коде, но они действительно различаются в зависимости от компилятора.
Вы можете спросить, как это получается в такой ситуации? Включить пути, конечно! Если при компиляции разделяемой библиотеки путь include ведет к header1.h , и вы в конечном итоге используете header2.h в своей собственной программе, вы оставите царапины в своем заголовке, задаваясь вопросом, что произошло (каламбур).
Пример того, как это может произойти в реальном мире, объясняется ниже.
Дальнейшая разработка с примером
У меня есть два проекта: graphics.lib и main.exe . Оба проекта зависят от common_math.h . Предположим, что библиотека экспортирует следующую функцию:
И затем вы идете вперед и включаете библиотеку в свой собственный проект.
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 и т.д. Это контрольный знак того, что этот код изначально предназначался как для UNICODE, так и для многобайтового символа ( или «узкий» ).
Чтобы исправить это, создайте все необходимые библиотеки и проекты с последовательным определением UNICODE (и _UNICODE ).
Это можно сделать либо с помощью:
Или в настройках проекта;
Свойства проектa > Общие > Значения по умолчанию > Набор символов
Или в командной строке;
Альтернатива применима также, если UNICODE не предназначен для использования, убедитесь, что определения не установлены, и/или многосимвольная настройка используется в проектах и последовательно применяется.
Не забывайте быть совместимыми между сборками «Release» и «Debug».
Ответ 26
Очистить и восстановить
«Чистый» сборщик может удалить «мертвую древесину», которая может быть оставлена лежащей вокруг предыдущих сборок, неудачными сборками, неполными сборками и другими проблемами сборки, связанными с сборкой.
В общем случае IDE или сборка будет включать в себя некоторую форму «чистой» функции, но это может быть неправильно настроено (например, в ручном make файле) или может завершиться неудачей (например, промежуточные или результирующие двоичные файлы доступны только для чтения).
После завершения «очистки» убедитесь, что «чистый» преуспел, и все созданные промежуточные файлы (например, автоматический make файл) были успешно удалены.
Этот процесс можно рассматривать как окончательный вариант, но часто является хорошим первым шагом; особенно если недавно был добавлен код, связанный с ошибкой (локально или из исходного репозитория).
Ответ 27
Отсутствует «extern» в const объявления/определения переменных (только С++)
Для людей, приезжающих с C, может быть неожиданностью, что в С++ глобальные переменные const имеют внутреннюю (или статическую) связь. В C это не так, поскольку все глобальные переменные неявно extern (т.е. Когда отсутствует ключевое слово static ).
— использовать заголовочный файл и включать его в файлы file2.cpp и file1.cpp
В качестве альтернативы можно было объявить переменную const в файле file1.cpp с явным extern
Ответ 28
Несмотря на то, что это довольно старые вопросы с несколькими принятыми ответами, я хотел бы поделиться тем, как устранить ошибку скрытого «неопределенная ссылка на».
Разные версии библиотек
Я использовал псевдоним для ссылки на std::filesystem::path : файловая система находится в стандартной библиотеке начиная с С++ 17, но моя программа должна была также компилироваться в С++ 14, поэтому я решил использовать переменный псевдоним:
Допустим, у меня есть три файла: 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»))) являются внешними в результирующем общем объекте.
Вы можете проверить, являются ли символы, которые вы ищете, внешними, вызывая:
скрытые/локальные символы отображаются с помощью nm с символом нижнего регистра, например t вместо «T для кодового раздела:
Вы также можете использовать nm с опцией -C для демонстрации имен (если использовался C++).
Подобно Windows-dll, можно было бы отметить публичные функции с помощью определения, например DLL_PUBLIC определенного как:
Что примерно соответствует версии Windows/MSVC:
Более подробную информацию о видимости можно найти в wiki.
Когда блок перевода скомпилирован с -fvisibility=hidden результирующие символы имеют по-прежнему внешнюю связь (показан с типом символа верхнего регистра на nm ) и могут быть использованы для внешней связи без проблем, если объектные файлы становятся частью статических библиотек. Связь становится локальной только тогда, когда объектные файлы связаны с общей библиотекой.
Чтобы найти, какие символы в объектном файле скрыты, выполните:
Ответ 30
Разные архитектуры
Вы можете увидеть сообщение как:
В этом случае это означает, что доступные символы для другой архитектуры, чем та, для которой вы компилируете.
В Visual Studio это происходит из-за неправильной «Платформы», и вам нужно либо выбрать подходящую, либо установить правильную версию библиотеки.
В Linux это может быть связано с неправильной папкой библиотеки (например, с использованием lib вместо lib64 ).
В MacOS есть возможность отправить обе архитектуры в одном файле. Может быть, что ссылка ожидает, что обе версии будут там, но только одна есть. Это также может быть проблема с неправильной папкой lib / lib64 которой находится библиотека.
Источник