Domanda

Ho lavorato con una serie di progetti C durante la mia carriera di programmazione e le strutture dei file di intestazione di solito rientrano in uno di questi due modelli:

  1. Un file di intestazione che contiene tutti i prototipi di funzione
  2. file Una .h per ogni file .c, contenente i prototipi per le funzioni definite solo in quel modulo.

I vantaggi di opzione 2 sono evidenti per me - lo rende più conveniente per condividere il modulo tra più progetti e rende le dipendenze tra i moduli più facile vedere

.

Ma quali sono i vantaggi di opzione 1? Deve avere alcuni vantaggi altrimenti non sarebbe stato così popolare.


Questa domanda si applicherebbe a C ++ e C, ma non ho mai visto # 1 in un progetto C ++.

Il posizionamento di #defines, structs ecc varia anche, ma per questa domanda che vorrei mettere a fuoco i prototipi di funzione.

È stato utile?

Soluzione

L'opzione 1 consente di avere tutte le definizioni in un unico luogo in modo da avere per includere / ricerca di un solo file, invece di dover includere / search molti file. Questo vantaggio è più evidente se il sistema viene fornito come una libreria a terzi -. Non si preoccupano molto circa la struttura della libreria, vogliono solo essere in grado di usarlo

Altri suggerimenti

Credo che la motivazione principale per # 1 è ... la pigrizia. La gente pensa che sia troppo difficile da gestire le dipendenze che le cose suddivisione in file separati possono rendere più evidente, e / o pensate che sia in qualche modo "eccessivo" per avere file separati per ogni cosa.

Si può anche, naturalmente, spesso essere un caso di "ragioni storiche", dove il programma o il progetto è cresciuto da qualcosa di piccolo, e nessuno ha avuto il tempo di refactoring i file di intestazione.

Un altro motivo per l'utilizzo di un .h diverso per ogni .c è tempo di compilazione. Se v'è un solo .h (o se ci sono più di loro, ma si li sono compresi in ogni file .c), ogni volta che si apporta una modifica nel file h, si dovrà ricompilare tutti i file .c. Questo, in un grande progetto, può rappresentare una quantità importante di tempo perdersi, che può anche rompere il vostro flusso di lavoro.

  • 1 è semplicemente inutile. Non riesco a vedere una buona ragione per farlo, e un sacco di evitarlo.

Tre regole per seguire # 2 e non hanno problemi:

  • iniziare ogni file di intestazione con un

    #ifndef _HEADER_Namefile
    #define _HEADER_Namefile_
    

terminare il file con

    #endif

Che vi permetterà di includere lo stesso file di intestazione più volte sullo stesso modulo (innadvertely può accadere) senza causare alcun problema.

  • non si può avere definizioni sui file header ... e questo è qualcosa che tutti pensa / lei sa, su prototipi di funzione, ma quasi mai ignora per le variabili globali. Se si desidera una variabile globale, che per definizione dovrebbe essere visibile all'esterno è la definizione del modulo C, usare la parola chiave extern:

    extern unsigned long G_BEER_COUNTER;
    

, che indica al compilatore che il simbolo G_BEER_COUNTER è in realtà un lungo (così, funziona come una dichiarazione), che in alcuni altri moduli avranno E 'corretta definizione / inizializzazione senza segno. (Questo permette anche il linker per mantenere la tabella dei simboli risolto / irrisolto.) La definizione reale (stessa dichiarazione, senza esterno) va nel file del modulo .c.

  • solo su provata necessità assoluta di fare includere altre intestazioni all'interno di un file di intestazione. includono affermazioni dovrebbero essere visibili solo sui file .c (i moduli). Che ti permette di interpretare al meglio le dependecies, e trovare / risolvere i problemi.

Consiglio un approccio ibrido: fare un'intestazione separata per ogni componente del programma che concettualmente, potrebbe essere utilizzato in modo indipendente, quindi facendo un'intestazione di progetto che include tutti loro. In questo modo, ogni file sorgente ha solo bisogno di includere un colpo di testa (non c'è bisogno di andare aggiornare tutti i file di origine se si refactoring componenti), ma si mantiene un organizzazione logica per le dichiarazioni e lo rendono facile da riutilizzare il codice.

C'è anche Credo che una terza opzione: ogni .c ha il suo .h, ma c'è anche una .h che comprende tutti gli altri file .h. Questo porta il meglio dei due mondi a scapito di mantenere un .h fino ad oggi, anche se questo potrebbe fatto automaticamente.

Con questa opzione, si utilizza internamente i singoli file .h, ma un 3rd party può semplicemente includere il file .h onnicomprensiva.

Quando si ha un progetto molto grande con centinaia / migliaia di piccoli file di intestazione, controllo delle dipendenze e la compilazione possono rallentare in modo significativo come un sacco di file di piccole dimensioni devono essere aperti e letti. Questo problema può essere risolto utilizzando spesso intestazioni precompilate.

In C ++ si sarebbe sicuramente vuole un file di intestazione per classe e utilizzare le intestazioni pre-compilati di cui sopra.

Un file di intestazione per un intero progetto è impraticabile a meno che il progetto è estremamente piccolo - come un compito scolastico

Questo dipende da quanto la funzionalità è in un unico file di intestazione / source. Se è necessario includere 10 file solo, per esempio, ordinare qualcosa, è male.

Ad esempio, se voglio usare vettori STL ho appena includere e non mi interessa quello che sono necessari meccanismi interni per il vettore da utilizzare. GCC comprende 8 altre intestazioni - allocatore, algobase, costruire, non inizializzato, vettoriali e bvector. Sarebbe doloroso per includere tutti coloro 8 solo per usare vettore, sei d'accordo?

MA biblioteca header interni devono essere il più scarsa possibile. I compilatori sono più felici se non includono cose inutili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top