Ad esempio, lo strumento SysInternals "FileMon" del passato ha un driver in modalità kernel il cui codice sorgente è interamente in un file di 4.000 righe. Lo stesso per il primo programma di ping mai scritto (~ 2.000 LOC).
Ad esempio, lo strumento SysInternals "FileMon" del passato ha un driver in modalità kernel il cui codice sorgente è interamente in un file di 4.000 righe. Lo stesso per il primo programma di ping mai scritto (~ 2.000 LOC).
Risposte:
L'uso di più file richiede sempre un sovraccarico amministrativo aggiuntivo. Uno deve impostare uno script di compilazione e / o makefile con fasi di compilazione e collegamento separate, assicurarsi che le dipendenze tra i diversi file siano gestite correttamente, scrivere uno script "zip" per una più facile distribuzione del codice sorgente via e-mail o download, e così via sopra. Gli IDE moderni oggi in genere assumono molto di quel peso, ma sono abbastanza sicuro al momento in cui è stato scritto il primo programma di ping, tale IDE non era disponibile. E per file piccoli come ~ 4000 LOC, senza un simile IDE che gestisce bene più file per te, il compromesso tra l'overhead menzionato e i vantaggi derivanti dall'uso di più file potrebbe consentire alle persone di prendere una decisione per l'approccio a file singolo.
Perché C non è bravo nella modularizzazione. Diventa disordinato (file di intestazione e #include, funzioni esterne, errori di link-time, ecc.) E più moduli porti, più diventa complicato.
I linguaggi più moderni hanno migliori capacità di modularizzazione in parte perché hanno imparato dagli errori di C e semplificano la suddivisione della base di codice in unità più piccole e più semplici. Ma con C, può essere utile evitare o minimizzare tutti quei problemi, anche se ciò significa raggruppare ciò che altrimenti verrebbe considerato troppo codice in un singolo file.
A parte le ragioni storiche, c'è un motivo per usarlo nei moderni software sensibili alle prestazioni. Quando tutto il codice si trova in un'unità di compilazione, il compilatore è in grado di eseguire ottimizzazioni dell'intero programma. Con unità di compilazione separate, il compilatore non può ottimizzare l'intero programma in determinati modi (ad es. Incorporando un determinato codice).
Il linker può certamente eseguire alcune ottimizzazioni oltre a ciò che il compilatore può fare, ma non tutti. Ad esempio: i moderni linker sono davvero bravi a eludere le funzioni senza riferimenti, anche su più file oggetto. Potrebbero essere in grado di eseguire alcune altre ottimizzazioni, ma nulla di simile a quello che un compilatore può fare all'interno di una funzione.
Un esempio ben noto di un modulo di codice a sorgente singola è SQLite. Puoi leggere di più a riguardo nella pagina The SQLite Amalgamation .
1. Sintesi
Oltre 100 file di origine separati vengono concatenati in un unico file di grandi dimensioni di codice C denominato "sqlite3.c" e chiamato "fusione". La fusione contiene tutto ciò di cui un'applicazione ha bisogno per incorporare SQLite. Il file di fusione è lungo più di 180.000 righe e ha dimensioni superiori a 6 megabyte.
La combinazione di tutto il codice per SQLite in un unico grande file semplifica l'implementazione di SQLite: c'è solo un file da tenere traccia. E poiché tutto il codice si trova in una singola unità di traduzione, i compilatori possono eseguire una migliore ottimizzazione tra procedure, ottenendo un codice macchina compreso tra il 5% e il 10% più veloce.
$(CC) $(CFLAGS) $(LDFLAGS) -o $(TARGET) $(CFILES)
piuttosto che spostare tutto in un singolo file soudce. Puoi persino eseguire la compilazione dell'intero programma come destinazione alternativa allo script di compilazione tradizionale che salta la ricompilazione dei file di origine che non sono cambiati, in modo simile al modo in cui le persone potrebbero disattivare la creazione di profili e il debug per la destinazione di produzione. Non hai questa opzione se tutto è in un'unica grande risorsa. Non è ciò a cui le persone sono abituate, ma non c'è nulla di ingombrante al riguardo.
Oltre al fattore di semplicità menzionato dall'altro rispondente, molti programmi C sono scritti da un individuo.
Quando hai un team di persone, diventa desiderabile dividere l'applicazione su più file sorgente per evitare conflitti gratuiti nelle modifiche al codice. Soprattutto quando ci sono programmatori avanzati e molto junior che lavorano al progetto.
Quando una persona lavora da sola, non è un problema.
Personalmente, utilizzo più file in base alla funzione come una cosa abituale. Ma sono solo io.
Perché C89 non aveva inline
funzioni. Ciò significava che suddividere il file in funzioni causava il sovraccarico di spingere i valori in pila e saltare. Ciò ha aggiunto un po 'di sovraccarico all'implementazione del codice in 1 istruzione switch di grandi dimensioni (loop di eventi). Ma un loop di eventi è sempre molto più difficile da implementare in modo efficiente (o persino corretto) rispetto a una soluzione più modulare. Quindi, per i progetti di grandi dimensioni, la gente opterebbe comunque per la modularizzazione. Ma quando hanno avuto il progetto pensato in anticipo e sono stati in grado di controllare lo stato in 1 istruzione switch, hanno optato per quello.
Al giorno d'oggi, anche in C, non è necessario sacrificare le prestazioni per modularizzare perché anche nelle funzioni C possono essere incorporate.
inline
parola chiave nei compilatori C89 non potesse essere in linea, motivo per cui è stato necessario scrivere tutto in un'unica funzione gigante non è corretto. Praticamente non dovresti mai usare inline
come ottimizzazione delle prestazioni - il compilatore saprà comunque meglio di te (e può anche ignorare la parola chiave).
inline
parola chiave ha una semantica relativa al linker che è più importante della questione se eseguire o meno le ottimizzazioni in linea, ma alcune implementazioni hanno altre direttive per il controllo in linea e tali cose a volte possono essere molto importanti. In alcuni casi, una funzione può sembrare troppo grande per essere degna di essere allineata, ma la piegatura costante potrebbe ridurre le dimensioni e il tempo di esecuzione a quasi nulla. Un compilatore a cui non viene dato un forte impulso per incoraggiare l'in-lining potrebbe non ...
Questo è un esempio di evoluzione, che mi sorprende non è ancora stato menzionato.
Nei giorni bui della programmazione, la compilazione di un singolo FILE potrebbe richiedere alcuni minuti. Se un programma fosse modularizzato, l'inclusione dei file di intestazione necessari (senza opzioni di intestazione precompilate) sarebbe una causa aggiuntiva significativa di rallentamento. Inoltre, il compilatore potrebbe scegliere / necessitare di conservare alcune informazioni sul disco stesso, probabilmente senza il vantaggio di un file di scambio automatico.
Le abitudini che questi fattori ambientali hanno portato a trasferire nelle pratiche di sviluppo in corso e si sono lentamente adattate nel tempo.
Al momento, il guadagno derivante dall'uso di un singolo file sarebbe simile a quello ottenuto dall'utilizzo di SSD anziché HDD.