#c #c #unix #compilation
#c #c #unix #Сборник
Вопрос:
Большинство людей, работающих в UNIX, часто сталкиваются с этой раздражающей ошибкой. и в некоторых случаях для решения потребуется меньше времени, а иногда это займет чертовски много времени.
Даже я сталкивался с этим регулярно, и мне нужен какой-нибудь хороший документ или статья, касающиеся конкретной ошибки в c / c
каковы все случаи, когда может быть ошибка «Символ не найден» / «Неопределенный символ».
Кто-нибудь может помочь мне узнать, что это за все эти случаи?
Комментарии:
1. Ошибки компоновщика при выполнении компиляции
Ответ №1:
Ошибка связана не с UNIX / Windows / любой другой ОС, а скорее с самими языками. На самом деле диагностировать его довольно просто с помощью информации, предоставляемой компиляторами. Обычно они сообщают вам, какой символ отсутствует, а иногда и где он используется. Основными причинами отсутствия символа являются:
- Вы объявили, но так и не определили его
- Вы определили его, но не добавили скомпилированный символ (объектный файл / библиотеку) в компоновщик
- Он внешний, и вы забыли связать библиотеку, или вы связываете недопустимую версию, или в неправильном порядке.
Первый вариант немного сложнее, если вы намеревались определить символ, но не соответствовали объявлению (объявленный void foo( int, double );
, но определенный void foo( double, int )
. Как и во всех других случаях, компилятор сообщит вам точную сигнатуру, которую он ищет, убедитесь, что вы определили этот символ, а не что-то близкое или похожее, особый угловой случай может быть, если вы используете разные соглашения о вызовах в объявлении и определении, поскольку они будут выглядеть очень похожими в коде.
В случае внешнего кода библиотек сложность заключается в определении того, какую библиотеку необходимо связать для добавления этого символа, и это следует из документации библиотеки. Помните, что в статических библиотеках порядок библиотек в командной строке компоновщика влияет на результат.
Чтобы помочь вам определить, какие символы на самом деле определены, вы можете использовать nm
(gcc, который распространен в системах unix). Таким образом, в принципе, вы можете запустить nm
с объектными файлами / библиотеками, которые вы связываете, и выполнить поиск символа, на который жалуется компоновщик. Это поможет в случаях, когда порядок — это то, что имеет значение (т. Е. Символ есть, но компоновщик пропустил его).
Во время выполнения (спасибо Matthieu M. за указание на это) у вас могут возникнуть аналогичные проблемы с динамическими библиотеками, если в LD_LIBRARY_PATH найдена неправильная версия библиотеки, вы можете получить библиотеку, в которой нет требуемого символа.
Комментарии:
1. И, конечно, небольшая проблема, вы объявили зависимость во время соединения, но, к сожалению,
LD_LIBRARY_PATH
это не так, как вы могли ожидать, и в конечном итоге (во время загрузки) вы связываетесь с другой версией библиотеки, которая не определяет символы, которые вы ожидали (например, проблемы с совместимостью двоичных файлов).2. @Matthieu M. Хороший вопрос, я думал только во время компиляции.
3. Я использую только библиотеки DLL, так что для меня это постоянная проблема 🙂
Ответ №2:
Хотя они могут зависеть от платформы, у меня есть несколько «более сложных» примеров некоторых пунктов от Андреаса и Дэвида:
- При работе с разделяемыми библиотеками (.so or.dll ) и привязка к символам, которые не экспортируются (dllimport / dllexport в Windows и видимость («по умолчанию») с GCC в * nix)
- Или аналогичный: привязка к статической библиотеке, ожидая общую библиотеку или наоборот. Это немного похоже на комментарий Матье о связывании с другой, неожиданной версией библиотеки.
- Создание чисто виртуальных классов и отсутствие реализации хотя бы для одного метода (в результате чего недоступна vtable).
- На самом деле более сложный случай объявления, но не определения: ошибки связывания, которые вы можете получить при работе с большими вложенными шаблонами. Выяснить, что не было определено, может быть сложно из-за больших сообщений об ошибках.
Ответ №3:
В большинстве случаев, когда вы получаете сообщение о том, что символ не найден / неопределенный символ или иногда даже ошибку «дублирующий символ», они обычно связаны с тем фактом, что компоновщику не удается найти символ в проекте, который вы пытаетесь создать.
Лучший способ сделать это — посмотреть сгенерированный файл карты или таблицу символов, которая является результатом компилятора. Это может выглядеть примерно так:
Это позволит вам увидеть, присутствует символ или нет. Также могут возникнуть другие тайные проблемы, такие как оптимизация компилятора, которые могут вызвать дублирование символа, особенно при встроенной сборке. Их обнаружить сложнее всего.
Что касается хороших ресурсов и материалов, у меня не так много хороших ссылок. Когда я тогда поспрашивал, большинство старших инженеров действительно извлекли уроки из собственного опыта.
Но я уверен, что именно там присутствуют форумы, подобные этим, которые помогут нам ускорить приобретение таких знаний.
Надеюсь, это помогло 🙂
Приветствия!
Комментарии:
1. Я приношу свои извинения, но я пытался загрузить изображение, чтобы вы могли просмотреть образец, но безрезультатно. Я выбрал небольшой файл jpg для загрузки, но он продолжает отклоняться. Я где-то ошибся?
Ответ №4:
Я предполагаю, что вы имеете в виду ошибку компоновщика. Вот список из верхней части моей головы в том, что я считаю наиболее распространенным:
- Вы забыли сообщить компоновщику о зависимости (например, о LIB-файле).
- У вас есть класс со статическим элементом данных, и вы забыли его инициализировать (только C ).
- Вы объявили функцию, не являющуюся чисто виртуальной, и забыли ее реализовать.
- Вы забыли реализовать функцию, которую вы вызвали из другой функции (только C, C выдаст ошибку компилятора, которую гораздо легче найти).
- Вы объявили внешнюю переменную и забыли ее инициализировать.
- Объявление функции не соответствует реализации (только C , C примет это и может ужасно умереть).
- Вы забыли реализовать функцию, которую объявили и вызвали из другой функции.
Комментарии:
1. если вы забудете реализовать функцию, компилятор C не сообщит вам об этом. Он будет довольствоваться объявлением функции.
2. @MatthieuM.: Ну, есть два маркера, посвященных этому (4-й и последний). Разница между ними небольшая, но важная. В C вы можете вызвать функцию, не объявляя ее сначала. Если вам затем не удастся определить (реализовать) его, компоновщик подаст жалобу. В C вы не можете использовать необъявленную функцию без жалобы компилятора. Об этом и идет речь в пункте 4. Теперь, если вы действительно объявили его, но не определили, компоновщик будет жаловаться как на C, так и на C . Об этом и идет речь в последнем пуле.