Поделиться через


Ошибка средств компоновщика LNK2001

неразрешенный внешний символ "символ"

Скомпилированный код делает ссылку или вызов символа. Символ не определен в библиотеках или файлах объектов, искомого компоновщиком.

За этим сообщением об ошибке следует неустранимая ошибка LNK1120. Чтобы устранить ошибки LNK1120, сначала исправьте все ошибки LNK2001 и LNK2019.

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

Что такое неразрешенный внешний символ?

Символ — это внутреннее имя функции или глобальной переменной. Это форма имени, используемого или определенного в скомпилированном файле объекта или библиотеке. Глобальная переменная определяется в файле объекта, для которого выделено хранилище. Функция определяется в файле объекта, где помещается скомпилированный код для тела функции. Внешний символ ссылается на один файл объекта, но определен в другом файле библиотеки или объекта. Экспортируемый символ — это символ, который становится общедоступным в файле объекта или библиотеке, определяющей ее.

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

Эта ошибка может произойти:

  • Если проект отсутствует ссылка на библиотеку (. LIB или объект (. OBJ) файл. Чтобы устранить эту проблему, добавьте ссылку на требуемую библиотеку или файл объектов в проект. Дополнительные сведения см. в разделе lib Files в качестве входных данных компоновщика.

  • Если проект имеет ссылку на библиотеку (). LIB или объект (. OBJ) файл, который, в свою очередь, требует символов из другой библиотеки. Это может произойти, даже если вы не вызываете функции, вызывающие зависимость. Чтобы устранить эту проблему, добавьте ссылку на другую библиотеку в проект. Дополнительные сведения см. в разделе "Общие сведения о классической модели для связывания: принятие символов вместе с поездкой".

  • При использовании параметров /NODEFAULTLIB или /Zl . При указании этих параметров библиотеки, содержащие обязательный код, не связаны с проектом, если они не включены явным образом. Чтобы устранить эту проблему, явно включите все библиотеки, используемые в командной строке ссылки. Если при использовании этих параметров отображается множество отсутствующих имен функций CRT или стандартной библиотеки, явно включите библиотеки CRT и стандартные библиотеки DLL или файлы библиотеки в ссылку.

  • При компиляции с помощью параметра /clr . Возможно, отсутствует ссылка на .cctor. Дополнительные сведения об устранении этой проблемы см. в разделе инициализация смешанных сборок.

  • Если вы связываетесь с библиотеками режима выпуска при создании отладочной версии приложения. Аналогичным образом, если вы используете параметры /MTd или /MDd или определить _DEBUG , а затем связаться с библиотеками выпуска, следует ожидать, что многие потенциальные неразрешенные внешние элементы, среди других проблем. Связывание сборки режима выпуска с библиотеками отладки также приводит к аналогичным проблемам. Чтобы устранить эту проблему, убедитесь, что в сборках отладки и розничных библиотеках в розничных сборках используются библиотеки отладки.

  • Если код ссылается на символ из одной версии библиотеки, но вы связываете другую версию библиотеки. Как правило, нельзя смешивать файлы объектов или библиотеки, созданные для разных версий компилятора. Библиотеки, которые находятся в одной версии, могут содержать символы, которые не могут находиться в библиотеках, включенных в другие версии. Чтобы устранить эту проблему, создайте все файлы объектов и библиотеки с одной и той же версией компилятора, прежде чем связать их вместе. Дополнительные сведения см. в статье о совместимости двоичных файлов C++ между версиями Visual Studio.

  • Если пути библиотеки устарели. Диалоговое окно "Параметры > инструментов>" > " Каталоги VC++ в разделе "Файлы библиотеки" позволяет изменить порядок поиска библиотеки. Папка компоновщика в диалоговом окне "Страницы свойств проекта" также может содержать пути, которые могут быть устаревшими.

  • При установке нового пакета SDK для Windows (возможно, в другом расположении). Порядок поиска библиотеки должен быть обновлен, чтобы указать новое расположение. Как правило, необходимо поместить путь к новому каталогу SDK, включая и каталоги lib перед расположением Visual C++ по умолчанию. Кроме того, проект, содержащий внедренные пути, может по-прежнему указывать на старые пути, допустимые, но устаревшие. Обновите пути для новых функций, добавленных новой версией, установленной в другом расположении.

  • При сборке в командной строке и создании собственных переменных среды. Убедитесь, что пути к средствам, библиотекам и файлам заголовков переходят к согласованной версии. Дополнительные сведения см. в статье Использование набора инструментов MSVC из командной строки.

Проблемы с программированием

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

  • Несоответствие регистра в исходном коде или файле определения модуля (.def). Например, если вы назовете переменную var1 в одном исходном файле C++ и попытаетесь получить к ней доступ, как VAR1 в другом, эта ошибка создается. Чтобы устранить эту проблему, используйте последовательно орфографические и регистрированные имена.

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

  • Вызов функции C из программы C++ без использования extern "C" объявления для функции C. Компилятор использует различные соглашения об именовании внутренних символов для кода C и C++. Имя внутреннего символа — это то, что компоновщик ищет при разрешении символов. Чтобы устранить эту проблему, используйте оболочку extern "C" для всех объявлений функций C, используемых в коде C++, что приводит к использованию компилятором внутреннего соглашения об именовании C для этих символов. Параметры компилятора /Tp и /Tc вызывают компиляцию файлов компилятора как C++ или C соответственно, независимо от того, что такое расширение имени файла. Эти параметры могут привести к тому, что внутренние имена функций отличаются от ожидаемых.

  • Попытка ссылаться на функции или данные, которые не имеют внешней компоновки. В C++встроенные функции и const данные имеют внутреннюю компоновку, если явно не указано как extern. Чтобы устранить эту проблему, используйте явные extern объявления для символов, на которые ссылается вне определяющего исходного файла.

  • Отсутствующий текст функции или определение переменной . Эта ошибка распространена при объявлении, но не определяет, переменные, функции или классы в коде. Компилятору требуется только прототип функции или extern объявление переменной для создания файла объекта без ошибок, но компоновщик не может разрешить вызов функции или ссылку на переменную, так как код функции или пространство переменной зарезервировано. Чтобы устранить эту проблему, обязательно определите каждую связанную с ссылкой функцию и переменную в исходном файле или библиотеке.

  • Вызов функции, использующий типы возвращаемых и параметров или соглашения о вызовах, которые не соответствуют тем в определении функции. В файлах объектов C++ оформление имен кодирует соглашение о вызовах, область действия класса или пространства имен, а также типы возвращаемых и параметров функции. Кодированная строка становится частью окончательного декорированного имени функции. Это имя используется компоновщиком для разрешения или сопоставления вызовов функции из других файлов объектов. Чтобы устранить эту проблему, убедитесь, что объявление функции, определение и вызовы используют одинаковые области, типы и соглашения о вызовах.

  • Код C++ вызывается при включении прототипа функции в определение класса, но не включает реализацию функции. Чтобы устранить эту проблему, обязательно предоставьте определение для всех вызываемого элемента класса.

  • Попытка вызвать чистую виртуальную функцию из абстрактного базового класса. Чистая виртуальная функция не имеет реализации базового класса. Чтобы устранить эту проблему, убедитесь, что все называемые виртуальные функции реализованы.

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

  • При сборке версии выпуска проекта ATL создается сообщение о том, что требуется код запуска CRT. Чтобы устранить эту проблему, выполните одно из следующих действий.

    • Удаление _ATL_MIN_CRT из списка препроцессора определяет, чтобы разрешить включить код запуска CRT. Дополнительные сведения см. на странице общего свойства (Project).

    • Если это возможно, удалите вызовы функций CRT, для которых требуется код запуска CRT. Вместо этого используйте эквиваленты Win32. Например, используйте функцию lstrcmp вместо strcmp. Известные функции, требующие кода запуска CRT, являются некоторыми из функций строки и с плавающей запятой.

Проблемы согласованности

В настоящее время нет стандартного оформления имен C++ между поставщиками компиляторов или даже между различными версиями одного компилятора. Файлы объектов, скомпилированные с различными компиляторами, могут не использовать ту же схему именования. Связывание их может вызвать ошибку LNK2001.

Сочетание встроенных и не встроенных параметров компиляции в разных модулях может привести к LNK2001. Если библиотека C++ создана с включенной функцией (/Ob1 или /Ob2), но соответствующий файл заголовка, описывающий функции, отключен (без inline ключевого слова), эта ошибка возникает. Чтобы устранить эту проблему, определите функции inline в файле заголовка, который вы включаете в другие исходные файлы.

Если вы используете директиву компилятора, убедитесь, что задано значение 2 или больше, и убедитесь, что вы также используете #pragma inline_depth параметр компилятора /Ob1 или /Ob2.

Эта ошибка может возникать, если при создании библиотеки DLL только для ресурсов не задан параметр LINK /NOENTRY. Чтобы устранить эту проблему, добавьте параметр /NOENTRY в команду ссылки.

Эта ошибка может возникать, если в проекте используются неправильные параметры /SUBSYSTEM или /ENTRY. Например, если написать консольное приложение и указать /SUBSYSTEM:WINDOWS, создается неразрешенная внешняя ошибка WinMain. Чтобы устранить эту проблему, убедитесь, что параметры соответствуют типу проекта. Дополнительные сведения об этих параметрах и точках входа см. в параметрах компоновщика /SUBSYSTEM и /ENTRY .

Экспортированные проблемы с символами DEF-файла

Эта ошибка возникает, когда экспорт, указанный в def-файле, не найден. Это может быть связано с тем, что экспорт не существует, орфографирован неправильно или использует декорированные имена C++. Файл .def не принимает украшенные имена. Чтобы устранить эту проблему, удалите ненужные экспорты и используйте extern "C" объявления для экспортированных символов.

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

Компилятор C++ и компоновщик используют "Украшение имен", также известное как name-mangling. Украшение имени кодирует дополнительные сведения о типе переменной в его имени символа. Имя символа функции кодирует тип возвращаемого значения, типы параметров, область и соглашение о вызовах. Это украшенное имя — это имя символа, на который компоновщик ищет разрешение внешних символов.

Ошибка ссылки может привести к тому, что объявление функции или переменной не соответствует определению функции или переменной. Это связано с тем, что любое различие становится частью имени символа для сопоставления. Ошибка может произойти, даже если один и тот же файл заголовка используется как в вызывающем коде, так и в определяемом коде. Один из способов может возникнуть, если вы компилируете исходные файлы с помощью разных флагов компилятора. Например, если код компилируется для использования __vectorcall соглашения о вызовах, но вы связываетесь с библиотекой, которая ожидает, что клиенты будут вызывать его с помощью соглашения по умолчанию __cdecl или __fastcall вызова. В этом случае символы не совпадают, так как соглашения о вызовах отличаются.

Чтобы найти причину, в сообщении об ошибке отображаются две версии имени. Он отображает как понятное имя, так и имя, используемое в исходном коде, и украшенное имя (в скобках). Вам не нужно знать, как интерпретировать украшенное имя. Вы по-прежнему можете искать и сравнивать его с другими украшенными именами. Средства командной строки помогают найти и сравнить ожидаемое имя символа и фактическое имя символа:

  • Здесь полезны параметры /EXPORTS и /SYMBOLS средства командной строки DUMPBIN. Они помогут вам определить, какие символы определены в файлах .dll и объектов или библиотеки. Вы можете использовать список символов, чтобы убедиться, что экспортированные декорированные имена соответствуют декорированным именам компоновщика.

  • В некоторых случаях компоновщик может сообщать только украшенное имя символа. С помощью средства командной строки UNDNAME можно получить неоценленную форму декорированного имени.

Дополнительные ресурсы

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