Як оголосити структуру в заголовку, який повинен використовуватися декількома файлами в 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 ++), тому я не буду деталізувати.

то як оголосити цю структуру як зовнішню в обох файлах. ?

Я не бачу сенсу, можливо, але Грег Х'югілл має дуже хорошу відповідь у своєму дописі Як оголосити структуру в заголовку, який повинен використовуватися декількома файлами в c? .

чи введемо це тоді, як?

  • Якщо ви використовуєте C ++, не робіть цього.
  • Якщо ви використовуєте C, ви повинні.

Причиною цього є те, що керування структурою C може викликати біль: Ви повинні оголосити ключове слово Stru всюди, де воно використовується:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Хоча typedef дозволить вам записати його без ключового слова stru.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

Це важливо , ви по- , як і раніше тримати ім'я структури. Написання:

typedef struct
{
   /* etc. */
} MyStruct ;

просто створить анонімну структуру з іменем typedef-ed, і ви не зможете переадресувати її. Тому дотримуйтесь наступного формату:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Таким чином, ви зможете використовувати MyStruct скрізь, де вам захочеться уникати додавання ключового слова stru, і все одно використовувати MyStructTag, коли typedef не працюватиме (тобто переадресація)

Редагувати:

Виправлено неправильне припущення щодо декларації структури С99, як справедливо зауважив Джонатан Леффлер .

Редагувати 2018-06-01:

Крейг Барнс нагадує нам у своєму коментарі, що вам не потрібно зберігати окремі назви для структури "тег" імені та його "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 окремі "простори імен" для структур та інших символів .


2
Чи можете ви прокоментувати або вказати на розділ стандарту C99, який вимагає коментаря "Якщо ви використовуєте C99, не робіть коментар"?
Джонатан Леффлер

Ти маєш рацію. Нещодавно я перевірив C99 і здивувався, побачивши, що моя нетипізована структура не була розпізнана при використанні способу C ++. Я шукав варіанти компілятора, а потім, у всіх стандартних документах, я міг би покласти свої руки, але не знайшов нічого, що могло б пояснити, я вважав, що це можливо ...
paercebal

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

6

ах:

#ifndef A_H
#define A_H

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

#endif

там ви йдете, тепер вам просто потрібно включити ах до файлів, де ви хочете використовувати цю структуру.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.