Дублирующаяся ошибка символа, связанная с символом константы * [] объявление

Я любил бы справку, диагностируя источник дублирующейся ошибки символа, которую я получаю, когда я пытаюсь скомпилировать с 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 неизменны. Andrey и другие комментаторы суммируют альтернативное использование подхода extern, который является излишеством в моих целях.

7
задан Sarah 22 June 2010 в 16:52
поделиться

5 ответов

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

14
ответ дан 6 December 2019 в 06:35
поделиться

Скорее всего, вы #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++ это известно как "правило одного определения").

7
ответ дан 6 December 2019 в 06:35
поделиться

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

.
2
ответ дан 6 December 2019 в 06:35
поделиться

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

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

extern const int NUM_SOCIODEM_FILES;
extern const char * SOCIODEM_FILENAMES[];
2
ответ дан 6 December 2019 в 06:35
поделиться

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

2
ответ дан 6 December 2019 в 06:35
поделиться
Другие вопросы по тегам:

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