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

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

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

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

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

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

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


person Community    schedule 23.10.2008    source источник
comment
Обратите внимание, что определение структуры недействительно C. Как минимум должна быть точка с запятой после закрывающей фигурной скобки для struct b, но тогда ваша структура a объявляет неиспользуемый тип (вам, вероятно, следует определить имя члена, возможно k, после внутреннего закрывающая фигурная скобка и перед точкой с запятой.   -  person Jonathan Leffler    schedule 01.10.2014


Ответы (3)


если эта структура будет использоваться другим файлом 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:

Крейг Барнс напоминает нам в своем комментарии, что вам не нужно хранить отдельные имена для имени структурного тега и его typedef name, как я сделал выше для ясности.

Действительно, приведенный выше код можно было бы записать как:

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

person paercebal    schedule 23.10.2008
comment
Можете ли вы прокомментировать или указать на раздел стандарта C99, который гарантирует, что вы используете C99, не комментируйте? - person Jonathan Leffler; 26.11.2008
comment
Ты прав. Я недавно тестировал C99 и был удивлен, обнаружив, что моя нетипизированная структура не распознается при использовании способа C ++. Я искал параметры компилятора, а затем во всех стандартных документах, которые мог найти, но не нашел ничего, что могло бы объяснить, я считал это возможным ... - person paercebal; 30.11.2008
comment
... Так что в любом случае спасибо за замечание. Исправил прямо сейчас. - person paercebal; 30.11.2008
comment
Нет необходимости использовать разные имена для тега struct и имени typedef. C использует другое пространство имен для тегов struct, поэтому вы можете использовать MyStruct для обоих. - person Craig Barnes; 31.05.2018
comment
@CraigBarnes Вы правы, но я хотел, чтобы это стало ясно, просто прочитав код. Если бы я дал то же имя, это могло бы сбить с толку новичков C в необходимости писать имя * дважды в одном объявлении. Я добавлю заметку с упоминанием вашего комментария. Спасибо! - person paercebal; 01.06.2018

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

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

extern struct a myAValue;

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

struct a myAValue;

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

person Greg Hewgill    schedule 23.10.2008
comment
Вы можете получить ошибку компоновщика, а может и нет. В C модель связывания допускает «общие» определения, поэтому могут работать несколько определений без инициализатора (и, возможно, с одним и тем же инициализатором). Это «обычное расширение». Я считаю, что некоторые компиляторы также поддерживают предварительные (отсутствующие) определения. - person Jonathan Leffler; 23.10.2008
comment
Затем в одном исходном файле определите фактическую переменную:; ... или случайно определить его в двух исходных файлах ... :) - person Johannes Schaub - litb; 25.11.2008
comment
Один из методов, который я использовал для решения проблемы объявления / определения, - это условно определить GLOBAL как extern или ничего в верхней части заголовка, а затем объявить переменные как GLOBAL struct a myAValue;. Из большинства исходных файлов вы организуете использование версии #define GLOBAL extern (объявление переменных), и ровно из одного исходного файла это приводит к использованию пустого определения, поэтому переменные определены . - person TripeHound; 18.03.2014
comment
Имя структуры может быть таким же, как имя typedef в C, но не в C ++. - person xuhdev; 27.06.2014

a.h:

#ifndef A_H
#define A_H

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

#endif

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

person fmsf    schedule 23.10.2008