Почему non-extern может быть в .h файлах на C / C ++? - PullRequest
1 голос
/ 23 марта 2011

Возьмите этот файл , например, есть много внешних структур, таких как:

struct list_head source_list;

Как это может работать, если этот заголовочный файл включен более чем в одну единицу компиляции?

Должно быть сообщение об ошибке, что один и тот же символ определяется дважды, верно?

Ответы [ 4 ]

2 голосов
/ 23 марта 2011

Технически, так должно быть, но такое использование было в течение многих лет и его невозможно искоренить (его пытались; время от времени некоторые поставщики решают сделать это ошибкой и возвращаются после первых сотен или около того сообщений об ошибках).Педантически файл .h должен объявить его extern, а файл один .c / .cpp должен его определить.

Вкратце, когда вы не указываете связь (static, extern и т. Д.) Переменной верхнего уровня, она объявлена ​​как "общая".Во время ссылки, если все ссылки на эту переменную имеют одинаковый размер (и тип, если он доступен), тогда она выделяется один раз, и все ссылки делаются для указания на нее.Если компоновщик находит разные размеры / типы / связи для одной и той же переменной, он выдает ошибку.

РЕДАКТИРОВАТЬ: это явно смущает людей.Здесь:

jinx:1714 Z$ cat foo.h
int foo;
extern void bar();
jinx:1715 Z$ cat foo.c
#include "foo.h"

int
main(int argc, char **argv)
{
  bar();
  return 0;
}
jinx:1716 Z$ cat bar.c
#include "foo.h"

void
bar(void)
{
  return;
}
jinx:1717 Z$ gcc -Wall foo.c bar.c -o foo
jinx:1718 Z$ ./foo
jinx:1719 Z$ _

Обратите внимание на полное отсутствие ошибок при многократном определении int foo. Это - это то, что я пытался сказать.

1 голос
/ 23 марта 2011

Термин для этого является «предварительным определением»:

Объявление идентификатора для объекта, который имеет область действия файла без инициализатора, без спецификатора класса хранения или сСпецификатор класса static, представляет собой предварительное определение.Если модуль перевода содержит одно или несколько предварительных определений для идентификатора, а модуль перевода не содержит внешнего определения для этого идентификатора, то поведение точно такое, как если бы модуль перевода содержал объявление области файла для этого идентификатора с составным типом какконца блока перевода, с инициализатором, равным 0.

Так что это хорошо определено в C (но часто осуждается).

1 голос
/ 23 марта 2011

Эти struct list_head source_list; поля объявлены внутри других структур, поэтому они не являются символами.

Объявления других структур (верхнего уровня) имеют разные имена, так что это тоже нормально.

редактировать

Обратите внимание, что все переменные в этом заголовке действительно помечены extern.

0 голосов
/ 23 марта 2011

Должно быть действительно extern. Однако нет явного определения этой переменной, поэтому компилятор помечает ее как внешнюю для вас.

Вы получите ошибку компоновщика, если у вас будет

struct list_head source_list = { 0 };

... так как определяет , определяя символ один раз на единицу перевода (и поэтому компоновщик жалуется).

...