Ответ 1
Вот соответствующие части стандарта. См. Мои объяснения ниже стандартного текста:
§6.9.2/2 Определение внешних объектов
Объявление идентификатора для объекта с областью файлов без инициализатора и без спецификатора класса хранения или с помощью статического элемента класса хранения представляет собой предварительное определение. Если единица перевода содержит одно или несколько предварительных определений для идентификатора, а единица перевода не содержит внешнего определения для этого идентификатора, то поведение в точности совпадает с тем, что единица перевода содержит объявление области файла этого идентификатора, причем составной тип как конца блока перевода, причем инициализатор равен 0.
ISO C99 §6.9/5 Внешние определения
Внешнее определение - это внешнее объявление, которое также является определением функции (отличной от встроенного определения) или объекта. Если идентификатор, объявленный с внешней связью, используется в выражении (отличном от части операнда оператора sizeof, результат которого является целочисленной константой), где-то во всей программе должно быть ровно одно внешнее определение для идентификатора; в противном случае должно быть не более одного.
С версией C глобальные переменные 'g' объединены в один, поэтому в конце дня вы будете иметь только один, который объявляется дважды. Это нормально из-за времени, когда экстерн не нужен или, возможно, не выходил. Следовательно, это связано с историей и соображениями совместимости для создания старого кода. Это расширение gcc для этой устаревшей функции.
В основном это означает, что gcc выделяет память для переменной с именем "a", поэтому может быть более одного объявления, но только одно определение. Вот почему приведенный ниже код не будет работать даже с gcc.
Это также называется предварительным определением. С С++ такого не существует, и это пока оно компилируется. С++ не имеет понятия условной декларации.
Предварительное определение - это любое объявление внешних данных, которое не имеет спецификатора класса хранения и не имеет инициализатора. Предварительное определение становится полным определением, если конец единицы перевода достигнут, и определение не появилось с инициализатором для идентификатора. В этой ситуации компилятор резервирует неинициализированное пространство для определенного объекта.
Обратите внимание, однако, что следующий код не будет компилироваться даже с gcc, потому что это уже предварительное определение/объявление с назначенными значениями:
в файле "a.c/a.cpp"
int a = 1;
в файле "b.c/b.cpp"
int a = 2;
int main() { return 0; }
Пойдем даже дальше этого с дальнейшими примерами. Следующие утверждения показывают нормальные определения и предварительные определения. Обратите внимание: статичность будет немного отличаться, поскольку это область файлов и больше не будет внешней.
int i1 = 10; /* definition, external linkage */
static int i2 = 20; /* definition, internal linkage */
extern int i3 = 30; /* definition, external linkage */
int i4; /* tentative definition, external linkage */
static int i5; /* tentative definition, internal linkage */
int i1; /* valid tentative definition */
int i2; /* not legal, linkage disagreement with previous */
int i3; /* valid tentative definition */
int i4; /* valid tentative definition */
int i5; /* not legal, linkage disagreement with previous */
Более подробную информацию можно найти на следующей странице:
http://c0x.coding-guidelines.com/6.9.2.html
Смотрите также эту запись в блоге для более подробной информации:
http://ninjalj.blogspot.co.uk/2011/10/tentative-definitions-in-c.html