C ++:: Почему код (с использованием openCV) не работает после изменения подсистемной консоли в Windows? [dубликат]

Вот скрипт Python, который работает для меня. Запустите его с помощью python script.py audiofile.mp3.

Вам понадобится mutagen; установите его с помощью sudo -H pip install mutagen.

from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC, error
import sys
mp3file=sys.argv[1]
audio = MP3(mp3file, ID3=ID3)

try:
   audio.add_tags()
except error:
   pass

audio.tags.add(
   APIC(
      encoding=1,
      mime='image/png',
      type=3,
      desc=u'Cover',
      data=open('/path/to/artwork.png').read()
   )
)
audio.save()
1199
задан 21 January 2018 в 21:39

17 ответов

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

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

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%d\n", global_var_name, local_var_name);
    printf("%d\n", 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

Linker видит экспортированные символы и связывает их. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

, и перестроить объектный файл

$ 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

на

    .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, см. (dL)

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

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

f18]

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

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

64
ответ дан 15 August 2018 в 16:53

Отсутствует «extern» в const объявлениях / определениях переменных (только C ++)

Для людей, приходящих с C, может показаться неожиданным, что в 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
}

correct would использовать файл заголовка и включить его в файлы file2.cpp и file1.cpp

extern const int test;
extern int test2;

. В качестве альтернативы можно было бы объявить переменную const в файле file1.cpp с явным extern

7
ответ дан 15 August 2018 в 16:53

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

32
ответ дан 15 August 2018 в 16:53

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

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

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

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

Что смысл компиляции?

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

Наблюдение

После того, как компоновщик найдет один символ он не ищет в других библиотеках для него. Порядок связывания библиотек имеет значение. Если Linker находит внешний символ в одной статической библиотеке, он включает в себя символ на выходе проекта. Однако, если библиотека является общей (динамической), он не включает в себя код (символы) на выходе, но сбой во время выполнения может

Наблюдение

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

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

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

Определите весь свой символ, который вы объявляете в своих заголовочных файлах. [D6] Если файлы .cpp зависят от дополнительных файлов .h, чтобы найти символы, которые могут или не могут быть определены в файле .cpp Убедитесь, что ваш внешний библиотека не содержит символов, которые могут вступать в конфликт с другими символами, определенными в ваших файлах заголовков. Если существует один .cpp-файл, в котором компилятор не смог найти один символ, ошибка времени компилятора вызывает сообщение Symbol x не может
30
ответ дан 15 August 2018 в 16:53
  • 1
    Разве ваш ответ не является специфическим для визуальной студии? В вопросе не указаны никакие инструменты IDE / компилятора, поэтому он делает ваш ответ бесполезным для части, не являющейся визуальной студией. – Victor Polevoy 13 August 2015 в 12:23
  • 2
    Ты прав . Но каждый процесс IDE в компиляции / компоновке выполняется немного по-другому. Но файлы обрабатываются точно так же (даже g ++ делает то же самое при анализе флагов ..) – user 13 August 2015 в 16:01
  • 3
    Проблема не в том, что касается IDE, а об ответе на проблемы с связыванием. Проблемы с связыванием не связаны с IDE, а с процессом компиляции и сборки. – Victor Polevoy 13 August 2015 в 16:02
  • 4
    Да. Процесс сборки / компоновки выполняется в g ++ / Visual Studio (компилятор, предоставленный Microsoft для VS) / Eclipse / Net Beans таким же образом – user 13 August 2015 в 16:06

Когда ваши пути include разные

