Как объявить структуру в заголовке, которая будет использоваться несколькими файлами в c?

115

Если у меня есть файл source.c со структурой:

struct a { 
    int i;
    struct b {
        int j;
    }
};

Как можно использовать эту структуру в другом файле (т.е. func.c)?

Должен ли я создать новый файл заголовка, объявить там структуру и включить этот заголовок func.c?

Или мне следует определить всю структуру в файле заголовка и включить ее в оба source.cи func.c? Как можно объявить структуру externв обоих файлах?

Я должен typedefэто сделать? Если да, то как?

Инженер-программист
источник
Обратите внимание, что определение структуры недопустимо C. Как минимум должна быть точка с запятой после закрывающей скобки для struct b, но тогда ваша структура aобъявляет тип, который не используется (вероятно, вам следует определить имя члена, возможно k, после внутренней закрывающей скобки и перед точкой с запятой
Джонатан Леффлер

Ответы:

140

если эта структура будет использоваться другим файлом func.c, как это сделать?

Когда тип используется в файле (например, в файле func.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 ;

Хватит, и это снижает сцепление.

или можем ли мы определить общую структуру в заголовочном файле и включить ее в source.c и func.c?

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

В C ++ это может привести к интересным осложнениям, но это не по теме (без тега C ++), поэтому я не буду вдаваться в подробности.

затем как объявить эту структуру как extern в обоих файлах. ?

Возможно, я не понимаю сути, но у Грега Хьюгилла есть очень хороший ответ в его сообщении. Как объявить структуру в заголовке, которая будет использоваться несколькими файлами в 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, как справедливо заметил Джонатан Леффлер .

Изменить 2018-06-01:

Крейг Барнс напоминает нам в своем комментарии, что вам не нужно хранить отдельные имена для имени 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 для структур и других символов. ,

paercebal
источник
2
Можете ли вы прокомментировать или указать на раздел стандарта C99, который требует вашего комментария «Если вы используете C99, не надо»?
Джонатан Леффлер,
Ты прав. Я недавно тестировал C99 и был удивлен, обнаружив, что моя нетипизированная структура не распознается при использовании способа C ++. Я искал параметры компилятора, а затем во всех стандартных документах, которые мог
достать
2
... Так что в любом случае спасибо за замечание. Исправил прямо сейчас.
paercebal
Нет необходимости использовать разные имена для structтега и typedefимени. C использует другое пространство имен для structтегов, поэтому вы можете использовать MyStructоба.
Крейг Барнс
1
@CraigBarnes Вы правы, но я хотел, чтобы это стало ясно, просто прочитав код. Если бы я дал то же имя, это могло бы запутать новичков C в необходимости писать имя * дважды "в одном и том же" объявлении ". Я добавлю примечание с упоминанием вашего комментария. Спасибо!
paercebal
34

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

externДекларация не используется для определения структуры, но вместо этого используется для объявления переменных (то есть, некоторые значения данных с типом структуры , которые вы определили). Если вы хотите использовать одну и ту же переменную в более чем одном исходном файле, объявите ее как externв файле заголовка, например:

extern struct a myAValue;

Затем в одном исходном файле определите фактическую переменную:

struct a myAValue;

Если вы забудете это сделать или случайно определите это в двух исходных файлах, компоновщик сообщит вам об этом.

Грег Хьюгилл
источник
Вы можете получить ошибку компоновщика, а может и нет. В C модель связывания допускает «общие» определения, поэтому могут работать несколько определений без инициализатора (и, возможно, с одним и тем же инициализатором). Это «обычное расширение». Я полагаю, что некоторые компиляторы также поддерживают предварительные (отсутствующие) определения.
Джонатан Леффлер,
Затем в одном исходном файле определите фактическую переменную:; ... или случайно определить его в двух исходных файлах ... :)
Йоханнес Шауб - litb
Один из методов, который я использовал для решения проблемы объявления / определения, - это условно определить GLOBALкак externили ничего в верхней части заголовка, а затем объявить переменные как GLOBAL struct a myAValue;. Из большинства исходных файлов, вы устраиваете для #define GLOBAL externверсии , которые будут использоваться ( объявление переменных) и точно с одного исходного файла он вызывает пустые определить для использования так являются переменными определены .
TripeHound
Вы можете иметь имя структуры, такое же, как имя typedef в C, но не в C ++.
xuhdev
5

ах:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

Итак, теперь вам просто нужно включить ah в файлы, в которых вы хотите использовать эту структуру.

fmsf
источник