Risposte:
Se usata correttamente, questa può essere una tecnica utile.
Supponiamo di avere un sottosistema complesso e critico per le prestazioni con un'interfaccia pubblica abbastanza piccola e un sacco di codice di implementazione non riutilizzabile. Il codice viene eseguito su diverse migliaia di righe, un centinaio di funzioni private e un bel po 'di dati privati. Se lavori con sistemi embedded non banali, probabilmente affronti questa situazione abbastanza frequentemente.
La vostra soluzione sarà probabilmente stratificata, modulare e disaccoppiata e questi aspetti possono essere utilmente rappresentati e rinforzati codificando diverse parti del sottosistema in file differenti.
Con C, puoi perdere molto in questo modo. Quasi tutti i toolchain forniscono un'ottimizzazione decente per una singola unità di compilazione, ma sono molto pessimisti su qualsiasi cosa dichiarata esternamente.
Se metti tutto in un modulo sorgente C, ottieni:
Miglioramenti delle prestazioni e della dimensione del codice: in molti casi le chiamate di funzione saranno inline. Anche senza inlining, il compilatore ha l'opportunità di produrre codice più efficiente.
Dati a livello di collegamento e occultamento delle funzioni.
Evitare l'inquinamento dello spazio dei nomi e il suo corollario: puoi usare nomi meno ingombranti.
Compilazione e collegamento più veloci.
Ma quando si tratta di modificare questo file si ottiene anche un pasticcio empio e si perde la modularità implicita. Questo può essere superato suddividendo il sorgente in diversi file e includendoli per produrre un'unica unità di compilazione.
Tuttavia, è necessario imporre alcune convenzioni per gestirlo correttamente. Questi dipenderanno dalla tua toolchain in una certa misura, ma alcuni suggerimenti generali sono:
Metti l'interfaccia pubblica in un file di intestazione separato - dovresti farlo comunque.
Avere un file .c principale che include tutti i file .c sussidiari. Questo potrebbe anche includere il codice per l'interfaccia pubblica.
Utilizzare le protezioni del compilatore per garantire che le intestazioni private ei moduli di origine non siano inclusi dalle unità di compilazione esterne.
Tutti i dati e le funzioni privati dovrebbero essere dichiarati statici.
Mantieni la distinzione concettuale tra i file .c e .h. Questo fa leva sulle convenzioni esistenti. La differenza è che avrai molte dichiarazioni statiche nelle intestazioni.
Se la tua toolchain non impone alcun motivo per non farlo, dai un nome ai file di implementazione privati .c e .h. Se usi include guards, queste non produrranno codice e non introdurranno nuovi nomi (potresti ritrovarti con alcuni segmenti vuoti durante il collegamento). L'enorme vantaggio è che altri strumenti (ad esempio IDE) tratteranno questi file in modo appropriato.
va bene? sì, verrà compilato
è consigliato? no - I file .c vengono compilati in file .obj, che vengono collegati insieme dopo la compilazione (dal linker) nell'eseguibile (o libreria), quindi non è necessario includere un file .c in un altro. Quello che probabilmente vorresti fare invece è creare un file .h che elenchi le funzioni / variabili disponibili nell'altro file .c e includere il file .h
No.
A seconda del tuo ambiente di compilazione (non specificato), potresti scoprire che funziona esattamente nel modo desiderato.
Tuttavia, ci sono molti ambienti (sia IDE che molti Makefile fatti a mano) che si aspettano di compilare * .c - se ciò accade, probabilmente ti ritroverai con errori del linker a causa di simboli duplicati.
Di regola questa pratica dovrebbe essere evitata.
Se devi assolutamente #includere il sorgente (e generalmente dovrebbe essere evitato), usa un suffisso di file diverso per il file.
Ho pensato di condividere una situazione in cui il mio team ha deciso di includere file .c. Il nostro archivio consiste in gran parte in moduli disaccoppiati tramite un sistema di messaggi. Questi gestori di messaggi sono pubblici e chiamano molte funzioni di lavoro statiche locali per svolgere il proprio lavoro. Il problema si è verificato durante il tentativo di ottenere copertura per i nostri casi di unit test, poiché l'unico modo per esercitare questo codice di implementazione privato era indirettamente attraverso l'interfaccia del messaggio pubblico. Con alcune funzioni di lavoratore immerse fino alle ginocchia nella pila, questo si è rivelato un incubo per ottenere una copertura adeguata.
Includere i file .c ci ha dato un modo per raggiungere l'ingranaggio della macchina che ci interessava nei test.
Puoi usare il compilatore gcc in linux per collegare due file c in un unico output. Supponiamo di avere due file c, uno è "main.c" e un altro è "support.c". Quindi il comando per collegare questi due è
gcc main.c support.c -o main.out
In questo modo due file saranno collegati a un unico output main.out Per eseguire l'output il comando sarà
./main.out
Se stai usando la funzione in main.c che è dichiarata nel file support.c, dovresti dichiararla in main anche usando la classe di archiviazione esterna.
L'estensione del file non è importante per la maggior parte dei compilatori C, quindi funzionerà.
Tuttavia, a seconda del makefile o delle impostazioni del progetto, il file c incluso potrebbe generare un file oggetto separato. Quando si collega ciò potrebbe portare a simboli a doppia definizione.
È possibile includere correttamente file .C o .CPP in altri file di origine. A seconda del tuo IDE, di solito puoi impedire il doppio collegamento guardando le proprietà dei file di origine che desideri includere, di solito facendo clic con il tasto destro su di esso e facendo clic su proprietà, e deseleziona / seleziona compila / collega / escludi dalla build o qualsiasi altra opzione può essere. Oppure non puoi includere il file nel progetto stesso, quindi l'IDE non saprà nemmeno che esiste e non proverà a compilarlo. E con i makefile semplicemente non metteresti il file al suo interno per la compilazione e il collegamento.
EDIT: Scusa, ho reso una risposta invece di una risposta su altre risposte :(
Il linguaggio C non proibisce quel tipo di #include, ma l'unità di traduzione risultante deve comunque essere valida C.
Non so quale programma stai usando con un file .prj. Se stai usando qualcosa come "make" o Visual Studio o altro, assicurati di impostare il suo elenco di file da compilare senza quello che non può essere compilato in modo indipendente.
Includere un file C in un altro file è legale, ma non è consigliabile, a meno che tu non sappia esattamente perché lo stai facendo e cosa stai cercando di ottenere.
Sono quasi sicuro che se pubblicherai qui il motivo per cui dietro la tua domanda la community troverà un altro modo più appropriato per raggiungere il tuo obiettivo (tieni presente il "quasi", poiché è possibile che questa sia la soluzione dato il contesto ).
A proposito, ho perso la seconda parte della domanda. Se il file C è incluso in un altro file e nello stesso tempo incluso nel progetto probabilmente ti ritroverai con il problema del simbolo duplicato perché collegare gli oggetti, cioè la stessa funzione verrà definita due volte (a meno che non siano tutti statici).