Dovrei usare # include nelle intestazioni?

È necessario #include un file, se all’interno di un’intestazione (* .h), vengono utilizzati i tipi definiti in questo file?

Ad esempio, se uso GLib e desidero utilizzare il tipo di base gchar in una struttura definita nella mia intestazione, è necessario eseguire un #include , sapendo che l’ho già nel mio file * .c?

Se sì devo anche metterlo tra #ifndef e #define o dopo #define ?

Le regole Goddard Space Flight Center ( GSFC ) della NASA per le intestazioni in stato C indicano che deve essere ansible includere un’intestazione in un file sorgente come unica intestazione e che il codice che utilizza le funzionalità fornite da tale intestazione verrà quindi compilato.

Il vantaggio di questa regola è che se qualcuno ha bisogno di usare l’intestazione, non devono faticare per capire quali altre intestazioni devono essere incluse – sanno che l’intestazione fornisce tutto il necessario.

L’eventuale svantaggio è che alcune intestazioni potrebbero essere incluse molte volte; questo è il motivo per cui le guardie multiple di intestazione di inclusione sono cruciali (e perché i compilatori cercano di evitare le intestazione reincluding quando ansible).

Implementazione

Questa regola significa che se l’intestazione usa un tipo – come ” FILE * ” o ” size_t ” – allora deve assicurarsi che l’altra intestazione appropriata ( o per esempio) debba essere inclusa. Un corollario, spesso dimenticato, è che l’intestazione non dovrebbe includere alcuna altra intestazione che non è necessaria all’utente del pacchetto per poter utilizzare il pacchetto. L’intestazione dovrebbe essere minima, in altre parole.

Inoltre, le regole GSFC forniscono una tecnica semplice per garantire che questo sia ciò che accade:

  • Nel file di origine che definisce la funzionalità, l’intestazione deve essere la prima intestazione elencata.

Quindi, supponiamo di avere un ordinamento magico.

magicsort.h

 #ifndef MAGICSORT_H_INCLUDED #define MAGICSORT_H_INCLUDED #include  typedef int (*Comparator)(const void *, const void *); extern void magicsort(void *array, size_t number, size_t size, Comparator cmp); #endif /* MAGICSORT_H_INCLUDED */ 

magicsort.c

 #include  void magicsort(void *array, size_t number, size_t size, Comparator cmp) { ...body of sort... } 

Si noti che l’intestazione deve includere un’intestazione standard che definisce size_t ; l’intestazione standard più piccola che lo fa è , sebbene molti altri lo facciano anche ( , , , probabilmente alcuni altri).

Inoltre, come accennato prima, se il file di implementazione ha bisogno di altre intestazioni, così sia, ed è del tutto normale che alcune intestazioni extra siano necessarie. Ma il file di implementazione (‘magicsort.c’) dovrebbe includerli da solo, e non fare affidamento sulla sua intestazione per includerli. L’intestazione dovrebbe includere solo ciò di cui hanno bisogno gli utenti del software; non di ciò che gli implementatori hanno bisogno.

Intestazioni di configurazione

Se il tuo codice usa un’intestazione di configurazione (GNU Autoconf e il generato ‘config.h’, per esempio), potresti doverlo usare in ‘magicsort.c’:

 #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "magicsort.h" ... 

Questa è l’unica volta che so che l’intestazione privata del modulo non è la primissima intestazione nel file di implementazione. Tuttavia, l’inclusione condizionale di “config.h” dovrebbe probabilmente trovarsi in “magicsort.h” stesso.


Aggiornamento 2011-05-01

L’URL collegato sopra non è più funzionante (404). Puoi trovare lo standard C ++ (582-2003-004) su EverySpec.com ; lo standard C (582-2000-005) sembra mancare all’azione.

Le linee guida dello standard C erano:

§2.1 UNITÀ

(1) Il codice deve essere strutturato come unità o come file di intestazione stand-alone.

(2) Un’unità deve consistere in un singolo file di intestazione (.h) e uno o più file di body (.c). Collettivamente, i file di intestazione e di corpo sono indicati come i file di origine.

(3) Un file di intestazione dell’unità deve contenere tutte le informazioni pertinenti richieste da un’unità client. Il client di un’unità deve accedere solo al file di intestazione per poter utilizzare l’unità.

(4) Il file di intestazione dell’unità deve contenere istruzioni #include per tutte le altre intestazioni richieste dall’intestazione dell’unità. Ciò consente ai client di utilizzare un’unità includendo un singolo file di intestazione.

(5) Il file del corpo dell’unità deve contenere una dichiarazione #include per l’intestazione dell’unità, prima di tutte le altre dichiarazioni #include. Ciò consente al compilatore di verificare che tutte le istruzioni #include richieste siano nel file di intestazione.

(6) Un file del corpo deve contenere solo funzioni associate a un’unità. Un file body non può fornire implementazioni per funzioni dichiarate in intestazioni diverse.

