В C и C ++, если я хочу использовать глобальную переменную в других модулях компиляции, я определю переменную следующим образом:
int g_myVal = 0;
Это выделяет память для int
,
Затем в заголовочном файле я объявляю переменную:
extern int g_myVal;
Это сообщает компилятору, что указанный символ существует в каком-то другом модуле компиляции. Затем компоновщик должен разрешить символ.
Однако, если я хочу, чтобы переменная была доступна со связью «C», я должен определить переменную (выделить хранилище), например:
extern "C" int g_myVal = 0;
Так как же тогда отличить выделение памяти от простого информирования компилятора о том, что указанный символ существует в другом модуле компиляции?
Ваша путаница проистекает из того факта, что extern
а также extern "C"
сделать две разные вещи.
extern
extern
сам по себе является спецификатор класса хранения:
[C++11: 7.1.1/6]:
extern
Спецификатор может применяться только к именам переменных и функций.extern
спецификатор нельзя использовать в объявлении членов класса или параметров функции. Для связи имени, объявленного сextern
спецификатор, см. 3.5. [ Заметка:extern
Ключевое слово также может быть использовано в Явная-конкретизация а также сцепления-спецификации, но это не хранения класса спецификатор в таких контекстах. —Конечная записка]
extern "C"
Как намекает на это заключительное примечание, существует другой контекст, в котором вы можете использовать extern
Ключевое слово, и это как спецификатор связи:
[C++11: 7.5/2]:
Связь (3.5) между фрагментами кода C ++ и не-C ++ может быть достигнута с помощью Связь-спецификация:Связь-спецификации:
extern
Строка литерала{
Заявление-слвыбирать}
extern
строково-буквенное объявление
C ++ любит повторно использовать ключевые слова.
Теперь по умолчанию переменная, помеченная спецификатором связи, является декларация а не определение, так что в этом смысле, как будто вы также использовали другой смысл extern
:
[C++11: 7.5/7]:
Декларация, непосредственно содержащаяся в Связь-спецификация рассматривается как если бы он содержалextern
спецификатор (7.1.1) с целью определения связи объявленного имени и определения, является ли оно определением. Такое объявление не должно указывать класс хранения. [ Пример:extern "C" double f(); static double f(); // error extern "C" int i; // declaration extern "C" { int i; // definition } extern "C" static void g(); // error
— конец примера]
Как вы можете видеть в приведенном выше примере, все еще возможно или объявление или определение, когда вы используете спецификатор связи.
Вот еще один пример этого:
// Everything in this block has C linkage
extern "C" {
// Declaration of g_myVal
extern int g_myVal;
// Definition of g_myVal2
int g_myVal2;
}
int main()
{
g_myVal2 = 5; // ok
g_myVal = 6; // not okay - linker error, symbol not found
}
Это все, как говорится, лечение g_myVal
как декларация является отменено с помощью использования инициализатора, который заставляет заявление быть определением:
[C++11: 7/8]:
Синтаксические компоненты помимо тех, которые находятся в общем виде объявления, добавляются к объявлению функции для определения функции. Объявление объекта, однако, также является определением, если оно не содержитextern
спецификатор и не имеет инициализатора (3.1). Определение заставляет зарезервировать соответствующий объем памяти и выполнить любую соответствующую инициализацию (8.5).
Я надеюсь, что это проясняет, что происходит в вашем коде с этими различными значениями extern
,
В этом случае ставим инициализатор = 0
изменил объявление на определение, поэтому здесь выделено хранилище. (То же самое случилось бы даже с просто extern
.)