Понимание происхождения дубликата символьной ошибки компоновщика

17

У меня есть c ++-программа, скомпилированная ранее, но после того, как вы удалили Jamfiles, программа больше не компилировалась, а ld выбрали duplicate symbol error . Это продолжалось после последовательного возврата к исходным файлам Jamfiles, запуская bjam clean , удаляя объекты вручную и переключаясь с clang с передним концом gcc на gcc 4.2.1 на MacOs 10.6.7.

Упрощенное описание программы - это main.cpp и четыре файла, a.h,cpp и b.h,cpp , которые скомпилированы в статическую библиотеку, связанную с main.o . Оба, main.cpp и b.cpp зависят от файла, содержащего оскорбительный символ, off.h , через два разных промежуточных файла, но ни a.h , ни a.cpp никак не зависят от off.h .

Прежде чем вы спросите, я убедился, что все файлы были обернуты множеством определений ( #ifndef , #define , #endif ), и, хотя я нашел файл, который их пропускал, он не ссылался на% код%. Что еще более важно, off.h не содержит ничего, что ссылается на b.h , только реализация, off.h , ссылается на b.cpp . Это только меня озадачило.

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

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

    
задан rcollyer 24.05.2011 в 22:04
источник
  • Вы ожидаете, что нам удастся диагностировать ошибку, которую вы не можете воспроизвести самостоятельно? –  Neil Butterworth 24.05.2011 в 22:07
  • @Neil, а не как таковой. Примечание. Я смог воспроизвести эту проблему для многочисленных попыток компиляции, пока не наткнулся на «решение». И я ожидаю, что ответы будут более теоретическими, чем конкретные из-за проблемы, которая не появится снова. Тем не менее, я пытаюсь лучше понять, что может привести к конфликту символа, упомянутого в двух отдельных единицах компиляции, когда конфликт не существует. И какие, если вообще есть, дополнительные шаги, которые я могу предпринять, чтобы самостоятельно диагностировать и смягчать проблему. –  rcollyer 24.05.2011 в 22:18
  • Я бы дал 90% вероятность, что вы фактически не очистили все объектные файлы (может быть, libtool или что-то скрывало их в скрытом каталоге?). Тем не менее, если вы не можете воспроизвести его, нет никакого способа разобраться в этом. –  bdonlan 24.05.2011 в 22:41
  • @bdonlan, интересная идея, и было бы довольно неприятно, если бы libtool выполнял такое кэширование. Предположительно, через bjam clean или rm * .o * .a все объекты и библиотеки должны быть удалены. Если нет, то после перекомпиляции они должны были быть перезаписаны. Кроме того, bjam по умолчанию компилируется в разных каталогах при изменении компиляторов, поэтому любое кэширование с помощью libtool, или других, сделает такое разделение бессмысленным. Кэширование такого рода, по моему мнению, затрудняет создание надежных построений, что делает его довольно оскорбительным. –  rcollyer 24.05.2011 в 23:33
  • @bdonlan, что касается вашего мнения о неприводимости, как бывшего тестера, эти проблемы были самыми расстраивающими как для тестировщика, так и для разработчика, и из-за этого они часто отвергались. Тем не менее, я думаю, что есть заслуга в вопросах: как я мог попасть в эту ситуацию, несмотря на принятые меры предосторожности? И однажды в этой ситуации, что можно сделать, чтобы выбраться из этого? Несмотря на отсутствие конкретного исправления, эти вопросы раскрывают потенциальные проблемы в процессе сборки / языке, которые могут быть непонятными для начинающих / промежуточных программистов. –  rcollyer 24.05.2011 в 23:41
Показать остальные комментарии

1 ответ

39

Это часто является результатом определения объекта в файле заголовка, а не просто его объявления. Рассмотрим:

h.h :

#ifndef H_H_
#define H_H_
int i;
#endif

a.cpp :

#include "h.h"

b.cpp :

#include "h.h"
int main() {}

Это приведет к дублированию символа i . Решение состоит в том, чтобы объявить объект в файле заголовка: extern int i; и определить его в одном из файлов исходного кода: int i; .

    
ответ дан Robᵩ 24.05.2011 в 22:40
источник
  • символ был функцией, объявленной внутри строки в заголовке, но не содержал ключевое слово inline, которое это может потребоваться. Но мой вопрос о вашем решении - почему int i в объектном файле a.o отображается так, что он конфликтует с тем, который найден в b.o? Насколько я понял, это не должно происходить. Что мне не хватает? –  rcollyer 24.05.2011 в 23:22
  • 1) Имена, объявленные в области файлов, имеют внешнюю связь. 2) Объект может быть объявлен в нескольких переводах, но должен быть определен ровно один раз. –  Robᵩ 24.05.2011 в 23:39
  • , чем больше я думаю об этом, тем больше похоже, что это может быть причиной. Однако, если я не могу повторить это, я останусь сбитым с толку, почему он скомпилирован один день, но не следующий. –  rcollyer 01.06.2011 в 05:29
  • @ Robᵩ: Можете ли вы предложить некоторое решение для объявлений классов в заголовочном файле? –  doptimusprime 13.03.2013 в 11:17
  • @ 9000 - вы можете # включить тот же заголовок в два файла, если у него нет строки типа int i; или int i = 17 ;. Он должен иметь только объявления, а не определения - extern int i; это нормально. –  Robᵩ 26.03.2014 в 03:00
Показать остальные комментарии