Informativno-zabavni portal
Pretraga sajta

Šta bi trebalo biti u c-datoteci, a šta u h-datoteci? Online Uključivanje jednog C izvornog fajla u drugi? Šta može biti u zaglavlju



Ako se pravilno koristi, ovo može biti korisna metoda.

Recimo da imate složen podsistem kritičan za misiju sa prilično malim javnim interfejsom i puno neimplementiranog implementacionog koda. Kod pokreće do nekoliko hiljada linija, stotine privatnih funkcija i dosta privatnih podataka. Ako radite sa ne-trivijalnim ugrađenim sistemima, vjerovatno ćete se često susresti sa ovom situacijom.

Vaše rješenje će vjerovatno biti slojevito, modularno i odvojeno, a ovi aspekti se mogu prikladno predstaviti i poboljšati kodiranjem različitih dijelova podsistema u različitim datotekama.

Sa C-om možete mnogo izgubiti radeći ovo. Gotovo svi alati pružaju pristojne optimizacije za jednu jedinicu kompilacije, ali su vrlo pesimistični u pogledu svega što je deklarirano za eksterno.

Ako sve stavite u jedan C izvorni modul dobićete -

    Poboljšanja performansi i veličine koda - U mnogim slučajevima, pozivi funkcija će biti umetnuti. Čak i bez inlaya, kompajler ima mogućnost da proizvede efikasniji kod.

    Podaci o razini kanala i funkcijama su skriveni.

    Izbjegavanje zagađenja prostora imena i njegovih posljedica - možete koristiti manje glomazna imena.

    Brža kompilacija i komunikacija.

Ali također ćete završiti sa bezbožnim neredom kada je u pitanju uređivanje ovog fajla i gubite impliciranu modularnost. Ovo se može prevazići cijepanjem izvornog koda u više datoteka i uključivanjem u jednu jedinicu kompilacije.

Međutim, morate nametnuti neke konvencije da biste se pozabavili ovim. U određenoj mjeri to će ovisiti o vašem lancu alata, ali postoje neki opći smjernici

    Stavite javno sučelje u zasebnu datoteku zaglavlja - ovo bi i dalje trebalo da radite.

    Imajte jednu glavnu .c datoteku koja uključuje sve podređene .c datoteke. Ovo također može uključivati ​​kod za otvoreni interfejs.

    Koristite zaštitu kompajlera da spriječite da privatna zaglavlja i izvorni moduli budu uključeni od strane eksternih kompilacijskih modula.

    Svi lični podaci i funkcije moraju biti proglašeni statičnima.

    Održavajte konceptualnu razliku između .c i .h datoteka. Ovo koristi postojeće konvencije. Razlika je u tome što ćete u zaglavljima imati mnogo statičnih oglasa.

    Ako vaš alatni lanac ne nameće ništa, ne biste trebali specificirati privatne implementacijske datoteke kao što su .c i .h. Ako koristite omogućene zaštite, oni neće generirati kod i neće uvesti nikakva nova imena (kao rezultat možete završiti s nekim praznim segmentima). Ogromna prednost je u tome što će drugi alati (poput IDE-a) rukovati ovim datotekama u skladu s tim.

Ekstenzija datoteke nije važna većini C kompajlera, tako da će ovo funkcionirati.

Međutim, ovisno o postavkama vaše datoteke ili projekta, uključena c datoteka može generirati zasebnu objektnu datoteku. Kada se poveže, to može rezultirati dvostruko definiranim znakovima.

Jezik C ne zabranjuje ovu vrstu #include, ali rezultirajuća jedinica prijevoda i dalje mora biti važeća C.

Ne znam koji program koristite sa .prj datotekom. Ako koristite nešto poput "make" ili Visual Studio ili bilo šta drugo, samo se pobrinite da to postavite na listu datoteka koje treba kompajlirati bez one koja se ne može kompajlirati nezavisno.

Možete ispravno uključiti .C ili .CPP datoteke u druge izvorne datoteke. Ovisno o vašem IDE-u, obično možete spriječiti dvostruko povezivanje tako što ćete pregledati svojstva izvornih datoteka koje želite uključiti, obično desnim klikom na njih i klikom na svojstva, te poništite /check compilation/link/exclude from build ili bilo koji drugi opcija . Možda. Ili ne možete uključiti datoteku u sam projekat, tako da IDE čak ni ne zna da postoji i neće pokušati da je prevede. A sa make-fajlovima, jednostavno niste stavili fajl u njega da biste ga kompajlirali i povezali.

