Come configurare smartphone e PC. Portale informativo
  • casa
  • Recensioni
  • File di origine c. Cosa dovrebbe essere nel file c e cosa dovrebbe essere nel file h? Dividere il testo del programma in moduli

File di origine c. Cosa dovrebbe essere nel file c e cosa dovrebbe essere nel file h? Dividere il testo del programma in moduli

File sorgenti

Il testo di un programma C può essere suddiviso in diversi file sorgente. Un file sorgente è un file di testo che contiene l'intero programma o parte di esso. Quando si compila un programma sorgente, ciascuno dei file sorgente che lo costituiscono deve essere compilato separatamente e quindi collegato agli altri file tramite un linker. File sorgente separati possono essere combinati in un unico file sorgente, compilato nel suo insieme, usando la direttiva del preprocessore #includere.

Il file sorgente può contenere qualsiasi combinazione completa di direttive, istruzioni del compilatore, dichiarazioni e definizioni. Integrità significa che oggetti come definizioni di funzioni, strutture di dati o un insieme di direttive di compilazione condizionale correlate devono trovarsi interamente in un file, ovvero non possono iniziare in un file, ma continuare in un altro.

Il file di origine non deve contenere istruzioni eseguibili. A volte è conveniente inserire le definizioni delle variabili in un file e utilizzare queste variabili in altri file dichiarandole. In questo caso, le definizioni delle variabili diventano facilmente ricercabili e modificabili. Per lo stesso motivo, le costanti e le macro denominate vengono solitamente raccolte in file separati e incluse tramite una direttiva del preprocessore #includere ai file sorgente che li richiedono.

Le istruzioni del compilatore di solito si applicano solo a sezioni specifiche del file sorgente. Le azioni specifiche del compilatore, specificate dalle direttive, sono determinate dall'implementazione specifica del compilatore C.

Nell'esempio seguente, il programma sorgente è costituito da due file sorgente. Funzioni principale e max presentati in file separati. Funzione principale usa la funzione max nel corso della sua esecuzione.

