Perché alcuni programmi C sono scritti in un enorme file sorgente?

88

Ad esempio, lo SysInternals strumento "FileMon" del passato ha un driver in modalità kernel il cui codice sorgente è interamente in un file di 4.000 righe. Lo stesso vale per il primo programma ping mai scritto (~ 2000 LOC).

    
posta Bran 02.03.2017 - 22:44
fonte

6 risposte

141

L'utilizzo di più file richiede sempre un sovraccarico amministrativo aggiuntivo. Si deve configurare uno script di build 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ì sopra. Gli IDE moderni oggi in genere richiedono molto di questo onere, ma sono abbastanza sicuro al momento in cui è stato scritto il primo programma ping, nessun IDE disponibile. E per i file che piccoli come ~ 4000 LOC, senza un IDE che gestisce più file per te, il compromesso tra l'overhead menzionato e i vantaggi dell'utilizzo di più file potrebbe consentire alle persone di prendere una decisione per l'approccio al singolo file.

    
risposta data 02.03.2017 - 23:00
fonte
80

Perché C non è adatto alla modularizzazione. Diventa disordinato (file header e #include, funzioni extern, errori link-time, ecc.) E più moduli inserisci, più diventa complicato.

Più lingue moderne hanno migliori capacità di modularizzazione in parte perché hanno imparato dagli errori di C, e rendono più facile abbattere la tua 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 che si potrebbe ridurre troppo il codice a un singolo file.

    
risposta data 02.03.2017 - 23:09
fonte
37

A parte i motivi storici, c'è un motivo per usarlo nei moderni software sensibili alle prestazioni. Quando tutto il codice si trova in una unità di compilazione, il compilatore è in grado di eseguire ottimizzazioni dell'intero programma. Con unità di compilazione separate, il compilatore non è in grado di ottimizzare l'intero programma in determinati modi (ad esempio inlining di determinati codici).

Il linker può certamente eseguire alcune ottimizzazioni oltre a ciò che il compilatore può fare, ma non a tutto. Ad esempio: i moderni linker sono davvero bravi a eliminare le funzioni senza referenze, anche su più file oggetto. Potrebbero essere in grado di eseguire altre ottimizzazioni, ma nulla di simile a ciò che un compilatore può fare all'interno di una funzione.

Un esempio ben noto di un modulo di codice a sorgente singola è SQLite. Puoi saperne di più su La pagina di Amalgamazione di SQLite .

1. Executive Summary

Over 100 separate source files are concatenated into a single large files of C-code named "sqlite3.c" and called "the amalgamation". The amalgamation contains everything an application needs to embed SQLite. The amalgamation file is more than 180,000 lines long and over 6 megabytes in size.

Combining all the code for SQLite into one big file makes SQLite easier to deploy — there is just one file to keep track of. And because all code is in a single translation unit, compilers can do better inter-procedure optimization resulting in machine code that is between 5% and 10% faster.

    
risposta data 03.03.2017 - 03:59
fonte
15

Oltre al fattore semplicità che l'altro intervistato ha menzionato, molti programmi C sono scritti da un individuo.

Quando hai una squadra di individui, diventa opportuno dividere l'applicazione tra diversi file sorgente per evitare conflitti gratuiti nelle modifiche al codice. Soprattutto quando ci sono programmatori avanzati e molto giovani che lavorano al progetto.

Quando una persona lavora da sola, non è un problema.

Personalmente, uso più file basati sulla funzione come una cosa abituale. Ma sono solo io.

    
risposta data 02.03.2017 - 23:28
fonte
2

Perché C89 non aveva funzioni inline . Il che significava che suddividere il tuo file in funzioni causava il sovraccarico di spingere i valori sullo stack e saltare in giro. Questo ha aggiunto un po 'di overhead rispetto all'implementazione del codice in 1 istruzione switch di grandi dimensioni (loop eventi). Ma un ciclo di eventi è sempre molto più difficile da implementare in modo efficiente (o anche corretto) rispetto a una soluzione più modulare. Pertanto, per i progetti di grandi dimensioni, le persone opterebbero comunque per la modularizzazione. Ma quando hanno progettato il progetto in anticipo e hanno potuto controllare lo stato in una sola frase, hanno optato per questo.

Al giorno d'oggi, anche in C, non è necessario sacrificare le prestazioni per modularizzare perché anche le funzioni C possono essere in linea.

    
risposta data 04.03.2017 - 11:41
fonte
1

Questo è un esempio di evoluzione, che sono sorpreso non è stato ancora menzionato.

Nei giorni bui della programmazione, la compilazione di un singolo FILE potrebbe richiedere alcuni minuti. Se un programma è stato modularizzato, l'inclusione dei file di intestazione necessari (nessuna opzione di intestazione precompilata) costituirebbe una significativa causa aggiuntiva di rallentamento. Inoltre, il compilatore potrebbe scegliere / avere bisogno 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 adattate solo lentamente nel tempo.

Al momento il guadagno derivante dall'utilizzo di un singolo file sarebbe simile a quello che otteniamo usando SSD anziché HDD.

    
risposta data 15.03.2017 - 23:42
fonte

Leggi altre domande sui tag