EDIT: Oprostite, dao sam odgovor umjesto da odgovaram na druge odgovore :(

Uključivanje C fajla u drugi fajl je legalno, ali nije preporučljivo osim ako ne znate tačno zašto to radite i šta pokušavate da postignete.
Prilično sam siguran da ćete, ako ovdje objavite razlog zašto će vaše pitanje biti saopšteno zajednici, pronaći drugi prikladan način da postignete svoj cilj (zapazite "skoro" jer je moguće da je ovo rješenje s obzirom na kontekst) .

Inače, propustio sam drugi dio pitanja. Ako je C datoteka uključena u drugu datoteku i istovremeno uključena u projekat, vjerovatno ćete naići na problem duplih simbola, zašto će povezivanje objekata, odnosno ista funkcija biti definirana dva puta (osim ako nisu statični).

U zavisnosti od vašeg okruženja izrade (nećete specificirati) možda ćete otkriti da radi upravo onako kako želite.

Međutim, postoje mnoga okruženja (i IDE i mnogi ručno izrađeni Makefile) koja očekuju da se *.c kompajlira - ako se to dogodi, vjerovatno ćete naići na greške linkera zbog duplih simbola.

Općenito, ovu praksu treba izbjegavati.

Ako apsolutno morate # uključiti izvor (a to općenito treba izbjegavati), koristite drugu datoteku za datoteku.

Ovo je u redu? da, kompajlirat će se

da li se ovo preporučuje? no - .c datoteke se kompajliraju u .obj datoteke, koje se nakon kompilacije (od strane povezivača) povezuju u izvršnu datoteku (ili biblioteku), tako da nema potrebe za uključivanjem jedne .c datoteke u drugu. Umjesto toga, najvjerovatnije ćete htjeti napraviti .h datoteku koja navodi funkcije/varijable dostupne u drugoj .c datoteci i uključiti .h datoteku

Možete koristiti gcc kompajler na linuxu da povežete dva fajla sa jednim izlazom. Recimo da imate dva c fajla, jedan je "main.c", a drugi je "support.c". Dakle, komanda za povezivanje ovo dvoje je

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

Ove dvije datoteke će biti povezane s jednim main.out. Za pokretanje izlaza naredba bi bila

./main.out

Ako koristite main.c funkciju koja je deklarirana u support.c datoteci onda biste je trebali deklarirati u main također koristeći klasu eksterne memorije.



Možete ispravno uključiti .C ili .CPP datoteke u druge izvorne datoteke. Ovisno o vašem IDE-u, obično možete spriječiti dvostruko povezivanje tako što ćete pregledati svojstva izvornih datoteka koje želite uključiti, obično desnim klikom na njih i klikom na svojstva, te poništite /check compilation/link/exclude from build ili bilo koji drugi opcija . Možda. Ili ne možete uključiti datoteku u sam projekat, tako da IDE čak ni ne zna da postoji i neće pokušati da je prevede. A sa make-fajlovima, jednostavno niste stavili fajl u njega da biste ga kompajlirali i povezali.

EDIT: Oprostite, dao sam odgovor umjesto da odgovaram na druge odgovore :(

U zavisnosti od vašeg okruženja izrade (nećete specificirati) možda ćete otkriti da radi upravo onako kako želite.

Međutim, postoje mnoga okruženja (i IDE i mnogi ručno izrađeni Makefile) koja očekuju da se *.c kompajlira - ako se to dogodi, vjerovatno ćete naići na greške linkera zbog duplih simbola.

Općenito, ovu praksu treba izbjegavati.

Ako apsolutno morate # uključiti izvor (a to općenito treba izbjegavati), koristite drugu datoteku za datoteku.

Mislio sam da podijelim situaciju u kojoj je moj tim odlučio uključiti .c datoteke. Naš arhitekta se uglavnom sastoji od modula koji su odvojeni kroz sistem poruka. Ovi rukovaoci porukama su javni i pozivaju mnoge lokalne statičke radne funkcije da obave svoj posao. Problem je nastao kada smo pokušali da dobijemo pokrivenost za naše pojedinačne testne slučajeve, pošto je jedini način za implementaciju ovog privatnog implementacionog koda bio indirektno preko zajedničkog interfejsa za poruke. Sa nekim radničkim karakteristikama u krilu steka, ovo se pokazalo kao noćna mora za osiguranje odgovarajuće pokrivenosti.

Uključivanje .c fajlova dalo nam je priliku da dođemo do zupčanika u mašini, što nam je bilo zabavno testirati.

Uključivanje C fajla u drugi fajl je legalno, ali nije preporučljivo osim ako ne znate tačno zašto to radite i šta pokušavate da postignete.
Prilično sam siguran da ćete, ako ovdje objavite razlog zašto će vaše pitanje biti saopšteno zajednici, pronaći drugi prikladan način da postignete svoj cilj (zapazite "skoro" jer je moguće da je ovo rješenje s obzirom na kontekst) .

Inače, propustio sam drugi dio pitanja. Ako je C datoteka uključena u drugu datoteku i istovremeno uključena u projekat, vjerovatno ćete naići na problem duplih simbola, zašto će povezivanje objekata, odnosno ista funkcija biti definirana dva puta (osim ako nisu statični).

Jezik C ne zabranjuje ovu vrstu #include, ali rezultirajuća jedinica prijevoda i dalje mora biti važeća C.

Ne znam koji program koristite sa .prj datotekom. Ako koristite nešto poput "make" ili Visual Studio ili bilo šta drugo, samo se pobrinite da to postavite na listu datoteka koje treba kompajlirati bez one koja se ne može kompajlirati nezavisno.

trebali biste dodati ovakvo zaglavlje

#include

Napomena: oba fajla moraju biti smeštena na istom mestu

Možete koristiti gcc kompajler na linuxu da povežete dva fajla sa jednim izlazom. Recimo da imate dva c fajla, jedan je "main.c", a drugi je "support.c". Dakle, komanda za povezivanje ovo dvoje je

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

Ove dvije datoteke će biti povezane s jednim main.out. Za pokretanje izlaza naredba bi bila

./main.out

Ako koristite main.c funkciju koja je deklarirana u support.c datoteci onda biste je trebali deklarirati u main također koristeći klasu eksterne memorije.

Ekstenzija datoteke nije važna većini C kompajlera, tako da će ovo funkcionirati.

Međutim, ovisno o postavkama vaše datoteke ili projekta, uključena c datoteka može generirati zasebnu objektnu datoteku. Kada se poveže, to može rezultirati dvostruko definiranim znakovima.

Izvorni fajlovi

Tekst C programa može se podijeliti na nekoliko izvornih datoteka. Izvorna datoteka je tekstualna datoteka koja sadrži ili cijeli program ili njegov dio. Kada se kompajlira izvorni program, svaki njegov sastavni izvorni fajl mora biti preveden odvojeno i zatim povezan sa drugim fajlovima od strane povezivača. Pojedinačni izvorni fajlovi mogu se kombinovati u jednu izvornu datoteku, sastavljenu kao jednu jedinicu, koristeći direktivu preprocesora #include.

Izvorni fajl može sadržavati bilo koju holističku kombinaciju direktiva, instrukcija kompajlera, deklaracija i definicija. Integritet znači da objekti kao što su definicije funkcija, strukture podataka ili skup povezanih direktiva uslovne kompilacije moraju biti u potpunosti u jednoj datoteci, tj. ne mogu početi u jednoj datoteci i nastaviti u drugoj.

Izvorna datoteka ne mora sadržavati izvršne izraze. Ponekad je zgodno postaviti definicije varijabli u jednu datoteku, au drugim datotekama koristiti ove varijable tako što ćete ih deklarirati. U ovom slučaju, definicije varijabli postaju lako pretražive i modificirane. Iz istih razloga, imenovane konstante i makro definicije se obično skupljaju u zasebne datoteke i uključuju putem direktive preprocesora #include u izvorne datoteke koje ih zahtijevaju.

Uputstva kompajleru se obično odnose samo na određene dijelove izvorne datoteke. Specifične radnje kompajlera specificirane u uputstvima određene su specifičnom implementacijom C kompajlera.

U sljedećem primjeru, izvorni program se sastoji od dvije izvorne datoteke. Funkcije main I max predstavljeni u zasebnim fajlovima. Funkcija main koristi funkciju max u procesu njegove implementacije.

/* izvorni fajl 1 - glavna funkcija */

eksterni int max(int, int); /* deklaracija funkcije */

main() /* definicija funkcije */

int w = JEDAN, x = DVA, y = TRI;

/* izvorni fajl 2 - maksimalna funkcija */

int max (a, b) /* definicija funkcije */

U prvom izvornom fajlu funkcija max deklarisano, ali nije definisano. Ova deklaracija funkcije naziva se preddeklaracija; omogućava kompajleru da kontroliše pozive funkcije pre nego što je definisana. Definicija funkcije main sadrži pozive funkcija max.

Redovi koji počinju simbolom # su direktive pretprocesora. Direktive ukazuju predprocesoru na potrebu da zameni identifikatore JEDAN, DVA, TRI sa odgovarajućim vrednostima u prvom izvornom fajlu. Opseg direktiva se ne proteže na drugi izvorni fajl.

Nedavno mi je slično pitanje postavio kolega koji je počinjao programirati u C. I mislio sam da je ovo dobra prilika da podijelim svoje razumijevanje ovog pitanja. Jer čak ni iskusni programeri nemaju uvijek slična gledišta po ovom pitanju.

Djelomično je ovo stvar ukusa, pa ako nekoga zanima kako to radim, dobrodošao u mačku.

Uprkos činjenici da je “cijela istina” o h-datotekama sadržana u odgovarajućem dijelu opisa gcc pretprocesora, dozvolit ću sebi neka objašnjenja i ilustracije.

Dakle, doslovno, datoteka zaglavlja (h-file) je datoteka koja sadrži C deklaracije i makro definicije namijenjene za korištenje u nekoliko izvornih datoteka (c-fajlovi). Hajde da to ilustrujemo.

Lako je primijetiti da se u oba fajla spominju funkcije 1 i 2, kao i makro 2. A pošto uključivanje datoteka zaglavlja daje iste rezultate kao kopiranje sadržaja u svaki C fajl, možemo učiniti sljedeće:

Stoga smo jednostavno izdvojili zajednički dio dvije datoteke i smjestili ga u datoteku zaglavlja.
Ali da li je datoteka zaglavlja interfejs u ovom slučaju?

  • Ako trebamo koristiti funkcionalnost koju funkcije 1 i 2 implementiraju negdje drugdje, onda Da
  • Ako je makro 2 namijenjen samo za upotrebu u datotekama Unit1.c i Unit2.c, tada mu nema mjesta u datoteci interfejsa
Štaviše, da li zaista trebamo imati dva C fajla za implementaciju interfejsa definisanog u zaglavlju? Ili je jedan dovoljan?
Odgovor na ovo pitanje zavisi od detalja implementacije funkcija interfejsa i mesta njihove implementacije. Na primjer, ako dijagrame učinite detaljnijim, možete zamisliti scenarij u kojem se funkcije sučelja implementiraju u različitim datotekama:


Ova opcija implementacije dovodi do visoke koherentnosti koda, niske mogućnosti testiranja i poteškoća u ponovnom korištenju takvih modula.
Da bih izbjegao takve poteškoće, C datoteku i datoteku zaglavlja uvijek smatram jednim modulom. U kojem,
  • Datoteka zaglavlja sadrži samo one deklaracije funkcija, tipova, makroa koji su dio sučelja ovog modula.
  • C datoteka, zauzvrat, mora sadržavati implementaciju svih funkcija deklariranih u h-datoteci, kao i privatne tipove, makroe i funkcije koje su potrebne za implementaciju sučelja.
Dakle, ako bih implementirao kod koji odgovara dijagramu iznad, pokušao bih postići sljedeće (završeci _s i _h u nazivima datoteka su dodati zbog nemogućnosti korištenja tačke u alatu koji sam koristio za kreiranje dijagrami):


Dijagram pokazuje da zapravo imamo posla sa dva nezavisna modula, od kojih svaki ima svoj interfejs u obliku datoteke zaglavlja. Ovo omogućava korišćenje samo interfejsa koji je zaista potreban u ovom konkretnom slučaju. Štaviše, ovi moduli se mogu testirati nezavisno jedan od drugog.
Čitalac je verovatno primetio da je makro 2 iz zaglavlja ponovo vraćen kao kopija u oba C fajla. Naravno, ovo nije baš zgodno za održavanje. Ali pravljenje ovog makroa u interfejs nije ispravno.
U takvim slučajevima, radije bih napravio zasebnu datoteku zaglavlja koja sadrži tipove i makroe potrebne za nekoliko C datoteka.

Nadam se da sam uspeo da identifikujem one entitete koje je potrebno staviti u fajlove zaglavlja. Takođe, pokažite razliku između interfejsa i datoteka koje sadrže deklaracije i makroe koje zahteva nekoliko C fajlova.

Hvala vam na pažnji prema materijalu.