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

На вашем сервере server.js:

var express   =     require("express");
var app       =     express();
app.use(express.static(__dirname + '/public'));

Вы объявляете экспресс и приложение отдельно, создайте папку с именем «public» или по своему усмотрению, и все же вы можете получить доступ к этой папке. В шаблоне src вы поместили относительный путь из / public (или имя вашей папки в статические файлы). Остерегайтесь баров на маршрутах.

1378
задан Baum mit Augen 21 January 2018 в 17:39
поделиться

13 ответов

Ошибка в компиляторе / IDE

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

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

  • Очистить проект (некоторые IDE имеют возможность сделать это, вы также можете сделать это вручную, удалив объектные файлы)
  • Попробуйте начать новый проект, скопировав весь исходный код из исходного.
29
ответ дан Community 21 January 2018 в 17:39
поделиться

Функции или методы класса определяются в исходных файлах с помощью спецификатора inline.

Пример: -

main.cpp

#include "gum.h"
#include "foo.h"

int main()
{
    gum();
    foo f;
    f.bar();
    return 0;
}

foo.h (1)

#pragma once

struct foo {
    void bar() const;
};

gum.h (1)

#pragma once

extern void gum();

foo.cpp (1)

#include "foo.h"
#include <iostream>

inline /* <- wrong! */ void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp (1)

#include "gum.h"
#include <iostream>

inline /* <- wrong! */ void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

Если вы укажете, что gum (аналогично, foo::bar) является inline в своем определении, то компилятор будет встроен gum (если он выберет) , посредством: -

  • не испускающих какого-либо уникального определения gum, и, следовательно,
  • не испускают никакого символа, с помощью которого компоновщик может ссылаться на определение gum, и вместо этого
  • заменяет все вызовы на gum встроенными копиями скомпилированного тела из gum.

В результате, если вы определяете gum inline в исходном файле gum.cpp, он компилируется в объектный файл gum.o, в котором все вызовы gum являются встроенными, и символ не определен под которым линкер может ссылаться на gum. Когда вы связываете gum.o в программу вместе с другим объектным файлом, например, main.o, которые делают ссылки на внешний символ gum, компоновщик не может разрешить эти ссылки. Таким образом, связь не работает:

Компиляция:

g++ -c  main.cpp foo.cpp gum.cpp

Ссылка:

$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status

Вы можете определить gum как inline, если компилятор может видеть его определение в каждом исходном файле, в котором может быть вызвано gum. Это означает, что его встроенное определение должно существовать в файле заголовка , который вы включаете в каждый исходный файл, который вы компилируете, в который может быть вызван gum. Сделайте одно из двух:

Либо не включайте определения

Удалите спецификатор inline из определения исходного файла:

foo.cpp (2)

#include "foo.h"
#include <iostream>

void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp (2)

#include "gum.h"
#include <iostream>

void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

Перестройте с этим:

$ g++ -c  main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const

Успех.

Или правильно встроенный

Встроенные определения в заголовочных файлах:

foo.h (2)

#pragma once
#include <iostream>

struct foo {
    void bar() const  { // In-class definition is implicitly inline
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};
// Alternatively...
#if 0
struct foo {
    void bar() const;
};
inline void foo::bar() const  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif

] gum.h (2)

#pragma once
#include <iostream>

inline void gum() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

Теперь нам не нужны foo.cpp или gum.cpp:

$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const
1
ответ дан Mike Kinghan 21 January 2018 в 17:39
поделиться

Используйте компоновщик, чтобы помочь диагностировать ошибку

Большинство современных компоновщиков включают опцию подробного вывода, которая печатает в различной степени;

  • Вызов ссылки (командная строка),
  • Данные о том, какие библиотеки включены в этап соединения,
  • Расположение библиотек,
  • Используемые пути поиска.

Для gcc и clang; вы обычно добавляете -v -Wl,--verbose или -v -Wl,-v в командную строку. Более подробную информацию можно найти здесь:

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

  • Страница MSDN в опции компоновщика /VERBOSE .
27
ответ дан Niall 21 January 2018 в 17:39
поделиться

неопределенная ссылка на WinMain@16 или аналогичную «необычную» main() точку входа (особенно для ).

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

Если ваша IDE поддерживает Простые консольные проекты , вы можете выбрать этот тип проекта вместо проекта Windows-приложения.


Здесь case1 и case2 более подробно рассматриваются из проблемы реального мира .

41
ответ дан πάντα ῥεῖ 21 January 2018 в 17:39
поделиться

Пакет 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 год, и в этом случае это сработало.

34
ответ дан Malvineous 21 January 2018 в 17:39
поделиться

Непоследовательные UNICODE определения

Сборка Windows UNICODE строится с TCHAR и т. Д., Определенным как wchar_t и т. Д. Когда не выполняется сборка с UNICODE, определенным как сборка с TCHAR, определенным как char и т. Д. Эти определения UNICODE и _UNICODE влияют на все типы строк «T» ; LPTSTR, LPCTSTR и их лось.

Создание одной библиотеки с определенным UNICODE и попытка связать ее в проекте, где UNICODE не определено, приведет к ошибкам компоновщика, поскольку в определении TCHAR будет несоответствие; char против wchar_t.

Ошибка обычно включает в себя функцию со значением производного типа char или wchar_t, к ним также может относиться std::basic_string<> и т. Д. При просмотре уязвимой функции в коде часто встречается ссылка на TCHAR или std::basic_string<TCHAR> и т. Д. Это контрольный признак того, что код изначально предназначался как для UNICODE, так и для многобайтового символа ( или "узкая") сборка.

