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

Если у меня есть файл 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, после внутреннего закрывающая фигурная скобка и перед точкой с запятой.

Jonathan Leffler 01.10.2014 03:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
122
1
293 370
3

Ответы 3

а.х .:

#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 модель связывания допускает «общие» определения, поэтому могут работать несколько определений без инициализатора (и, возможно, с одним и тем же инициализатором). Это «обычное расширение». Я полагаю, что некоторые компиляторы также поддерживают предварительные (отсутствующие) определения.

Jonathan Leffler 23.10.2008 10:12

Затем в одном исходном файле определять фактическая переменная:; ... или случайно определять это в двух исходных файлах ... :)

Johannes Schaub - litb 25.11.2008 22:34

Один из методов, который я использовал для решения проблемы объявления / определения, - это условно определить GLOBAL как extern или ничего в верхней части заголовка, а затем объявить переменные как GLOBAL struct a myAValue;. Из большинства исходных файлов вы организуете использование версии #define GLOBAL extern (переменные объявление), и ровно из одного исходного файла это приводит к использованию пустого определения, поэтому переменные равны определенный.

TripeHound 18.03.2014 20:37

Имя структуры может быть таким же, как имя typedef в C, но не в C++.

xuhdev 27.06.2014 09:46

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

Можете ли вы прокомментировать или указать на раздел стандарта C99, который требует вашего комментария «Если вы используете C99, не делайте этого»?

Jonathan Leffler 26.11.2008 10:53

Ты прав. Я недавно тестировал C99 и был удивлен, обнаружив, что моя нетипизированная структура не распознается при использовании способа C++. Я искал параметры компилятора, а затем во всех стандартных документах, которые мог найти, но не нашел ничего, что могло бы объяснить, я считал это возможным ...

paercebal 30.11.2008 23:07

... Так что в любом случае спасибо за замечание. Исправил прямо сейчас.

paercebal 30.11.2008 23:10

Нет необходимости использовать разные имена для тега struct и имени typedef. C использует другое пространство имен для тегов struct, поэтому вы можете использовать MyStruct для обоих.

Craig Barnes 31.05.2018 11:02

@CraigBarnes Вы правы, но я хотел, чтобы это стало ясно, просто прочитав код. Если бы я дал то же имя, это могло бы запутать новичков C в необходимости писать имя * дважды "в одном и том же" объявлении ". Я добавлю примечание с упоминанием вашего комментария. Спасибо!

paercebal 01.06.2018 11:58

Другие вопросы по теме