Domanda

Supponi di avere un programma C che è suddiviso in un set di file * .c e * .h. Se il codice di un file utilizza le funzioni di un altro file, dove devo includere il file di intestazione? All'interno del file * .c che ha utilizzato la funzione o all'interno dell'intestazione del file?

es. il file foo.c include foo.h , che contiene tutte le dichiarazioni per foo.c ; lo stesso per bar.c e bar.h . La funzione foo1 () all'interno di foo.c chiama bar1 () , che è dichiarata in bar.h e definita in bar.c . Ora la domanda è: dovrei includere bar.h all'interno di foo.h o all'interno di foo.c ?

Quale sarebbe una buona serie di regole empiriche per tali problemi?

È stato utile?

Soluzione

Dovresti includere foo.h all'interno di foo.c. In questo modo altri file c che includono foo.h non trasporteranno bar.h inutilmente. Questo è il mio consiglio per includere file di intestazione:

  • Aggiungi le definizioni di inclusione nei file c - in questo modo le dipendenze dei file sono più evidenti durante la lettura del codice.
  • Dividi foo.h in due file separati, ad esempio foo_int.h e foo.h. Il primo porta il tipo e le dichiarazioni in avanti necessarie solo a foo.c. Foo.h include le funzioni e i tipi necessari per i moduli esterni. Questo è qualcosa come la sezione privata e pubblica di foo.
  • Evita i riferimenti incrociati, ovvero i riferimenti a barra e riferimenti a barra. Ciò può causare problemi di collegamento ed è anche un segno di cattiva progettazione

Altri suggerimenti

Come altri hanno notato, un'intestazione foo.h dovrebbe dichiarare le informazioni necessarie per poter utilizzare le funzionalità fornite da un file sorgente foo.c. Ciò include i tipi, le enumerazioni e le funzioni fornite da foo.c. (Non usi variabili globali, vero? Se lo fai, allora anche quelle sono dichiarate in foo.h.)

L'intestazione foo.h deve essere autonoma e idempotente. Autonomo significa che qualsiasi utente può includere foo.h e non deve preoccuparsi di quali altre intestazioni potrebbero essere necessarie (perché foo.h include quelle intestazioni). Idempotente significa che se l'intestazione è inclusa più di una volta, non si verifica alcun danno. Ciò si ottiene con la tecnica classica:

 #ifndef FOO_H_INCLUDED
 #define FOO_H_INCLUDED
 ...rest of the contents of foo.h...
 #endif /* FOO_H_INCLUDED */

La domanda posta:

Il file foo.c include foo.h, che contiene tutte le dichiarazioni per foo.c; lo stesso per bar.c e bar.h. La funzione foo1 () all'interno di foo.c chiama bar1 (), che è dichiarata in bar.h e definita in bar.c. Ora la domanda è: dovrei includere bar.h dentro foo.h o dentro foo.c?

Dipenderà dal fatto che i servizi forniti da foo.h dipendano o meno da bar.h. Se altri file che usano foo.h avranno bisogno di uno dei tipi o enumerazioni definiti da bar.h per utilizzare la funzionalità di foo.h, allora foo.h dovrebbe assicurarsi che bar.h sia incluso (includendolo). Tuttavia, se i servizi di bar.h sono utilizzati solo in foo.c e non sono necessari per coloro che usano foo.h, allora foo.h non dovrebbe includere bar.h

Includerei solo i file di intestazione in un file * .h richiesto per il file di intestazione stesso. I file di intestazione necessari per un file di origine, a mio avviso, dovrebbero essere inclusi nel file di origine in modo che le dipendenze siano evidenti dall'origine. I file di intestazione devono essere creati per gestire l'inclusione multipla in modo da poterli inserire in entrambi, se necessario per chiarezza.

Includo il set minimo di intestazioni possibile nel file .h e includo il resto nel file .c . Questo ha il vantaggio di ridurre talvolta i tempi di compilazione. Dato il tuo esempio, se foo.h non ha davvero bisogno di bar.h ma lo include comunque, e qualche altro file include foo.h , quel file verrà ricompilato se bar.h cambia, anche se potrebbe non essere effettivamente necessario o utilizzare bar.h .

Il file .h dovrebbe definire l'interfaccia pubblica (nota anche come api) per le funzioni nel file .c.

Se l'interfaccia di file1 utilizza l'interfaccia di file2, allora #include file2.h in file1.h

Se l'implementazione in file1.c fa uso di cose in file2.c, file1.c dovrebbe #include file2.h.

Devo ammetterlo però - poiché ho sempre #incluso file1.h in file1.c - normalmente non mi preoccuperei #includere file2.h direttamente in file1.c se fosse già #incluso in file1.h

Se ti trovi mai nella situazione in cui due file .c # includono i file .h, allora è un segno che la modularità si è rotta e dovresti pensare a ristrutturare un po 'le cose.

Utilizzando gli esempi di foo.c e foo.h ho trovato utili queste linee guida:

  • Ricorda che lo scopo di foo.h è di facilitare l'uso di foo.c , quindi mantenerlo semplice, organizzato e autoesplicativo il più possibile. Sii liberale con i commenti che spiegano come e quando di utilizzare le funzionalità di foo.c - e quando non per usarli.

  • foo.h dichiara le funzionalità pubbliche di foo.c : funzioni, macro, typedef e ( shudder ) globali variabili.

  • foo.c dovrebbe #include " foo.h - vedi la discussione, e anche il commento di Jonathan Leffler di seguito.

  • Se foo.c richiede intestazioni aggiuntive per la compilazione, includerle in foo.c.

  • Se per la compilazione di foo.h sono necessarie intestazioni esterne, includerle in foo.h

  • Sfrutta il preprocessore per impedire che foo.h venga incluso più di una volta. (Vedi sotto.)

  • Se per qualche motivo sarà richiesta un'intestazione esterna affinché un altro file .c utilizzi le funzionalità in foo.c , includi l'intestazione in foo.h per salvare lo sviluppatore successivo dal debug non necessario. Se sei contrario, considera l'aggiunta di una macro che visualizzerà le istruzioni in fase di compilazione se le intestazioni richieste non sono state incluse.

  • Non includere un file .c in un altro file .c a meno che non si abbia un molto buona ragione e documentalo chiaramente.

Come notato da kgiannakakis, è utile separare l'interfaccia pubblica dalle definizioni e dalle dichiarazioni necessarie solo all'interno dello stesso foo.c . Ma piuttosto che creare due file, a volte è meglio lasciare che il preprocessore faccia questo per te:

// foo.c
#define _foo_c_         // Tell foo.h it's being included from foo.c
#include "foo.h"
. . .

& nbsp;

// foo.h
#if !defined(_foo_h_)   // Prevent multiple inclusion
#define _foo_h_

// This section is used only internally to foo.c
#ifdef _foo_c_
. . .
#endif

// Public interface continues to end of file.

#endif // _foo_h_       // Last-ish line in foo.h
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top