Come dichiarare una struttura in un'intestazione che deve essere utilizzata da più file in c?


115

Se ho un file source.c con una struttura:

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

Come può essere usata questa struttura in un altro file (cioè func.c)?

Devo creare un nuovo file di intestazione, dichiarare lì la struttura e includere quell'intestazione func.c?

O dovrei definire l'intera struttura in un file di intestazione e includerla in entrambi source.ce func.c? Come si può dichiarare la struttura externin entrambi i file?

Dovrei typedef? Se é cosi, come?


Nota che la definizione della struttura non è valida C.Almeno dovrebbe esserci un punto e virgola dopo la parentesi graffa di chiusura per struct b, ma poi la tua struttura adichiara un tipo non utilizzato (dovresti probabilmente definire un nome di membro, forse k, dopo la parentesi graffa di chiusura interna e prima del punto e virgola.
Jonathan Leffler,

Risposte:


140

se questa struttura deve essere usata da qualche altro file func.c come si fa?

Quando un tipo viene utilizzato in un file (cioè file func.c), deve essere visibile. Il modo peggiore per farlo è copiarlo e incollarlo in ogni file sorgente necessario.

Il modo giusto è metterlo in un file di intestazione e includere questo file di intestazione ogni volta che è necessario.

dobbiamo aprire un nuovo file di intestazione e dichiarare la struttura lì e includere quell'intestazione nel func.c?

Questa è la soluzione che mi piace di più, perché rende il codice altamente modulare. Vorrei codificare la tua struttura come:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

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

#endif

Metterei le funzioni che utilizzano questa struttura nella stessa intestazione (la funzione che fa parte "semanticamente" della sua "interfaccia").

E di solito, potrei denominare il file dopo il nome della struttura e utilizzare di nuovo quel nome per scegliere le protezioni dell'intestazione definite.

Se devi dichiarare una funzione usando un puntatore alla struttura, non avrai bisogno della definizione completa della struttura. Una semplice dichiarazione anticipata come:

struct a ;

Sarà sufficiente e diminuisce l'accoppiamento.

oppure possiamo definire la struttura totale nel file di intestazione e includerla sia in source.c che in func.c?

Questo è un altro modo, un po 'più semplice, ma meno modulare: alcuni codici che richiedono solo la tua struttura per funzionare dovrebbero comunque includere tutti i tipi.

In C ++, questo potrebbe portare a complicazioni interessanti, ma questo è fuori tema (nessun tag C ++), quindi non approfondirò.

quindi come dichiarare quella struttura come esterna in entrambi i file. ?

Non riesco a vedere il punto, forse, ma Greg Hewgill ha un'ottima risposta nel suo post Come dichiarare una struttura in un'intestazione che deve essere utilizzata da più file in c? .

dobbiamo digitarlo allora come?

  • Se stai usando C ++, non farlo.
  • Se stai usando C, dovresti.

Il motivo è che la gestione della struttura in C può essere un problema: devi dichiarare la parola chiave struct ovunque venga utilizzata:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

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

Mentre un typedef ti consentirà di scriverlo senza la parola chiave struct.

struct MyStructTag ; /* Forward declaration */

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

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

È importante mantenere ancora un nome per la struttura. scrittura:

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

creerà solo una struttura anonima con un nome digitato e non sarai in grado di dichiararlo in avanti. Quindi mantieni il seguente formato:

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

Quindi, sarai in grado di utilizzare MyStruct ovunque tu voglia evitare di aggiungere la parola chiave struct e continuare a utilizzare MyStructTag quando un typedef non funzionerà (cioè dichiarazione in avanti)

Modificare:

Corretto il presupposto errato sulla dichiarazione della struttura C99, come giustamente osservato da Jonathan Leffler .

Modifica 01/06/2018:

Craig Barnes ci ricorda nel suo commento che non è necessario mantenere nomi separati per il nome della struttura "tag" e il suo nome "typedef", come ho fatto sopra per motivi di chiarezza.

In effetti, il codice sopra potrebbe essere scritto come:

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

IIRC, questo è in realtà ciò che fa C ++ con la sua dichiarazione struct più semplice, dietro le quinte, per mantenerlo compatibile con C:

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

Tornando a C, ho visto entrambi gli usi (nomi separati e stessi nomi) e nessuno ha svantaggi che conosco, quindi usare lo stesso nome rende la lettura più semplice se non usi "spazi dei nomi" separati C per strutture e altri simboli .


2
Puoi commentare o sottolineare la sezione dello standard C99 che garantisce il tuo commento "Se stai usando C99, non farlo"?
Jonathan Leffler

Hai ragione. Di recente ho testato un C99 e sono rimasto sorpreso di vedere che la mia struttura non tipizzata non è stata riconosciuta quando utilizzata nel modo C ++. Ho cercato le opzioni del compilatore e poi, in tutti i documenti standard su cui ho potuto mettere le mani, ma non ho trovato nulla che potesse spiegare, credevo fosse possibile ...
paercebal

2
... Quindi comunque, grazie per l'osservazione. L'ho corretto adesso.
paercebal

Non è necessario utilizzare nomi diversi per il structtag e il typedefnome. C utilizza uno spazio dei nomi diverso per i structtag, quindi puoi usarli MyStructper entrambi.
Craig Barnes

1
@ CraigBarnes Hai ragione, ma volevo che fosse chiaro solo leggendo il codice. Se avessi dato lo stesso nome, avrei potuto confondere i neofiti di C sulla necessità di scrivere il nome * due volte "nella stessa" dichiarazione ". Aggiungerò una nota che menziona il tuo commento. Grazie!
paercebal

34

Per una definizione di struttura che deve essere utilizzata in più di un file sorgente, è necessario inserirla in un file di intestazione. Quindi includi quel file di intestazione in qualsiasi file di origine che necessita della struttura.

La externdichiarazione non viene utilizzata per le definizioni della struttura, ma viene invece utilizzata per le dichiarazioni di variabili (ovvero, un valore di dati con un tipo di struttura definito dall'utente). Se desideri utilizzare la stessa variabile in più di un file sorgente, dichiarala come externin un file di intestazione come:

extern struct a myAValue;

Quindi, in un file sorgente, definire la variabile effettiva:

struct a myAValue;

Se ti dimentichi di farlo o lo definisci accidentalmente in due file sorgente, il linker te lo farà sapere.


Potresti ricevere un errore del linker oppure no. In C, il modello di collegamento consente definizioni "comuni", quindi più definizioni senza inizializzatore (e forse con lo stesso inizializzatore) potrebbero funzionare. È una "estensione comune". Alcuni compilatori supportano anche definizioni provvisorie (mancanti), credo.
Jonathan Leffler,

Quindi, in un file sorgente, definire la variabile effettiva:; ... o definirlo accidentalmente in due file sorgente ... :)
Johannes Schaub - litb

Una tecnica che ho usato per il problema di dichiarazione / definizione è definire in modo condizionale GLOBALcome externo niente nella parte superiore dell'intestazione e quindi dichiarare le variabili come GLOBAL struct a myAValue;. Dalla maggior parte dei file sorgente, si dispone la #define GLOBAL externversione da utilizzare ( dichiarando le variabili) e da esattamente un file sorgente si fa in modo che venga utilizzata la definizione vuota in modo che le variabili siano definite .
TripeHound

È possibile avere il nome della struttura uguale al nome typedef in C, ma non in C ++.
xuhdev

6

ah:

#ifndef A_H
#define A_H

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

#endif

ecco fatto, ora devi solo includere ah nei file in cui vuoi usare questa struttura.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.