/* file sorgente 1 - funzione principale * /

extern int max (int, int); / * dichiarazione di funzione * /

main() / * definizione della funzione * /

int w = UNO, x = DUE, y = TRE;

/ * file sorgente 2 - funzione massima * /

int max (a, b) / * definizione funzione * /

Nel primo file sorgente, la funzione max dichiarato ma indefinito. Questa dichiarazione di funzione è chiamata provvisoria; consente al compilatore di controllare la chiamata alla funzione prima che venga definita. Definizione della funzione principale contiene chiamate di funzione max.

Le righe che iniziano con # sono direttive del preprocessore. Le direttive dicono al preprocessore di sostituire gli identificatori UNO, DUE, TRE nel primo file sorgente con i valori corrispondenti. Le direttive non hanno come ambito il secondo file di origine.

support.microsoft

Quando si modificano i file originali in Visual C++ e li si salva, le righe devono essere terminate con la combinazione "CR / LF" [ritorno a capo, avanzamento riga]. Sui sistemi UNIX, le linee terminano con "LF". Pertanto, quando si visualizzano file che sono stati modificati nel gruppo Windows su sistemi UNIX, è possibile che nelle stringhe vengano visualizzati molti caratteri "^ M". Questo accade solo quando l'editor non sa come interpretare il file di Windows. Visual C ++ può aprire file vicino a righe che terminano con la creazione di UNIX LF. Se modifichi questo file e lo salvi da Visual C ++, viene salvato in formato Windows (vedrai CR / LF e non LF che era precedentemente sul sistema).

Questo articolo descrive le procedure per salvare un file modificato creato su una piattaforma Windows in un formato utilizzabile su sistemi UNIX.

NOTA: Visual C++ .NET IDE contiene funzioni disponibili per il salvataggio di un file in formato UNIX. Nell'IDE, salva il file con Salva come..., seleziona salva dall'elenco a discesa Salva con la codifica... e premi thrn ... Seleziona Codifica stringa dall'elenco a discesa UNIX (LF) e quindi fare clic su ok.

È possibile utilizzare i passaggi seguenti per creare un progetto di applicazione console Win32 che converte un file che contiene "CR / LF" in terminazione di riga per "LF":

  1. Per creare un nuovo progetto vuoto denominato DOS2UNIX utilizzando le applicazioni console Win32.
  2. A partire dal File menu, premere il pulsante Nuovo e quindi fare clic su File tab.
  3. Si prega di selezionare File sorgente C/C++ e inserisci il nome del nuovo file DOS2UNIX.cpp.
  4. Incolla il seguente codice in DOS2UNIX.cpp:

    #includere #includere #includere usando lo spazio dei nomi std; int main (int argc, char * argv) (if (argc! = 2) (cout<< "Please specify: dos2unix filename" << endl; return 0; } char ch; char temp="\0"; //Open the file for reading in binarymode. ifstream fp_read(argv, ios_base::in \ / ios_base::binary); sprintf(temp, "%s.temp", argv); //Create a temporary file for writing in the binary mode. This //file will be created in the same directory as the input file. ofstream fp_write(temp, ios_base::out \ / ios_base::trunc \ / ios_base::binary); while(fp_read.eof() != true) { fp_read.get(ch); //Check for CR (carriage return) if((int)ch == 0x0D) continue; if (!fp_read.eof())fp_write.put(ch); } fp_read.close(); fp_write.close(); //Delete the existing input file. remove(argv); //Rename the temporary file to the input file. rename(temp, argv); //Delete the temporary file. remove(temp); return 0; }

  5. A partire dal Edificio menu, premere il pulsante Creazione di DOS2UNIX.exe per creare un file EXE.

Potrebbe essere necessario controllare questo file exe per vedere se funziona correttamente. Per fare ciò, apri il file nell'editor binario di Visual C ++ Quando si sceglie Aprire in gruppo File menu selezionando DOS2UNIX.ex, Impostazioni Aperto come A cui Binario e poi cliccando Aprire... Ad esempio, se il file contiene "hellocrlfworld", il file di dati binari (esadecimale) avrà il seguente aspetto:

48 65 6 C 6 C 6F 0 D 0A 57 6F 72 6 C 64

Questo è equivalente a:

Ehi
Pace

Al prompt dei comandi, esegui dos2unix.exe ... Quindi, apri il file nell'editor binario di Visual C ++. Vedrai che gli 0x0d vengono rimossi. Fino a quando non modifichi il file e lo salvi in ​​Visual C ++ 0x0d s non verrà visualizzato.

Può essere utilizzato insieme al modello di automazione di Visual C ++ per automatizzare l'intero processo. È possibile scrivere semplici macro di script di Microsoft Visual Basic per chiamare questo strumento, ma è necessario prima aggiungere questo strumento Servizio il menu si presenta così:

  1. A partire dal Servizio menu, premere il pulsante personalizzazione e quindi fare clic su Servizio tab.
  2. Specificare un nome come DOS2UNIX e specificare il percorso completo del file Dos2unix.exe in Squadra campo di modifica.
  3. Imposta l'argomento su $ (Filename) $ (FileExt).
  4. Specifica la directory di origine $ (WkspDir) (fornisci il tuo percorso).

Per testare il programma, apri il file in un editor Visual C++, quindi da Servizio menu di lancio DOS2UNIX si intende. Vedrai che il file aperto nell'editor ha tutti i suoi caratteri CR rimossi.

Se si desidera automatizzare questo processo, gestire in modo che ogni volta che si salva un file aperto nell'editor di Visual C ++, lo strumento DOS2UNIX.exe venga chiamato per rimuovere 0x0d s e quindi utilizzare la seguente macro VBScript:

"Questo evento viene generato ogni volta che il documento viene salvato nell'editor VC ++. Sub Application_DocumentSave (theDocument)" Questo chiamerà lo strumento utente nel menu Strumenti. "Cambia il numero in base a quello che hai. Per impostazione predefinita solo tu" hai 6 strumenti nel menu Strumenti, quindi lo strumento DOS2UNIX sarà il settimo. EseguiComando "UserTool7" End Sub

Questo codice VBScript funzionerà solo se i file sono aperti nell'editor di Visual C ++. Questo è l'unico modo per chiamare un file exe da una macro VBScript (non è possibile passare parametri a una macro VBScript). Puoi scrivere invece e sarà più flessibile. Chiama lo strumento "DOS2UNIX.exe" dal componente aggiuntivo senza doverlo aggiungere a Servizio menù.

In Visual C++ utilizzando la macro VBScript fornita:

  1. Apri un file esistente con estensione .dsm o creane uno.
  2. Incolla il codice precedentemente fornito nel file.
  3. In Visual C++, segui questi passaggi.
    1. A partire dal Servizio menu, premere il pulsante personalizzazione.
    2. Fare clic sul pulsante Macro e file aggiuntivi tab.
    3. Fare clic sul pulsante Panoramica scaricare il file .dsm contenente la macro. Una volta nel file .dsm è stato selezionato in Panoramica finestra di dialogo, il file apparirà in Componenti aggiuntivi e macro un elenco di file utilizzando la casella di controllo selezionata accanto ad esso.
    4. Fare clic sul pulsante Vicino continuare.

Ora se apri il file nell'editor Visual C ++ e salvi dal file File il menu chiamato dalla macro e tutti gli 0x0d verranno rimossi dal file aperto. Poiché ciò influenzerà qualsiasi file che manterrai d'ora in poi e si applicherà a qualsiasi progetto che aprirai in futuro, assicurati di disabilitare la macro da Servizio menu usando personalizzazione(deseleziona la casella accanto alla macro).



A seconda del tuo ambiente di compilazione (non lo specificherai), potresti scoprire che funziona esattamente come desideri.

Tuttavia, ci sono molti framework (sia IDE che molti Makefile fatti a mano) in attesa di compilare * .c - se ciò accade, è probabile che si verifichino errori del linker a causa di simboli duplicati.

Come regola generale, questa pratica dovrebbe essere evitata.

Se devi assolutamente # includere la fonte (e generalmente dovrebbe essere evitato), usa un file diverso per il file.

Includere il 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 abbastanza sicuro che se pubblichi qui un motivo per cui la tua domanda viene comunicata alla community, troverai un altro modo appropriato per raggiungere il tuo obiettivo (nota il "quasi" in quanto potrebbe essere una soluzione dato il contesto).

A proposito, mi ero perso la seconda parte della domanda. Se il file C è incluso in un altro file e contemporaneamente incluso nel progetto, è probabile che si verifichi il problema dei simboli duplicati, perché collegando gli oggetti, cioè la stessa funzione verrà definita due volte (a meno che non siano statici ).

Il linguaggio C non vieta questo tipo di #include, ma l'unità di traduzione risultante deve essere ancora valida C.

Non so quale programma stai usando con il file .prj. Se stai usando qualcosa come "make" o Visual Studio o altro, assicurati di impostare il suo elenco di file da compilare senza uno che non possa essere compilato in modo indipendente.

Puoi usare il compilatore gcc su Linux per collegare due file con un output. Supponiamo di avere due file c, uno è "main.c" e l'altro è "support.c". Quindi il comando per connettere questi due

Gcc main.c support.c -o main.out

Questi due file saranno collegati allo stesso output main.out. Per eseguire l'output, il comando sarebbe

./main.out

Se stai usando la funzione main.c che è dichiarata nel file support.c, allora devi dichiararla fondamentalmente usando anche la classe di archiviazione extern.

Usata correttamente, questa può essere una tecnica utile.

Supponiamo di avere un sottosistema mission-critical complesso con un'interfaccia pubblica abbastanza piccola e un sacco di codice di implementazione non implementato. Il codice esegue fino a diverse migliaia di righe, centinaia di funzioni private e un bel po' di dati privati. Se stai lavorando con sistemi embedded non banali, probabilmente hai a che fare con questa situazione abbastanza spesso.

È probabile che la tua soluzione sia stratificata, modulare e disaccoppiata e questi aspetti possono essere convenientemente rappresentati e migliorati codificando diverse parti del sottosistema in file diversi.

Con C, puoi perdere molto in questo modo. Quasi tutti gli strumenti forniscono ottimizzazioni decenti per una singola unità di compilazione, ma sono molto pessimisti su qualsiasi cosa dichiarata extern.

Se metti tutto in un modulo C originale, ottieni...

    Miglioramenti delle prestazioni e della dimensione del codice: in molti casi, le chiamate di funzione saranno in linea. Anche senza inlay, il compilatore ha la capacità di generare codice più efficiente.

    I dati e le funzioni a livello di canale sono nascosti.

    Evitando l'inquinamento dello spazio dei nomi e le sue conseguenze, puoi usare nomi meno ingombranti.

    Compilazione e collegamento più veloci.

Ma ottieni anche un brutto pasticcio quando si tratta di modificare quel file e perdi la modularità implicita. Questo può essere superato suddividendo il codice sorgente in più file e includendoli in un'unica unità di compilazione.

Tuttavia, è necessario applicare alcune convenzioni per gestirlo. Dipenderà in una certa misura dalla tua toolchain, ma alcuni suggerimenti generali sono:

    Metti l'interfaccia pubblica in un file di intestazione separato: dovresti comunque farlo.

    Avere un file .c principale che includa tutti i file .c secondari. Può anche includere codice per un'interfaccia aperta.

    Utilizzare le protezioni del compilatore in modo che le intestazioni private e le unità di origine non vengano incluse dalle unità di compilazione esterne.

    Tutti i dati personali e le funzioni devono essere dichiarati statici.

    Mantenere una distinzione concettuale tra i file .c e .h. Utilizza le convenzioni esistenti. La differenza è che avrai molti annunci statici nei tuoi titoli.

    A meno che la tua toolchain non imponga alcun motivo, non dovresti specificare file di implementazione privati ​​come .c e .h. Se utilizzi le guardie incluse, non genereranno un codice e non inseriranno nuovi nomi (di conseguenza, potresti incontrare alcuni segmenti vuoti). L'enorme vantaggio è che altri strumenti (come l'IDE) gestiranno questi file in modo appropriato.

L'estensione del file è irrilevante per la maggior parte dei compilatori C, quindi funzionerà.

Tuttavia, a seconda delle impostazioni del file o del progetto, il file c incluso potrebbe generare un file oggetto separato. Quando è collegato, ciò può comportare caratteri definiti doppi.

dovresti aggiungere un titolo come questo

#includere

nota: entrambi i file devono trovarsi nella stessa posizione

Puoi includere correttamente i file .C o .CPP in altri file sorgente. A seconda del tuo IDE, di solito puoi prevenire il doppio legame guardando le proprietà dei file sorgente che vuoi includere, di solito facendo clic con il pulsante destro del mouse su di essi e facendo clic su proprietà, e deselezionando / controlla la compilazione / collega / escludi dall'assieme o qualsiasi altro opzione... può essere. Oppure non puoi includere il file nel progetto stesso, quindi l'IDE non sa nemmeno che esiste e non proverà a compilarlo. E con i makefile, semplicemente non hai inserito il file per la compilazione e il collegamento.

EDIT: scusa, ho dato la risposta invece delle altre risposte :(

Una domanda simile mi è stata fatta recentemente da un collega che sta iniziando a programmare in linguaggio C. E ho pensato che questo fosse un buon motivo per condividere la mia comprensione di questo problema. Perché anche i programmatori esperti non hanno sempre gli stessi punti di vista su questo argomento.

In parte è una questione di gusti, quindi chiunque sia interessato a come lo faccio, benvenuto in cat.

Nonostante il fatto che "tutta la verità" sugli h-file sia contenuta nella sezione corrispondente della descrizione del preprocessore gcc, mi permetto alcune spiegazioni e illustrazioni.

Quindi, letteralmente, un file di intestazione (h-file) è un file contenente dichiarazioni C e definizioni di macro destinate all'uso in diversi file sorgente (c-file). Illustriamo questo.

È facile vedere che le funzioni 1 e 2, così come la macro 2, sono menzionate in entrambi i file. E poiché l'inclusione dei file di intestazione produce gli stessi risultati della copia del contenuto in ciascun file C, possiamo fare quanto segue:

Pertanto, abbiamo semplicemente selezionato la parte comune dai due file e l'abbiamo inserita nel file di intestazione.
Ma il file di intestazione è un'interfaccia in questo caso?

  • Se abbiamo bisogno di usare funzionalità che le funzioni 1 e 2 implementano altrove, allora Sì
  • Se la macro 2 è destinata esclusivamente all'uso nei file Unit1.c e Unit2.c, non ha spazio nel file di interfaccia
Inoltre, abbiamo davvero bisogno di due file C per implementare l'interfaccia definita nel file di intestazione? O ne basta uno?
La risposta a questa domanda dipende dai dettagli di implementazione delle funzioni dell'interfaccia e da dove sono implementate. Ad esempio, se rendi gli schemi più dettagliati, puoi immaginare una variante quando le funzioni dell'interfaccia sono implementate in file diversi:


Questa implementazione porta a un'elevata coesione del codice, bassa testabilità e difficoltà nel riutilizzo di tali moduli.
Per non avere tali difficoltà, considero sempre il file C e il file di intestazione come un unico modulo. In quale,
  • il file header contiene solo quelle dichiarazioni di funzioni, tipi, macro che fanno parte dell'interfaccia di questo modulo.
  • Il file C, a sua volta, deve contenere l'implementazione di tutte le funzioni dichiarate nel file h, nonché i tipi privati, le macro e le funzioni necessarie per implementare l'interfaccia.
Quindi, se mi capitasse di implementare il codice che corrisponde al diagramma sopra, proverei a ottenere quanto segue (le desinenze _с e _h nei nomi dei file vengono aggiunte a causa dell'impossibilità di utilizzare un punto nello strumento che usavo per creare diagrammi):


Il diagramma mostra che in effetti abbiamo a che fare con due moduli indipendenti, ognuno dei quali ha una propria interfaccia sotto forma di file di intestazione. Ciò consente di utilizzare solo l'interfaccia realmente necessaria in questo caso particolare, inoltre questi moduli possono essere testati indipendentemente l'uno dall'altro.
Il lettore potrebbe aver notato che la macro 2 dal file di intestazione è tornata di nuovo come una copia in entrambi i file C. Naturalmente, questo non è molto conveniente da mantenere. Ma nemmeno rendere questa macro parte dell'interfaccia è corretto.
In questi casi, preferisco creare un file di intestazione separato contenente i tipi e le macro necessarie per più file C.

Spero di essere riuscito a identificare quelle entità che devono essere inserite nei file di intestazione. Inoltre, per mostrare la differenza tra interfacce e file contenenti dichiarazioni e macro richieste da diversi file C.

Grazie per l'attenzione al materiale.



vota in linea (8)

Includere il 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 abbastanza sicuro che se pubblichi qui un motivo per cui la tua domanda viene comunicata alla community, troverai un altro modo appropriato per raggiungere il tuo obiettivo (nota il "quasi" in quanto potrebbe essere una soluzione dato il contesto).

A proposito, mi ero perso la seconda parte della domanda. Se il file C è incluso in un altro file e contemporaneamente incluso nel progetto, è probabile che si verifichi il problema dei simboli duplicati, perché collegando gli oggetti, cioè la stessa funzione verrà definita due volte (a meno che non siano statici ).

L'estensione del file è irrilevante per la maggior parte dei compilatori C, quindi funzionerà.

Tuttavia, a seconda delle impostazioni del file o del progetto, il file c incluso potrebbe generare un file oggetto separato. Quando è collegato, ciò può comportare caratteri definiti doppi.

A seconda del tuo ambiente di compilazione (non lo specificherai), potresti scoprire che funziona esattamente come desideri.

Tuttavia, ci sono molti framework (sia IDE che molti Makefile fatti a mano) in attesa di compilare * .c - se ciò accade, è probabile che si verifichino errori del linker a causa di simboli duplicati.

Come regola generale, questa pratica dovrebbe essere evitata.

Se devi assolutamente # includere la fonte (e generalmente dovrebbe essere evitato), usa un file diverso per il file.

Puoi usare il compilatore gcc su Linux per collegare due file con un output. Supponiamo di avere due file c, uno è "main.c" e l'altro è "support.c". Quindi il comando per connettere questi due

Gcc main.c support.c -o main.out

Questi due file saranno collegati allo stesso output main.out. Per eseguire l'output, il comando sarebbe

./main.out

Se stai usando la funzione main.c che è dichiarata nel file support.c, allora devi dichiararla fondamentalmente usando anche la classe di archiviazione extern.

Ho pensato di condividere una situazione in cui il mio team ha deciso di includere i file .c. Il nostro architetto è composto principalmente da moduli che vengono disaccoppiati tramite un sistema di messaggistica. Questi gestori di messaggi sono pubblici e chiamano molte funzioni di lavoro statiche locali per svolgere il proprio lavoro. Il problema si è presentato durante il tentativo di ottenere copertura per i nostri singoli casi di test, poiché l'unico modo per implementare questo codice di implementazione privato era indirettamente tramite l'interfaccia di messaggistica comune. Con alcune delle ginocchia del lavoratore in pila, questo si è rivelato un incubo per garantire una copertura adeguata.

Compresi i file .c ci ha dato la possibilità di raggiungere l'ingranaggio dell'auto, eravamo interessati ai test.

Il linguaggio C non vieta questo tipo di #include, ma l'unità di traduzione risultante deve essere ancora valida C.

Non so quale programma stai usando con il file .prj. Se stai usando qualcosa come "make" o Visual Studio o altro, assicurati di impostare il suo elenco di file da compilare senza uno che non possa essere compilato in modo indipendente.

Usata correttamente, questa può essere una tecnica utile.

Supponiamo di avere un sottosistema mission-critical complesso con un'interfaccia pubblica abbastanza piccola e un sacco di codice di implementazione non implementato. Il codice esegue fino a diverse migliaia di righe, centinaia di funzioni private e un bel po' di dati privati. Se stai lavorando con sistemi embedded non banali, probabilmente hai a che fare con questa situazione abbastanza spesso.

È probabile che la tua soluzione sia stratificata, modulare e disaccoppiata e questi aspetti possono essere convenientemente rappresentati e migliorati codificando diverse parti del sottosistema in file diversi.

Con C, puoi perdere molto in questo modo. Quasi tutti gli strumenti forniscono ottimizzazioni decenti per una singola unità di compilazione, ma sono molto pessimisti su qualsiasi cosa dichiarata extern.

Se metti tutto in un modulo C originale, ottieni...

    Miglioramenti delle prestazioni e della dimensione del codice: in molti casi, le chiamate di funzione saranno in linea. Anche senza inlay, il compilatore ha la capacità di generare codice più efficiente.

    I dati e le funzioni a livello di canale sono nascosti.

    Evitando l'inquinamento dello spazio dei nomi e le sue conseguenze, puoi usare nomi meno ingombranti.

    Compilazione e collegamento più veloci.

Ma ottieni anche un brutto pasticcio quando si tratta di modificare quel file e perdi la modularità implicita. Questo può essere superato suddividendo il codice sorgente in più file e includendoli in un'unica unità di compilazione.

Tuttavia, è necessario applicare alcune convenzioni per gestirlo. Dipenderà in una certa misura dalla tua toolchain, ma alcuni suggerimenti generali sono:

    Metti l'interfaccia pubblica in un file di intestazione separato: dovresti comunque farlo.

    Avere un file .c principale che includa tutti i file .c secondari. Può anche includere codice per un'interfaccia aperta.

    Utilizzare le protezioni del compilatore in modo che le intestazioni private e le unità di origine non vengano incluse dalle unità di compilazione esterne.

    Tutti i dati personali e le funzioni devono essere dichiarati statici.

    Mantenere una distinzione concettuale tra i file .c e .h. Utilizza le convenzioni esistenti. La differenza è che avrai molti annunci statici nei tuoi titoli.

    A meno che la tua toolchain non imponga alcun motivo, non dovresti specificare file di implementazione privati ​​come .c e .h. Se utilizzi le guardie incluse, non genereranno un codice e non inseriranno nuovi nomi (di conseguenza, potresti incontrare alcuni segmenti vuoti). L'enorme vantaggio è che altri strumenti (come l'IDE) gestiranno questi file in modo appropriato.

Questo va bene? si, si compilerà

è consigliato? no - I file .c vengono compilati in file .obj che sono collegati dopo la compilazione (dal linker) in un eseguibile (o libreria), quindi non è necessario includere un file .c in un altro. Invece, molto probabilmente vorrai creare un file .h che elenchi le funzioni / variabili disponibili in un altro file .c e includere un file .h

Principali articoli correlati