Jak zadeklarować strukturę w nagłówku, która ma być używana przez wiele plików w c?

115

Jeśli mam plik source.c ze strukturą:

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

Jak można użyć tej struktury w innym pliku (tj. func.c)?

Czy powinienem utworzyć nowy plik nagłówkowy, zadeklarować tam strukturę i dołączyć ten nagłówek func.c?

A może powinienem zdefiniować całą strukturę w pliku nagłówkowym i dołączyć ją do obu source.ci func.c? Jak można zadeklarować strukturę externw obu plikach?

Powinienem to typedefzrobić? Jeśli tak to jak?

Inżynier oprogramowania
źródło
Zauważ, że definicja struktury nie jest prawidłowa C.Najmniej powinien występować średnik po nawiasie zamykającym dla struct b, ale wtedy twoja struktura adeklaruje typ, który nie jest używany (prawdopodobnie powinieneś zdefiniować nazwę elementu, być może kpo wewnętrznym nawiasie zamykającym i przed średnikiem
Jonathan Leffler

Odpowiedzi:

140

jeśli ta struktura ma być używana przez jakiś inny plik func.c, jak to zrobić?

Kiedy typ jest używany w pliku (np. Plik func.c), musi być widoczny. Najgorszym sposobem na zrobienie tego jest skopiowanie i wklejenie go do każdego pliku źródłowego tego potrzebnego.

Właściwym sposobem jest umieszczenie go w pliku nagłówkowym i dołączenie tego pliku nagłówkowego, gdy jest to potrzebne.

czy powinniśmy otworzyć nowy plik nagłówkowy i zadeklarować tam strukturę i dołączyć ten nagłówek do func.c?

Jest to rozwiązanie, które lubię bardziej, ponieważ sprawia, że ​​kod jest wysoce modułowy. Zakodowałbym twoją strukturę jako:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

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

#endif

Funkcje wykorzystujące tę strukturę umieściłbym w tym samym nagłówku (funkcja, która jest „semantycznie” częścią jej „interfejsu”).

Zwykle mogłem nazwać plik po nazwie struktury i użyć tej nazwy ponownie, aby wybrać definicje ochrony nagłówka.

Jeśli chcesz zadeklarować funkcję za pomocą wskaźnika do struktury, nie będziesz potrzebować pełnej definicji struktury. Prosta deklaracja forward, taka jak:

struct a ;

Wystarczy, a to zmniejszy sprzężenie.

czy też możemy zdefiniować całkowitą strukturę w pliku nagłówkowym i zawrzeć ją zarówno w source.c, jak i func.c?

Jest to inny sposób, nieco łatwiejszy, ale mniej modułowy: niektóre kody wymagające do działania tylko Twojej struktury będą nadal musiały zawierać wszystkie typy.

W C ++ może to prowadzić do interesujących komplikacji, ale jest to poza tematem (brak tagu C ++), więc nie będę się rozpisywał.

następnie jak zadeklarować tę strukturę jako zewnętrzną w obu plikach. ?

Być może nie widzę sensu, ale Greg Hewgill ma bardzo dobrą odpowiedź w swoim poście Jak zadeklarować strukturę w nagłówku, która ma być używana przez wiele plików w c? .

powinniśmy więc wpisać to jak?

  • Jeśli używasz C ++, nie rób tego.
  • Jeśli używasz C, powinieneś.

Powodem jest to, że zarządzanie strukturą w C może być uciążliwe: musisz zadeklarować słowo kluczowe struct wszędzie, gdzie jest używane:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

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

Podczas gdy typedef pozwoli ci napisać go bez słowa kluczowego struct.

struct MyStructTag ; /* Forward declaration */

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

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

Jest to ważne, nadal zachować nazwę struktury. Pisanie:

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

po prostu utworzy anonimową strukturę z nazwą wpisaną w typoszeregu i nie będziesz w stanie jej zadeklarować. Dlatego zachowaj następujący format:

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

W ten sposób będziesz mógł używać MyStruct wszędzie tam, gdzie chcesz uniknąć dodawania słowa kluczowego struct i nadal używać MyStructTag, gdy typedef nie zadziała (tj. Deklaracja do przodu)

Edytować:

Poprawiono błędne założenie dotyczące deklaracji struktury C99, jak słusznie zauważył Jonathan Leffler .

Edycja 2018-06-01:

Craig Barnes przypomina nam w swoim komentarzu, że nie trzeba zachowywać oddzielnych nazw dla nazwy struktury „tag” i jej nazwy „typedef”, tak jak to zrobiłem powyżej dla przejrzystości.

Rzeczywiście, powyższy kod można by napisać jako:

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

IIRC, to właśnie robi C ++ z prostszą deklaracją struktury, za kulisami, aby zachować zgodność z C:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Wracając do C, widziałem oba zastosowania (oddzielne nazwy i te same nazwy) i żadna z nich nie ma wad, o których wiem, więc użycie tej samej nazwy ułatwia czytanie, jeśli nie używasz oddzielnych „przestrzeni nazw” w C dla struktur i innych symboli .

paercebal
źródło
2
Czy możesz skomentować lub wskazać sekcję standardu C99, która uzasadnia Twój komentarz „Jeśli używasz C99, nie rób tego”?
Jonathan Leffler
Masz rację. Niedawno przetestowałem C99 i byłem zaskoczony, widząc, że moja nietypowa struktura nie została rozpoznana, gdy została użyta w C ++. Szukałem opcji kompilatora, a potem we wszystkich standardowych dokumentach, na które mogłem położyć ręce, ale nie znalazłem nic, co mogłoby wyjaśnić, wierzyłem, że to możliwe ...
paercebal
2
... W każdym razie dzięki za uwagę. Poprawiłem to teraz.
paercebal
Nie ma potrzeby używania różnych nazw dla structtagu i typedefnazwy. C używa innej przestrzeni nazw dla structtagów, więc możesz używać MyStructobu.
Craig Barnes
1
@CraigBarnes Masz rację, ale chciałem, żeby było to jasne, po prostu czytając kod. Gdybym podał tę samą nazwę, mogłoby to zdezorientować nowicjuszy C w kwestii konieczności
wpisania
34

W przypadku definicji struktury, która ma być używana w więcej niż jednym pliku źródłowym, zdecydowanie należy umieścić ją w pliku nagłówkowym. Następnie dołącz ten plik nagłówkowy do dowolnego pliku źródłowego, który wymaga takiej struktury.

externDeklaracja nie służy do definicji struktury, ale zamiast tego jest używany do deklaracji zmiennych (czyli jakaś wartość danych z typem konstrukcji, które zostały zdefiniowane). Jeśli chcesz użyć tej samej zmiennej w więcej niż jednym pliku źródłowym, zadeklaruj ją tak, jak externw pliku nagłówkowym, takim jak:

extern struct a myAValue;

Następnie w jednym pliku źródłowym zdefiniuj rzeczywistą zmienną:

struct a myAValue;

Jeśli zapomnisz o tym zrobić lub przypadkowo zdefiniujesz to w dwóch plikach źródłowych, linker poinformuje Cię o tym.

Greg Hewgill
źródło
Możesz otrzymać błąd konsolidatora lub nie. W C model powiązań pozwala na „wspólne” definicje, więc może działać wiele definicji bez inicjatora (i być może z tym samym inicjatorem). Jest to „powszechne rozszerzenie”. Wydaje mi się, że niektóre kompilatory obsługują również wstępne (brakujące) definicje.
Jonathan Leffler,
Następnie w jednym pliku źródłowym zdefiniuj rzeczywistą zmienną:; ... lub przypadkowo zdefiniuj to w dwóch plikach źródłowych ... :)
Johannes Schaub - litb
Jedną z technik, których użyłem do zadeklarowania / zdefiniowania problemu, jest warunkowe zdefiniowanie GLOBALjako externlub nic u góry nagłówka, a następnie zadeklarowanie zmiennych jako GLOBAL struct a myAValue;. Z większości plików źródłowych ustalasz, która #define GLOBAL externwersja ma być używana ( deklarując zmienne) i z dokładnie jednego pliku źródłowego powoduje to użycie pustej definicji, więc zmienne są zdefiniowane .
TripeHound
Możesz mieć nazwę struktury taką samą jak nazwa typu typedef w C, ale nie w C ++.
xuhdev
6

ah:

#ifndef A_H
#define A_H

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

#endif

gotowe, teraz wystarczy dołączyć ah do plików, w których chcesz używać tej struktury.

fmsf
źródło