Ошибки компоновщика могут произойти, если заголовочный файл и связанная с ним общая библиотека (файл .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 (возможно, файлы include из двух разных версий библиотеки).

Отладка компоновщика

DUMPBIN - ваш друг, если вы используете Visual Studio. Я уверен, что у других компиляторов есть другие подобные инструменты.

Процесс выглядит следующим образом:

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

[1] В проекте я имею в виду набор исходных файлов, которые связаны друг с другом для создания библиотеки или исполняемого файла.

РЕДАКТИРОВАТЬ 1: переписать первый раздел, который будет легче понять , Пожалуйста, прокомментируйте ниже, чтобы сообщить мне, нужно ли что-то еще исправлять. Спасибо!

11
ответ дан 15 August 2018 в 16:53

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

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

INPUT (libtbb.so.2)

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

cp libtbb.so.2 libtbb.so

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

17
ответ дан 15 August 2018 в 16:53

Если все остальное не удается, перекомпилируйте.

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

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

59
ответ дан 15 August 2018 в 16:53
  • 1
    Правильно - это происходит, когда библиотеки имеют циклическую зависимость. – Luchian Grigore 4 December 2013 в 02:51
  • 2
    Я расширил ваш ответ и связал его в главном. Благодарю. – Luchian Grigore 4 December 2013 в 02:56

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

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

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

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

31
ответ дан 15 August 2018 в 16:53
  • 1
    Это кажется чрезмерно специфичным - возможно, новый поток будет лучшим местом для этого ответа. – Luchian Grigore 17 January 2015 в 17:56
  • 2
    @LuchianGrigore: Я действительно хотел опубликовать здесь , поскольку этот вопрос был именно такой проблемой, но он был отмечен как дубликат этого вопроса, поэтому я не мог ответить на него. Поэтому я написал здесь свой ответ. – Malvineous 17 January 2015 в 18:00
  • 3
    Этот вопрос уже имеет принятый ответ. Он отмечен как дубликат, потому что общая причина указана выше. Что произойдет, если бы у нас был ответ на каждый вопрос с библиотекой, которая не включена? – Luchian Grigore 17 January 2015 в 18:29
  • 4
    @LuchianGrigore: эта проблема не специфична для библиотеки, она затрагивает все библиотеки, используя систему управления пакетами Visual Studio. Мне просто удалось найти другой вопрос, потому что у нас обоих были проблемы с libpng. У меня также была та же проблема (с тем же решением) для libxml2, libiconv и glew. Этот вопрос касается проблемы с системой управления пакетами Visual Studio, и мой ответ объясняет причину и обеспечивает обходное решение. Кто-то просто видел «нерешенные внешние» и предположил, что это была стандартная проблема с компоновщиком, когда это проблема управления пакетами. – Malvineous 17 January 2015 в 18:40

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

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

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

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

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

Для gcc : добавить пути поиска заголовка пользователя -> добавить путь поиска библиотеки -> перетащить и удалить ссылку на фактическую библиотеку в папку проекта.

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

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

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

99
ответ дан 15 August 2018 в 16:53

Объявлено, но не определило переменную или функцию.

Типичным объявлением переменной является

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)

Другие примеры несоответствий включают

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

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

92
ответ дан 15 August 2018 в 16:53

Очистить и перестроить

«Чистая» сборка может удалить «мертвую древесину», которая может быть оставлена ​​лежащей рядом с предыдущими сборками, неудачными сборками, неполными сборками и другими проблемами сборки сборки.

В общем случае среда IDE или сборка будет включать в себя некоторую форму «чистой» функции, но это может быть неправильно настроено (например, в ручном make-файле) или может завершиться неудачей (например, промежуточные или результирующие двоичные файлы - только).

После завершения «очистки» убедитесь, что «чистый» преуспел, и весь сгенерированный промежуточный файл (например, автоматический файл makefile) был успешно удален.

Этот процесс можно рассматривать как окончательный вариант, но часто является хорошим первым шагом; особенно если недавно был добавлен код, связанный с ошибкой (локально или из исходного репозитория).

