2010-08-14 19 views
33

Non capisco come debbano essere separate le cose nei file di origine e di intestazione di C. Vedo spesso molti progetti con due serie di file con lo stesso nome (contiene l'estensione che indica i file di origine e di intestazione).Qualsiasi differenza fondamentale tra i file di origine e di intestazione in C?

Finora, da questa mancanza di comprensione, quando ho scritto le librerie, ho spostato tutto il codice del codice classe e classe in un unico file, con indecisione sulla scelta dell'estensione del file.

Cosa dovrebbero esserci nelle intestazioni e cosa dovrebbe essere nei file sorgente? Come implementare questa separazione?

risposta

43

Non c'è la differenza tecnica. Il compilatore ti consente di includere un file .c o di compilare direttamente un file .h, se lo desideri.

V'è, tuttavia, una differenza enorme culturale:

  • Dichiarazioni (prototipi) vai in .h file. Il file .h è l'interfaccia per qualsiasi cosa sia implementata nel file .c corrispondente.

  • Definizioni go in .c file. Sono implementano l'interfaccia specificata nel file .h.

La differenza è che un file .h può (e di solito sarà) è #include d in più unità di compilazione (.c file). Se si imposta una funzione in un file .h, questo si troverà in più file .o e il linker si lamenterà di un simbolo definito più volte. Ecco perché le definizioni non dovrebbero andare nei file .h. (Le funzioni inline sono l'eccezione.)

Se una funzione è definita in un file .c, e si desidera utilizzare da altri .c file, una dichiarazione di tale funzione deve essere disponibile in ciascuna di quelle altre .c file. Ecco perché hai inserito la dichiarazione in .h e #include in ognuno di essi. È anche possibile ripetere la dichiarazione in ciascun file .c, ma questo porta a un sacco di duplicazione del codice e un pasticcio non gestibile.

Se una funzione è definita in un file .c, ma non si desidera utilizzarlo da altri .c file, non c'è bisogno di dichiararlo nell'intestazione. È essenzialmente un dettaglio di implementazione del file .c. In tal caso, creare anche la funzione static, in modo che non sia in conflitto con le funzioni con nome identico in altri file.

+0

Sfortunatamente, anche se lo standard non impone nulla, la maggior parte dei compilatori che ho usato dipendono in una certa misura dall'estensione del file. Prova questo: crea due file contenenti (ad esempio, 'int main() {return 0;}') chiamato 'header.c' e' header.h' e prova a compilarli con gcc.(Non mi sto neanche avvicinando a VS!) – dirkgently

+0

Sospetto che la maggior parte dei programmatori non inserisca le dichiarazioni delle funzioni statiche nel file .h. La principale differenza tra .h e .c è tra interfaccia e implementazione. – Dipstick

+0

@dirkgently: Ok, ma il compilatore non agisce come un compilatore C se non ottiene l'estensione file giusta. Lo standard non dice nulla sui programmi che non sono compilatori C :) – Thomas

1

In genere, i file di intestazione contengono dichiarazioni, i file di origine contengono codice.

Quindi, se nel file di origine A.c è necessaria una funzione implementata nel file di origine B.c, è sufficiente includere B.h per ottenere la relativa dichiarazione.

14

Cosa dovrebbero esserci nelle intestazioni e cosa dovrebbe essere nei file sorgente?

Tipicamente intestazioni contengono uno o più dei seguenti elementi:

  • dichiarazione di funzione (tranne statica)
  • Dichiarazione delle variabili (tipicamente globale)
  • definito dall'utente dichiarazione del tipo (leggi struct, union ecc .)
  • Definizione macro

file sorgenti d'altra parte hanno:

  • Funzione/definizione variabile
  • dichiarazione della funzione statica e definizione (non si vuole esporre questi per i vostri clienti)
  • variabile definizione
  • Alcuni preferiscono definire le funzioni inline (C99) in un colpo di testa

Come faccio a implementare questa separazione?

La regola One Definition è tua amica.

Ricorda che se stai scrivendo una libreria, questo è ciò che il tuo cliente vedrà. Quindi, sii utile e fornisci tutte le informazioni che puoi per usare la tua biblioteca. I file di origine sono in genere compilati e forniti in formato binario.

E a proposito, C non ha il concetto di classi.

+0

'struct' è abbastanza vicino per me :) –

+2

Se" The One Rule Rule "è d'oro," Do not tell "è d'argento. Con questo intendo è che non inserire dettagli privati ​​nei file di intestazione. Se crei una libreria di terze parti, tutto ciò che inserisci nel file di intestazione devi supportare in futuro. Se lavori su progetti di grandi dimensioni, la riduzione al minimo dei file di intestazione promuove il refactoring in quanto ridurrà i tempi di compilazione quando è effettivamente necessario modificare i file di intestazione. (Sono stato in un progetto in cui ci sono volute 12 ore per ricompilare se hai cambiato i file di intestazione centrale. 12hoursx # numberofdevelopers = money.) – FuleSnabel

1

C'è una piccola differenza fondamentale tra i file .c e .h (anche se alcuni compilatori potrebbero rifiutarsi di compilare un file .h raw). La differenza è più per convenzione.

In genere il file .h fornisce l'API e il file .c fornisce l'implementazione.

Pertanto il file .h conterrà solo gli elementi necessari ad altri file di origine per accedere alle funzionalità fornite dal file .c. Quindi i file .h fornirebbero i prototipi di funzioni di funzioni globali, le dichiarazioni di variabili globali (se proprio ne avete bisogno) e le strutture e gli altri tipi utilizzati da loro. (Non esporre una struttura se l'API utilizza solo un puntatore alla struttura.)

Le funzioni in linea sono spesso incluse nei file .h, ma alcune linee guida di codifica preferiscono l'uso di un'estensione separata (per esempio .inl)

Tutte le altre implementazioni di funzioni, la definizione e l'inizializzazione di variabili e dichiarazioni di variabili (statiche) locali e funzioni sarebbero nel file .c.

Problemi correlati