2009-11-25 4 views
1

я получил два модуля (компиляции единиц), как с использованием переменного модуля с тем же именем:С: Инициализировать переменный модуль

FileA.c и
FileB.c оба содержит:

#includes 

int m_Test; 

// Functions 

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

int m_Test = 0; 

я получаю (с помощью VS2008) события е rror LNK2005: m_Test already defined in ...

У меня, вероятно, есть проблема, чтобы понять, что я делаю :) Что происходит, когда я пытаюсь инициализировать переменную модуля, как здесь? Я не мог найти информацию об этом (google, faq, newsgroup).

Спасибо!

+4

В ответах на этот вопрос можно найти подробные объяснения: http://stackoverflow.com/questions/1490693/tentative-definitions-in-c99-and-linking –

+1

См. Также http://stackoverflow.com/questions/1787875/question-on-extern-specifier-in-c –

ответ

7

Когда вы объявляете свою переменную как в своем исходном сообщении, переменная получает внешнюю связь (с нашей без инициализатора - не имеет значения). Это означает, что переменные не являются независимыми. В обоих объявлениях имена относятся к одному и тому же объекту. Фактически, у вас есть несколько определений одного и того же объекта с внешней связью. Это является нарушением правил определения языка C, т. Е. Это то, что мы обычно называем ошибкой . Обратите внимание, что это ошибка в любом случае: с инициализатором или без него.

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

Я могу на самом деле догадаться, что такое заблуждение. Видите ли, язык C имеет интересную особенность (нет в C++, BTW) под названием Предварительные определения. Эта функция говорит, что если вы объявляете переменную, как это, без инициализаторе

int m_Test; /* no initializer */ 

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

int m_Test; 
int m_Test; /* OK in C */ 
int m_Test; /* OK in C */ 

(заметьте, опять-таки, что это было бы незаконным в C++). Если в какой-то момент вы обеспечиваете без предварительного определения для этой переменной

int m_Test = 0; /* non-tentative */ 

компилятор «сливаться» все эти предыдущие предварительные определения с этим.Но если вы не дают без предварительного определения, компилятор будет генерировать для вас неявно, и это будет эквивалентно

int m_Test = 0; /* implicit definition generated by the compiler */ 

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

int m_Test; /* tentative */ 

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

Однако используемый вами компилятор должен относиться к правилам условного определения более дорогостоящим и позволяет что-то вроде «предварительных определений в нескольких единицах перевода». Вот почему вы не получаете ошибку в своем первом случае. Однако это всего лишь причуда вашего компилятора. Он по-прежнему является незаконным в C.

P.S. Как было отмечено в одной из связанных записей, это поведение является непереносимым «общим расширением» компиляторов C (даже упоминается в информационном разделе языкового стандарта). То есть некоторые компиляторы, как известно, допускают множественные объявления внешних объектов, если не более одного из них содержит инициализатор.

P.P.S. Конечно, если вы хотите, чтобы иметь indepedent переменные в разных единицах перевода, вы должны объявить их как static, как уже отмечалось другими.

3

Вместо определения его в двух * .c файлов, вы должны использовать:

extern int m_Test; 

Используйте это в каждом * .c файле, за исключением одного, или, предпочтительно, поставить эту Экстерн декларацию в заголовочном файле (который затем можно включить в любое количество * .c файлов).

только один файл должен содержать не-Экстерн определение:

int m_Test = 0; 

выше хороший совет, если (если) вы хотите такой же переменной используется во всех * .c файлов: так что, например, если вы измените его значение в одном файле, то измененное значение будет видно, когда вы прочитаете его из той же переменной в другом файле.

Если в качестве альтернативы вам нужны несколько независимых экземпляров переменной (где каждый файл .c имеет свою собственную независимую копию переменной), тогда определите каждую из них как «статическую» переменную, например. определить следующее в каждом из нескольких * .c файлов:

static int m_Test = 0; 

Статический подход работает, может вы объяснить, что происходит, когда я добавляю = 0 к определению, может быть?

Добавление = 0 к определению будет безвредна: это разрешено инициализировать переменную при определении его, и когда вы инициализировать статическую переменную переменная остается статичным (то есть до сих пор «внутренней связи» и ISN» t видно в других модулях).

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

+0

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

1

Вы случайно включаете внешнее определение переменной?IE, в том числе:

extern int m_Test; 

От FileA в FileB или наоборот?

Проверьте файлы заголовков и убедитесь, что в них нет ничего подобного.

+0

Нет, они определены только в файлах .c - не используются заголовки, внешние. Я уже проверил свои заголовки. Спасибо за предложение, это была моя первая идея. – tanascius

+0

Да, я полагал, что вы, вероятно, это сделали бы. Это единственная причина, по которой я могу придумать эту ошибку. Я буду размышлять. –

5

Если вы хотите, чтобы переменные были независимыми, объявите их static.

static int m_Test; 

означает, что переменная m_Test видна только в том, что файл-сферу, и не доступны для других файлов или модулей.

+0

Это правильное решение. Можете ли вы объяснить, что происходит, когда я добавляю '= 0' в определение, может быть? – tanascius

+0

@tanascius: если вы не инициализируете значение, множественные «определения» на самом деле являются просто совместимыми объявлениями, из которых вы можете иметь сколько угодно; когда вы добавляете инициализатор, вы форсируете определение, из которого вы можете иметь только один – Christoph

+0

@ Кристоф: Noy true. То, что вы говорите, было бы правдой, если бы объявление включало спецификатор extern. Без 'extern' это определение, с инициализатором или без него. – AnT

1

По умолчанию переменные в области файлов: extern; см C99 спецификации, 6.2.2 § 5:

Если декларация идентификатора для функции не имеет хранения класса спецификатор, его связи определяется точно так, как если бы она была объявлена ​​с Storage- спецификатор класса extern. Если объявление идентификатора для , объект имеет размер файла и не имеет значения спецификатор класса хранения, его связь является внешней.

Если вы хотите, чтобы ваши переменные были разными, добавьте спецификатор static.