Includere un file sorgente C in un altro?


Risposte:


113

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.


1
+1 questa è ancora la realtà, mentre compilatori migliori renderanno questo metodo obsoleto con il tempo. GCC 4.5 con l'ottimizzazione del tempo di collegamento è un grande passo avanti.
u0b34a0f6ae

Ho visto così tanti programmi farlo e mi dà sui nervi quando provo a riutilizzare il loro codice. Grazie per aver spiegato perché lo fanno e per aver suggerito l'uso di una guardia (cosa che spesso non viene eseguita).
Joey Adams

Nello sviluppo integrato per C51, l'uso di file C esterni e multipli non ha causato altro che mal di testa. Sto passando ad avere un file C che include tutti gli altri per recuperare il mio tempo.
Mahesh

Per i record: GCC supporta l'ottimizzazione su diverse unità di traduzione, vedere questo thread SO e la sezione del manuale GCC sull'ottimizzazione del tempo di collegamento .
Twonky

60

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


14
Vale anche la pena notare che anche se si compila, potrebbe non collegarsi se viene compilato anche il file #included .c e i due file oggetto sono collegati insieme: si potrebbe finire con simboli definiti più volte.
Nick Meyer

1
Una piccola domanda. Ho un file di intestazione che dichiara un metodo struct + e anche il file .c corrispondente per definirli. Se quel metodo accetta la struttura come parametro, come evitare di includere il file .c in un altro file .c in cui è definito il metodo principale?
stdout

12

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.


9

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.


6

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.


5

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.


3

È 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 :(


1

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.


0

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).


-6

dovresti aggiungere un'intestazione come questa

#include <another.c>

nota: entrambi i file devono essere posizionati nello stesso posto

lo uso in codevison AVR per i microcontrollori ATMEGA e funziona veramente ma non funziona nel normale file di linguaggio C.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.