61
ответ дан 15 August 2018 в 16:53
  • 1
    Чтобы быть полным, в этом ответе должны упоминаться файлы GCC visibility и Windows .def, так как они также влияют на имя и присутствие символа. – rubenvb 23 December 2012 в 21:39
  • 2
    @rubenvb Я не использовал файлы .def в возрасте. Не стесняйтесь добавлять ответ или редактировать его. – Luchian Grigore 29 December 2012 в 01:14
  • 3
    Или, наоборот, если вы разрабатываете библиотеку C, хорошим правилом является защита файлов заголовков путем окружения всех экспортированных объявлений с помощью #ifdef __cplusplus [\n] extern"C" { [\n] #endif и #ifdef __cplusplus [\n] } [\n] #endif ([\n], являющихся реальным возвратом каретки, но я не могу записать это правильно в комментариях). – Bentoy13 8 July 2015 в 17:37
  • 4
    Как и в приведенном выше комментарии, раздел «Создание разделов для смешанных языков» помог: oracle.com/technetwork/articles/servers-storage-dev/… – zanbri 23 November 2016 в 17:58

Связанный .lib-файл связан с .dll

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

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

23
ответ дан 15 August 2018 в 16:53

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

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

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

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

18
ответ дан 15 August 2018 в 16:53

Члены класса:

Для деструктора 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 без определения, с добавленными рассуждениями, которые генерирует чистая декларация dummy 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;
};

Non- virtual

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

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-использование этого элемента по-прежнему потребует определения области пространства имен, как описано выше. C ++ 11 позволяет инициализировать внутри класса для всех членов static const данных.

149
ответ дан 15 August 2018 в 16:53
  • 1
    Последняя строка этого ответа неверна, объявление в классе никогда не является определением. (Определения не нужны для статических членов, которые не используются odr, что является общим для интегральных констант времени компиляции) – Ben Voigt 12 August 2014 в 22:55
  • 2
    Не уверен, что эта часть - C ++ 11 позволяет инициализировать внутри класса для всех членов static const данных . [class.static.data] / 3 говорит, что вам нужно отметить статические члены данных constexpr, если они не являются целыми или перечисляемыми. – Praetorian 9 September 2014 в 06:08
  • 3
    Нет необходимости определять какую-либо не виртуальную функцию, которую вы никогда не используете. Кроме того, нет необходимости определять какую-либо виртуальную функцию, если вы никогда не строите объект класса и не вызываете его из производного класса, который вы фактически создаете. Кроме того, можно определить все чистые виртуальные функции. – Deduplicator 21 September 2014 в 00:06
  • 4
    @Deduplicator & quot; необходимость & quot; vs. "должен". Педантично необходимо определить нечистые виртуальные функции (хотя, как уже упоминалось, некоторые компиляторы не будут жаловаться до тех пор, пока вы их не назовете, но некоторые будут). Не думаю, что я сказал, что вы не можете определить чистые виртуальные. – Luchian Grigore 21 September 2014 в 00:09
  • 5
    @Deduplicator см. & Quot; Виртуальная функция, объявленная в классе, должна быть определена или объявлена ​​чистой (10.4) в этом классе или и то, и другое; но диагностика не требуется & quot; (10.3 Виртуальные функции) - если это не было изменено в C ++ 14 – Luchian Grigore 21 September 2014 в 00:13

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

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

Итак, чтобы повторить еще раз, порядок MUST имеет значение!

74
ответ дан 15 August 2018 в 16:53
  • 1
    Мне любопытно, что в моем случае у меня был один объектный файл, который зависит от общей библиотеки. Мне пришлось изменить Makefile и поместить библиотеку ПОСЛЕ объекта с gcc 4.8.4 на Debian. На Centos 6.5 с gcc 4.4 Makefile работал без проблем. – Marco Sulla 17 September 2016 в 19:28
  • 2
    -Wl, - start-group .....- Wl, - end-group решает эту проблему. – user2672165 29 December 2017 в 12:14

Это одно из самых запутанных сообщений об ошибках, которые каждый программист VC ++ видел снова и снова. Давайте сначала сделаем чёткость.

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

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

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

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

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

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

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, так как определение не существует в текущей области поиска.

48
ответ дан 15 August 2018 в 16:53

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

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

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

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

33
ответ дан 15 August 2018 в 16:53
  • 1
    Невозможно не указать на этот вопрос и тот факт, что это чаще всего вызвано отсутствием основной функции, а не наличием WinMain. Для действительных программ на C ++ требуется main. – chris 11 March 2014 в 09:46

Другие вопросы по тегам:

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