Почему определения структуры имеют внутреннюю связь? - PullRequest
1 голос
/ 15 августа 2011

(я говорю о C, но это также относится к шаблонам классов в C ++)

В заголовочный файл принято помещать все ДЕКЛАРАЦИИ, а не определения.Однако мы обычно помещаем определения структуры или шаблоны классов в заголовочный файл, даже не зная, почему мы можем.Это на самом деле не имеет смысла, потому что они также являются определениями - ПРАВИЛО ОДНОГО ОПРЕДЕЛЕНИЯ.(Да, определения структур и шаблоны классов не вызывают установки хранилища, но вы все равно получаете ошибку «переопределения», ниже которой подразумевается, что они являются определениями.)

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

Единственное, что имеет смысл, - это определения структуры и классшаблоны имеют внутреннюю связь (в отличие от внешней ссылки по умолчанию), но я не могу найти ссылки на нее в K & R или справочном руководстве.На самом деле, структуры даже не упоминаются в связи.

Я хочу знать точную ссылку, где стандарт ANSI указывает на этот феномен.(IMO, это довольно двусмысленная вещь, которую НЕОБХОДИМО упомянуть где-то в стандарте ANSI.)


EDIT Я НЕ спрашиваю, почему определения структуры можно поместить в заголовочный файл.

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

EX) test1.c: int a = 3;test2.c: int a = 4;Вызывает ошибку компиляции из-за переопределения.Однако

test1.c: struct test {int a};test2.c: struct test {int b};Не вызывает ошибки компиляции, и единственная причина, по которой я могу придумать, состоит в том, что определения структуры либо имеют внутреннюю связь, либо вообще не имеют связи.

Ответы [ 5 ]

4 голосов
/ 15 августа 2011

В Си только объекты и функции имеют связь. Поскольку struct в C может не содержать функций или объектов-членов "static", как в C ++, ваш вопрос здесь не имеет большого смысла.

Функции-члены в C ++, если они не определены, а объявлены только внутри struct, не представляют проблем. Если они также определены, они inline. Концепция inline была просто придумана для C ++, чтобы охватить этот случай: функция Definition , которая может быть передана через заголовочный файл в нескольких модулях компиляции. C99, который принял эту концепцию (слегка изменив ее).

static объекты-члены действительно представляют большую проблему. Синтаксис того, как создавать экземпляры этих парней, довольно неясен, особенно для template class es или struct s. Если вы хотите узнать о том, что вам нужно, просите об этом, специально помеченном C ++.

1 голос
/ 15 августа 2011

Структура строки - это просто определение. Определение не отображается за пределами исходного файла.
К вашему сведению: ни один из исходных файлов ничего не экспортирует.

Чтобы проверить это:

$ cat test1.c
struct test { char a; };
$ gcc -o test1.o -c test1.c
$ nm 
$ echo "struct test foo; " >> test1.c
$ gcc -o test1.o -c test1.c
$ nm
0000000000000001 C _foo
0 голосов
/ 15 августа 2011

У меня нет копии окончательной версии, но из n843 проекта спецификации Я вижу:

из 6.7.2.3 Теги : "4 Два объявления типов структуры, объединения или перечисления, которые находятся в разных областях или используют разные теги, объявляют разные типы. Каждое объявление структуры, объединения или перечисления Тип, который не содержит тег, объявляет отдельный тип. "

из 6.2.1 Области идентификаторов : "4 У каждого другого идентификатора есть область действия, определяемая размещением его объявления (в объявителе или спецификаторе типа). Если декларатор или спецификатор типа, который объявляет идентификатор появляется вне любого блока или списка параметров, идентификатор имеет область действия файла , которая заканчивается в конце единицы перевода. [...] "[выделение в оригинале] (Единственный тип идентификатора, который я см. упомянутое в этом разделе до того, как в этом выражении были метки, которые имеют функцию scope.)

Я не эксперт по Си или стандартам, но мне кажется, что это объясняет поведение, которое вы наблюдаете. Таким образом, структура должна иметь коллизию, если область файла одна и та же, а структура имеет дублирующий тег, потому что наличие отдельного тега потребуется для того, чтобы сделать их различными типами. Но если они находятся в другой области файлов, проблем не возникает, поскольку, если они находятся в разных областях, выполняются требования для отдельного типа.

0 голосов
/ 15 августа 2011

Я думаю, что вы немного смешиваетесь здесь, вы можете поместить все что угодно в заголовочный файл.

Обычно в них помещаются объявления перечислений, typedefs, структур и прототипов функций, поэтому различные C-файлы могут компилироваться без необходимости знать реальную функцию или реальную память (структура в основном является определением того, как память выложил)

0 голосов
/ 15 августа 2011

Структуры определены в файле заголовка, поскольку файл заголовка предоставляет интерфейс для модуля.Когда структура определяется в интерфейсе, пользователи интерфейса могут:

  • обращаться к членам структуры
  • знать, насколько велика структура - в противном случаеони не могли выделить память для структуры

Связывание не имеет к этому никакого отношения - связаны только функции, а не структуры данных.

Обратите внимание, что вы все равно можете объявить структурув заголовочном файле, что полезно, если вы хотите скрыть внутренние структуры (непрозрачная структура данных).Пользователи интерфейса могут иметь указатели на структуру и использовать их в качестве файла cookie, но они не могут сами выделить такую ​​структуру или «увидеть» внутри нее.

Что касается отсутствия ошибки переопределения при определенииструктура в файле заголовка, это просто из-за защиты заголовка - заголовок обычно выглядит так:

#ifdef MYHEADER_H
#define MYHEADER_H

struct a { int x; }
void f(void);
/* and so on */

#endif

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

...