Ошибка повторяющегося символа, связанная с объявлением const char * [] - PullRequest
7 голосов
/ 22 июня 2010

Мне бы очень хотелось помочь в диагностике источника ошибки дублирующегося символа, которую я получаю, когда пытаюсь скомпилировать с g ++ 4.2.1.

Конкретная ошибка:

ld: duplicate symbol _SOCIODEM_FILENAMES in /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//ccP3yVgF.o and /var/folders/c+/c+eq1Qz1Feye7vxs5mQOUE+++TI/-Tmp-//cc1NqtRL.o 
collect2: ld returned 1 exit status

Ошибка возникает, только когда я включаю это объявление в файл с именем Parameters.h:

// Parameters.h

#ifndef PARAMETERS_H
#define PARAMETERS_H

// ...[code snipped]...
const int NUM_SOCIODEM_FILES = 5;
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
     "FLEDGE_PDF.txt", 
     "PAIR_PDF.txt", 
     "BIRTH_AGE_PDF.txt",  
     "SPLIT_PDF.txt"  };
// ...[code snipped]...
#endif

Я искал всемои файлы, и это единственное место, где объявлено SOCIODEM_FILENAMES.Когда я закомментирую объявление, ошибка «дубликат символа» исчезнет.

Я не знаком с ошибками компоновщика (если это так) и был бы признателен за помощь в устранении проблемы.Все мои заголовочные файлы имеют #ifndef...#define...#endif оболочки.Моя команда компиляции:

g++ -o a.out -I /Applications/boost_1_42_0/ Host.cpp Simulation.cpp main.cpp Rdraws.cpp

Заранее спасибо.


Краткое описание решения

У меня теперь есть в Parameters.h:

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
                 "FLEDGE_PDF.txt", 
                 "PAIR_PDF.txt", 
                 "BIRTH_AGE_PDF.txt",  
                 "SPLIT_PDF.txt"  };

Все остальные определения и объявления в Parameters.h не изменены.Андрей и другие комментаторы суммируют альтернативный подход, используя extern, что является излишним для моих целей.

Ответы [ 5 ]

14 голосов
/ 22 июня 2010

По какой-то причине ни один из ответов до сих пор не удосужился объяснить разницу между вашим целочисленным NUM_SOCIODEM_FILES объектом и массивом SOCIODEM_FILENAMES объектом.Последний вызывает ошибку компоновщика по уже объясненным причинам: потому что вы включаете заголовочный файл в несколько файлов реализации.Тем не менее, первое связывалось бы без каких-либо проблем (потому что действительно нет проблем с объявлением NUM_SOCIODEM_FILES).Почему?

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

static const int NUM_SOCIODEM_FILES = 5; /* internal linkage */

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

В то же время ваш SOCIODEM_FILENAMES не являетсяобъявленная константа, поэтому по умолчанию она получает внешнюю связь и в конечном итоге приводит к ошибкам компоновщика.Но если вы также объявите свой SOCIODEM_FILENAMES как const, проблема исчезнет

const char * const SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = {
  ...

Обратите внимание, где в объявлении помещается дополнительный const.Если вы просто добавите этот дополнительный const и оставите все остальное как есть (т.е. сохраните определение, если SOCIODEM_FILENAMES в заголовочном файле), компоновщик не сообщит об ошибке, даже если вы включите свой заголовочный файл в несколько блоков перевода.

Это не рекомендуемый подход, так как таким образом вы дадите свою внутреннюю связь SOCIODEM_FILENAMES и получите автономную копию массива SOCIODEM_FILENAMES в каждой единице перевода - что-то, что может работать нормально, но все же делаеточень мало смысла.Поэтому для вашего массива обычно лучше использовать подход extern, рекомендованный в других ответах.

Однако учтите, что обычно вы не должны делать это для объявления NUM_SOCIODEM_FILES !!!Это нормально, как это определено в заголовочном файле.Если вы не пытаетесь сделать что-то необычное, скалярные константы обычно должны быть определены как с инициализатором в заголовочных файлах - таким образом, их можно рассматривать как константы времени компиляции во всех единицах перевода, что довольно ценноиметь.Итак, остерегайтесь странного совета, присутствующего в некоторых других ответах, чтобы перенести определение NUM_SOCIODEM_FILES в .cpp файл - это на самом деле не имеет никакого смысла и является абсолютно неправильной вещью.

7 голосов
/ 22 июня 2010

Скорее всего, вы #include используете этот файл в нескольких исходных файлах.Проблема в том, что каждое включение приводит к отдельному определению переменной с именем SOCIODEM_FILENAMES.Включить охранников не поможет с этим.Включение защиты предотвращает множественные декларации в пределах одной единицы компиляции;они не предотвращают множественные определения для нескольких модулей компиляции.

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

// Parameters.h

#ifndef PARAMETERS_H
#define PARAMETERS_H

// ...[code snipped]...
extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
// ...[code snipped]...
#endif

, а затем:

// Parameters.cpp (or some other source file)

const int NUM_SOCIODEM_FILES = 5;    
const char * SOCIODEM_FILENAMES[ NUM_SOCIODEM_FILES ] = { "LSPAN_PDF.txt", 
                 "FLEDGE_PDF.txt", 
                 "PAIR_PDF.txt", 
                 "BIRTH_AGE_PDF.txt",  
                 "SPLIT_PDF.txt"  };

Вы можете не делать этого для int, потому что это постоянное целое число, и поэтому компилятор может просто обработать егокак константа времени компиляции, и она никогда не будет отображаться в скомпилированном коде.Однако char* не может быть обработан таким образом, и поэтому должен иметь ровно одно определение (известное как «правило одного определения» в C ++).

2 голосов
/ 22 июня 2010

Как предлагали другие, один из способов сделать это - объявить NUM_SOCIODEM_FILES и SOCIODEM_FILENAMES как extern и определить их один раз во внешнем файле.Другой способ - объявить их как static - это приведет к их дублированию в каждом объектном файле, который содержит заголовок, но не приведет к ошибке, поскольку определение является частным для этого объектного файла.Какой вариант вы выберете, полностью зависит от ваших собственных предпочтений.

2 голосов
/ 22 июня 2010

Проблема в том, что вы помещаете определение в заголовочный файл. Если вы включите этот файл в несколько модулей компиляции (файл .cpp), вы фактически создадите несколько определений, и во время компоновки вы получите эту ошибку.

Вам нужно поместить оба этих определения в файл .cpp и поместить only объявление в заголовочный файл:

extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
2 голосов
/ 22 июня 2010

Защита заголовка (#ifndef..#endif оболочка) просто не позволяет включать один и тот же заголовок несколько раз в один исходный файл. Вы по-прежнему можете иметь несколько исходных файлов с этим заголовком, и каждый из них будет объявлять этот символ отдельно. Поскольку все они имеют одинаковые имена, связывание этих источников приведет к конфликту имен символов. Вы, вероятно, хотите объявить символ в исходном файле вместо файла заголовка

...