Чтобы исправить это, соберите все необходимые библиотеки и проекты с непротиворечивым определением UNICODE_UNICODE).

  1. Это можно сделать с помощью:

    #define UNICODE
    #define _UNICODE
    
  2. Или в настройках проекта;

    Свойства проекта> Общие > Проект по умолчанию> Набор символов

  3. Или в командной строке;

    /DUNICODE /D_UNICODE
    

Альтернатива также применима, если UNICODE не предназначен для использования, убедитесь, что определения не установлены, и / или многосимвольная настройка используется в проектах и ​​применяется последовательно.

Не забывайте также соблюдать согласованность между сборками «Release» и «Debug».

15
ответ дан Niall 21 January 2018 в 17:39
поделиться

Оболочка вокруг GNU ld, которая не поддерживает сценарии компоновщика

Некоторые файлы .so фактически сценарии компоновщика GNU ld , например, Файл libtbb.so представляет собой текстовый файл ASCII с таким содержимым:

INPUT (libtbb.so.2)

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

cp libtbb.so.2 libtbb.so

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

19
ответ дан JDiMatteo 21 January 2018 в 17:39
поделиться

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

Стандартное поведение 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

Больше информации о видимости можно найти в gcc 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
4
ответ дан ead 21 January 2018 в 17:39
поделиться

Также, если вы используете сторонние библиотеки, убедитесь, что у вас есть правильные 32/64-битные двоичные файлы

35
ответ дан Dula 21 January 2018 в 17:39
поделиться

Реализация шаблона не видна.

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

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

Дальнейшее объяснение и другое возможное решение (явная реализация) см. В на этот вопрос и ответ .

52
ответ дан Community 21 January 2018 в 17:39
поделиться

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

В чем смысл компиляции?

  • Visual Studio ищет файл .vcxproj и начинает компилировать каждый файл с расширением .cpp. Порядок компиляции не определен. Так что вы не должны предполагать, что файл main.cpp компилируется первым
  • Если файлы .cpp зависят от дополнительных файлов .h, чтобы найти символы, которые могут или не могут быть определены в file .cpp
  • Если существует один файл .cpp, в котором компилятору не удалось найти один символ, ошибка времени компилятора вызывает сообщение Символ x не найден
  • Для каждого файла с расширением .cpp создается объектный файл .o, а также Visual Studio записывает выходные данные в файл с именем ProjectName.Cpp.Clean.txt , который содержит все объектные файлы, которые должен обрабатываться компоновщиком.

Второй шаг компиляции выполняется Linker.Linker должен объединить весь объектный файл и, наконец, создать вывод (который может быть исполняемым файлом или библиотекой)

Steps In Linking проект

  • Разобрать все объектные файлы и найти определение, которое было объявлено только в заголовках (например: код одного метода класса, как упомянуто в предыдущих ответах, или событие инициализация статической переменной, которая является членом класса)
  • Если один символ не может быть найден в объектных файлах, его также ищут в дополнительных библиотеках. Для добавления новой библиотеки в проект Свойства конфигурации -> Каталоги VC ++ -> Каталоги библиотек и здесь вы указали дополнительную папку для поиска библиотек и Свойства конфигурации -> Линкер - > Ввести для указания имени библиотеки. -Если компоновщик не может найти символ, который вы пишете в одном .cpp, он вызывает ошибку времени компоновщика , которая может звучать как error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

Наблюдение ]

  1. Как только компоновщик находит один символ, он не ищет его в других библиотеках
  2. Порядок компоновки библиотек имеет значение .
  3. Если Линкер находит внешний символ в одной статической библиотеке, он включает этот символ в выходные данные проекта. Однако, если библиотека является общей (динамической), он не включает код (символы) в выходные данные, но Может произойти сбой во время выполнения

Как устранить эту ошибку

Ошибка времени компиляции:

  • Убедитесь, что вы правильно написали синтаксический проект вашего c ++.

Ошибка времени компоновщика

  • Определите весь свой символ, который вы объявляете в заголовочных файлах
  • Используйте #pragma once, чтобы компилятор не включал один заголовок, если он был уже включены в текущий .cpp, который скомпилирован
  • Убедитесь, что ваша внешняя библиотека не содержит символов, которые могут вступать в конфликт с другими символами, которые вы определили в ваших заголовочных файлах
  • Когда вы используете шаблон, чтобы убедиться, что вы включили определение каждой функции шаблона в файл заголовка, чтобы позволить компилятору генерировать соответствующий код для любых экземпляров.
34
ответ дан eFarzad 21 January 2018 в 17:39
поделиться

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

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

Например:

// B.h
#ifndef B_H
#define B_H

struct B {
    B(int);
    int x;
};

#endif

// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}

// A.h
#include "B.h"

struct A {
    A(int x);
    B b;
};

// A.cpp
#include "A.h"

A::A(int x) : b(x) {}

// main.cpp
#include "A.h"

int main() {
    A a(5);
    return 0;
};

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

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

Скомпилировать:

$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

Итак, повторить еще раз, порядок DOES важно!

80
ответ дан Svalorzen 21 January 2018 в 17:39
поделиться

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

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

В разделе 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 помещает информацию в поле внизу каждой функции в разделе «Библиотека».

106
ответ дан MvG 21 January 2018 в 17:39
поделиться
Другие вопросы по тегам:

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