(7) Tutte le unità client che utilizzano qualsiasi parte di una determinata unità U devono includere il file di intestazione per l’unità U; ciò garantisce che vi sia un solo luogo in cui sono definite le quadro nell’unità U. Le unità client possono chiamare solo le funzioni definite nell’intestazione dell’unità; non possono chiamare funzioni definite nel corpo ma non dichiarate nell’intestazione. Le unità client non possono accedere alle variabili dichiarate nel corpo ma non nell’intestazione.

Un componente contiene una o più unità. Ad esempio, una libreria matematica è un componente che contiene più unità come vettore, matrice e quaternione.

I file di intestazione autonomi non hanno corpi associati; ad esempio, un’intestazione di tipi comuni non dichiara funzioni, quindi non ha bisogno di alcun corpo.

Alcuni motivi per avere più file del corpo per un’unità:

  • Parte del codice del corpo dipende dall’hardware o dal sistema operativo, ma il resto è comune.
  • I file sono troppo grandi.
  • L’unità è un pacchetto di utilità comune e alcuni progetti utilizzeranno solo alcune delle funzioni. Mettere ciascuna funzione in un file separato consente al linker di escludere quelli non utilizzati dall’immagine finale.

§2.1.1 L’intestazione include la logica

Questo standard richiede che l’intestazione di un’unità contenga le istruzioni #include per tutte le altre intestazioni richieste dall’intestazione dell’unità. Inserire #include per l’intestazione dell’unità prima nel corpo dell’unità consente al compilatore di verificare che l’intestazione contenga tutte le istruzioni #include richieste.

Un disegno alternativo, non consentito da questo standard, non consente di #include istruzioni #include nelle intestazioni; tutti i #include sono fatti nei file del corpo. I file di intestazione dell’unità devono quindi contenere istruzioni #ifdef che controllano che le intestazioni richieste siano incluse nell’ordine corretto.

Un vantaggio del design alternativo è che l’elenco #include nel file body è esattamente l’elenco delle dipendenze necessario in un makefile e questo elenco viene controllato dal compilatore. Con la progettazione standard, è necessario utilizzare uno strumento per generare l’elenco delle dipendenze. Tuttavia, tutti gli ambienti di sviluppo consigliati dalla filiale forniscono uno strumento del genere.

Uno svantaggio principale del design alternativo è che se l’elenco di intestazioni richiesto di un’unità cambia, ogni file che utilizza quell’unità deve essere modificato per aggiornare l’elenco di istruzioni #include . Inoltre, l’elenco di intestazioni richiesto per un’unità di libreria del compilatore può essere diverso su target diversi.

Un altro svantaggio della progettazione alternativa è che i file di intestazione della libreria del compilatore e altri file di terze parti devono essere modificati per aggiungere le istruzioni #ifdef richieste.

Una pratica comune diversa consiste nell’includere tutti i file di intestazione di sistema prima di qualsiasi file di intestazione del progetto, nei file del corpo. Questo standard non segue questa pratica, poiché alcuni file di intestazione di progetto possono dipendere dai file di intestazione di sistema, sia perché utilizzano le definizioni nell’intestazione di sistema, sia perché desiderano sovrascrivere una definizione di sistema. Tali file di intestazione del progetto devono contenere istruzioni #include per le intestazioni di sistema; se il corpo li include per primi, il compilatore non controlla questo.

Standard GSFC disponibile tramite Internet Archive 2012-12-10

Informazioni per gentile concessione di Eric S. Bullington :

Lo standard di codifica C di riferimento NASA è accessibile e scaricabile tramite l’archivio Internet:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

sequencing

La domanda chiede anche:

