Если у меня есть файл source.c со структурой:
struct a {
int i;
struct b {
int j;
}
};
Как можно использовать эту структуру в другом файле (например, func.c)?
Должен ли я создать новый файл заголовка, объявить там структуру и включить этот заголовок в func.c?
Или я должен определить всю структуру в файле заголовка и включить его как в source.c, так и в func.c? Как можно объявить структуру extern в обоих файлах?
Стоит ли мне его typedef? Если да, то как?





а.х .:
#ifndef A_H
#define A_H
struct a {
int i;
struct b {
int j;
}
};
#endif
Итак, теперь вам просто нужно включить a.h в файлы, в которых вы хотите использовать эту структуру.
Для определения структуры, которая будет использоваться более чем в одном исходном файле, вы обязательно должны поместить его в файл заголовка. Затем включите этот файл заголовка в любой исходный файл, для которого нужна структура.
Объявление extern не используется для определений структур, но вместо этого используется для объявлений переменных (то есть некоторого значения данных с типом структуры, который вы определили). Если вы хотите использовать одну и ту же переменную в нескольких исходных файлах, объявите ее как extern в файле заголовка, например:
extern struct a myAValue;
Затем в исходном файле один определите фактическую переменную:
struct a myAValue;
Если вы забудете это сделать или случайно определите это в двух исходных файлах, компоновщик сообщит вам об этом.
Вы можете получить ошибку компоновщика, а может и нет. В C модель связывания допускает «общие» определения, поэтому могут работать несколько определений без инициализатора (и, возможно, с одним и тем же инициализатором). Это «обычное расширение». Я полагаю, что некоторые компиляторы также поддерживают предварительные (отсутствующие) определения.
Затем в одном исходном файле определять фактическая переменная:; ... или случайно определять это в двух исходных файлах ... :)
Один из методов, который я использовал для решения проблемы объявления / определения, - это условно определить GLOBAL как extern или ничего в верхней части заголовка, а затем объявить переменные как GLOBAL struct a myAValue;. Из большинства исходных файлов вы организуете использование версии #define GLOBAL extern (переменные объявление), и ровно из одного исходного файла это приводит к использованию пустого определения, поэтому переменные равны определенный.
Имя структуры может быть таким же, как имя typedef в C, но не в C++.
Когда тип используется в файле (например, в файле func.c), он должен быть видимым. Худший способ сделать это - скопировать и вставить в каждый необходимый исходный файл.
Правильный способ - поместить его в файл заголовка и включать этот файл заголовка всякий раз, когда это необходимо.
Это решение мне нравится больше, потому что оно делает код очень модульным. Я бы закодировал вашу структуру как:
#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME
struct a
{
int i;
struct b
{
int j;
}
};
#endif
Я бы поместил функции, использующие эту структуру, в один и тот же заголовок (функции, которые «семантически» являются частью ее «интерфейса»).
И обычно я мог назвать файл после имени структуры и использовать это имя снова, чтобы выбрать определения, определяемые защитой заголовка.
Если вам нужно объявить функцию с помощью указателя на структуру, вам не понадобится полное определение структуры. Простое предварительное объявление, например:
struct a ;
Хватит, и это снижает сцепление.
Это другой способ, несколько проще, но менее модульный: некоторый код, которому для работы нужна только ваша структура, все равно должен включать все типы.
В C++ это может привести к интересным осложнениям, но это не по теме (без тега C++), поэтому я не буду вдаваться в подробности.
Возможно, я не понимаю сути, но у Грега Хьюгилла есть очень хороший ответ в его сообщении Как объявить структуру в заголовке, которая будет использоваться несколькими файлами в c?.
Причина в том, что управление структурами C может быть проблемой: вы должны объявить ключевое слово struct везде, где оно используется:
struct MyStruct ; /* Forward declaration */
struct MyStruct
{
/* etc. */
} ;
void doSomething(struct MyStruct * p) /* parameter */
{
struct MyStruct a ; /* variable */
/* etc */
}
В то время как typedef позволит вам написать его без ключевого слова struct.
struct MyStructTag ; /* Forward declaration */
typedef struct MyStructTag
{
/* etc. */
} MyStruct ;
void doSomething(MyStruct * p) /* parameter */
{
MyStruct a ; /* variable */
/* etc */
}
Это важный, вы по-прежнему сохраняете имя для структуры. Пишу:
typedef struct
{
/* etc. */
} MyStruct ;
просто создаст анонимную структуру с определенным типом именем, и вы не сможете объявить ее вперед-объявить. Поэтому придерживайтесь следующего формата:
typedef struct MyStructTag
{
/* etc. */
} MyStruct ;
Таким образом, вы сможете использовать MyStruct везде, где хотите избежать добавления ключевого слова struct, и по-прежнему использовать MyStructTag, когда typedef не будет работать (например, предварительное объявление)
Исправлено неправильное предположение об объявлении структуры C99, как правильно заметил Джонатан Леффлер.
Крейг Барнс напоминает нам в своем комментарии, что вам не нужно хранить отдельные имена для имени struct "tag" и его имени "typedef", как я сделал выше для ясности.
Действительно, приведенный выше код можно было бы записать как:
typedef struct MyStruct
{
/* etc. */
} MyStruct ;
IIRC, на самом деле это то, что C++ делает со своим более простым объявлением структуры за кулисами, чтобы поддерживать его совместимость с C:
// C++ explicit declaration by the user
struct MyStruct
{
/* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;
Возвращаясь к C, я видел оба использования (разные имена и одинаковые имена), и ни у одного из них нет недостатков, о которых я знаю, поэтому использование одного и того же имени упрощает чтение, если вы не используете C отдельные "пространства имен" для структур и других символов.
Можете ли вы прокомментировать или указать на раздел стандарта C99, который требует вашего комментария «Если вы используете C99, не делайте этого»?
Ты прав. Я недавно тестировал C99 и был удивлен, обнаружив, что моя нетипизированная структура не распознается при использовании способа C++. Я искал параметры компилятора, а затем во всех стандартных документах, которые мог найти, но не нашел ничего, что могло бы объяснить, я считал это возможным ...
... Так что в любом случае спасибо за замечание. Исправил прямо сейчас.
Нет необходимости использовать разные имена для тега struct и имени typedef. C использует другое пространство имен для тегов struct, поэтому вы можете использовать MyStruct для обоих.
@CraigBarnes Вы правы, но я хотел, чтобы это стало ясно, просто прочитав код. Если бы я дал то же имя, это могло бы запутать новичков C в необходимости писать имя * дважды "в одном и том же" объявлении ". Я добавлю примечание с упоминанием вашего комментария. Спасибо!
Обратите внимание, что определение структуры недействительно C. Как минимум должна быть точка с запятой после закрывающей скобки для
struct b, но тогда ваша структураaобъявляет неиспользуемый тип (вам, вероятно, следует определить имя члена, возможно,k, после внутреннего закрывающая фигурная скобка и перед точкой с запятой.