Se sì, devo anche metterlo (le linee #include ) tra #ifndef e #define o dopo #define .

La risposta mostra il meccanismo corretto – l’inclusione nidificata, ecc, dovrebbe essere dopo il #define (e il #define dovrebbe essere la seconda riga senza commento nell’intestazione) – ma non spiega perché sia ​​corretto.

Considera cosa succede se metti #include tra #ifndef e #define . Supponiamo che l’altra intestazione stessa includa varie intestazioni, forse anche #include "magicsort.h" indirettamente. Se la seconda inclusione di magicsort.h verifica prima di #define MAGICSORT_H_INCLUDED , l’intestazione verrà inclusa una seconda volta prima che i tipi definiti siano definiti. Quindi, in C89 e C99, qualsiasi nome di tipo typedef verrà erroneamente ridefinito (C2011 consente di ridefinire lo stesso tipo) e si otterrà il sovraccarico di elaborazione del file più volte, vanificando lo scopo della protezione dell’intestazione nel primo posto. Questo è anche il motivo per cui #define è la seconda riga e non è scritta prima del #endif . La formula indicata è affidabile:

 #ifndef HEADERGUARDMACRO #define HEADERGUARDMACRO ...original content of header — other #include lines, etc... #endif /* HEADERGUARDMACRO */ 

Una buona pratica consiste nel mettere solo #include in un file di inclusione se il file di inclusione ne ha bisogno. Se le definizioni in un determinato file di inclusione vengono utilizzate solo nel file .c, includerlo solo nel file .c.

Nel tuo caso, lo includerei nel file include tra # ifdef / # endif.

Questo ridurrà al minimo le dipendenze in modo che i file che non hanno bisogno di un dato include non debbano essere ricompilati se il file include cambia.

Di solito, gli sviluppatori di librerie proteggono le loro inclusioni da molteplici, incluso il trucco “#ifndef / # define / #endif” in modo da non doverlo fare.

Certo, dovresti controllare … ma ad ogni modo il compilatore te lo dirà ad un certo punto 😉 È comunque una buona pratica controllare le inclusioni multiple dal momento che rallenta il ciclo di compilazione.

Durante il preprocessore di compilazione sostituisce semplicemente la direttiva #include dal contenuto del file specificato. Per evitare loop infiniti dovrebbe usare

 #ifndef SOMEIDENTIFIER #define SOMEIDENTIFIER ....header file body........ #endif 

Se un’intestazione è stata inclusa in un’altra intestazione che è stata inclusa nel tuo file piuttosto che non è necessario includerla di nuovo in modo esplicito, perché sarà inclusa nel file in modo ricorsivo

Sì, è necessario o il compilatore si lamenterà quando tenta di compilare il codice che non è “consapevole” di. Pensate a # include sono un suggerimento / gomito / gomito al compilatore per dirgli di raccogliere le dichiarazioni, le strutture ecc. Per una compilazione di successo. Il trucco dell’intestazione # ifdef / # endif, come sottolineato da jldupont, è di accelerare la compilazione del codice.

Viene usato nei casi in cui si ha un compilatore C ++ e si compila un codice C semplice come mostrato qui Ecco un esempio del trucco:

 #ifndef __MY_HEADER_H__
 #define __MY_HEADER_H__

 #ifdef __cplusplus
 extern "C" {
 #finisci se


 / * C codice qui come strutture, dichiarazioni ecc. * /

 #ifdef __cplusplus
 }
 #finisci se

 #endif / * __MY_HEADER_H__ * /

Ora, se questo è stato incluso più volte, il compilatore lo includerà solo una volta poiché il simbolo __MY_HEADER_H__ è definito una volta, il che accelera i tempi di compilazione. Si noti il ​​simbolo cplusplus nell’esempio sopra, che è il normale modo standard di gestire la compilazione C ++ se si ha un codice C in giro.

Ho incluso quanto sopra per mostrare questo (nonostante non sia davvero rilevante per la domanda originale del poster). Spero che questo aiuti, Cordiali saluti, Tom.

PS: Ci scusiamo per aver permesso a nessuno di svalutare questo dato che pensavo sarebbe stato utile per i nuovi arrivati ​​in C / C ++. Lascia un commento / critiche ecc. Come sono i benvenuti.

Basta includere tutte le intestazioni esterne in un file di intestazione comune nel progetto, ad esempio global.h e includerlo in tutti i tuoi file c:

Può assomigliare a questo:

 #ifndef GLOBAL_GUARD #define GLOBAL_GUARD #include  /*...*/ typedef int YOUR_INT_TYPE; typedef char YOUR_CHAR_TYPE; /*...*/ #endif 

Questo file usa include guard per evitare inclusioni multiple, definizioni multiple illegali, ecc.

Devi includere l’intestazione dall’intestazione e non è necessario includerla nel file .c. Include dovrebbe andare dopo il #define in modo che non vengano inutilmente inclusi più volte. Per esempio:

 /* myHeader.h */ #ifndef MY_HEADER_H #define MY_HEADER_H #include  struct S { gchar c; }; #endif /* MY_HEADER_H */ 

e

 /* myCode.c */ #include "myHeader.h" void myFunction() { struct S s; /* really exciting code goes here */ } 

Io uso il seguente costrutto per essere sicuro che il file di inclusione necessario sia incluso prima di includere questo. Includo tutti i file di intestazione solo nei file di origine.

 #ifndef INCLUDE_FILE_H #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h" #endif 

Quello che faccio normalmente è creare un singolo file include che includa tutte le dipendenze necessarie nell’ordine corretto. Quindi potrei avere:

 #ifndef _PROJECT_H_ #define _PROJECT_H_ #include  #include "my_project_types.h" #include "my_project_implementation_prototypes.h" #endif 

Tutto in project.h. Ora project.h può essere incluso ovunque senza requisiti di ordine, ma ho ancora il lusso di avere le mie dipendenze, i tipi e i prototipi di funzioni API in intestazioni diverse.