Header-fil

Det kalles header file , på spansk file/file (de) header , eller include file , på spansk file of inclusion , i informatikk , spesielt innen programmeringsspråkene C og C++ , til filen , vanligvis i form for kildekode , som automatisk inkluderes av kompilatoren når en annen kildefil behandles. Programmerere spesifiserer vanligvis inkludering av header-filer via pragmaer i begynnelsen ( head ) av en annen kildefil.

En overskriftsfil inneholder vanligvis en direkte erklæring av klasser , subrutiner , variabler eller andre identifikatorer . De programmererne som ønsker å erklære standardidentifikatorer i mer enn én kildefil, kan legge disse identifikatorene i en enkelt overskriftsfil , som skal inkluderes når koden den inneholder kreves av andre filer.

C Standard Library og C++ Standard Library erklærer tradisjonelt sine standardfunksjoner i overskriftsfiler .

Motivasjon

I de fleste moderne programmeringsspråk kan programmerere dele opp programmer i mindre komponenter (som klasser og subrutiner ) og distribuere disse komponentene blant mange uoversatte enheter (vanligvis i form av filer ) som kan kompileres av systemet . Hvis en subrutine må brukes utenfor enheten som skal oversettes der den er definert, må konseptet med direkte erklæring eller funksjonsprototyper introduseres . For eksempel, en funksjon definert slik i en kildefil:

int add ( int a , int b ) { returner a + b ; }

kan deklareres (med en funksjonsprototype) og refereres fra en andre kildefil som følger:

int add ( int , int ); int trippel ( int x ) { return add ( x , add ( x , x )); }

Denne enkle illustrasjonen krever imidlertid at programmereren oppbevarer funksjonserklæringen addpå to steder - i filen som inneholder implementeringen, og i filen som bruker funksjonaliteten. Hvis funksjonsdefinisjonen blir endret, må programmereren oppdatere alle prototypene spredt over hele programmet. Dette er nødvendig fordi både C- og C++-implementeringene må diagnostisere alle brudd på det som kalles " one definition rule " (ODR) i C++. Faktisk bruker de fleste av dem en linker for å gjøre denne jobben. Linkeren har imidlertid vanligvis svært begrenset kunnskap om typene som brukes i programmene. På grunn av dette blir noen av ODR-bruddene ikke fanget opp når språket implementeres. Som et resultat er det programmererens ansvar å opprettholde konsistensen til alle utsagn som krysser grensene til en enhet som skal oversettes. Det er en vanskelig oppgave å søke etter alle disse erklæringene fra en ekstern enhet og kontrollere at de er kompatible manuelt. (Merk at C ikke definerer begrepet "én definisjonsregel" — det er spesifikt for C++-språket. Hvis erklæringer av samme enhet i mange C-kildefiler er forskjellige, vil ikke funksjonen fungere som den skal, og uforutsigbar oppførsel kan føre til , uansett av regelen som brytes.)

For å forstå et ODR-brudd, bør du vurdere følgende (riktige) kode:

/* Fil print-heading.c */ #include <stdio.h> void print_heading ( void ) { printf ( "standard overskrift \n " ); } /* Fil main.c */ void print_heading ( void ); int main ( ugyldig ) { print_heading (); returner 0 ; }

Enheten som skal oversettes representert av kildefilen main.crefererer til funksjonen print_heading()som er definert i en annen enhet som skal oversettes ( print-heading.c). I henhold til C99- reglene må programmerere deklarere en ekstern funksjon før første gangs bruk. For å oppfylle dette kravet main.cerklærer filen funksjonen på første linje. Denne versjonen av koden fungerer som den skal.

Senere kan programmereren som vedlikeholder kildefilen print-heading.cbestemme seg for å gjøre funksjonen mer fleksibel og støtte tilpassede overskrifter. En mulig implementering kan være følgende:

/* Fil print-heading.c */ #include <stdio.h> void print_heading ( const char * heading ) { printf ( "%s \n " , overskrift ); }

Hvis programmereren glemmer å oppdatere setningen i main.c, kan det få ødeleggende resultater. Funksjonen print_heading()forventer et argument og bruker dens verdi, men funksjonen main()gir ingen verdi. Uforutsigbar oppførsel oppstår når du kjører dette programmet: programmet kan skrive ut søppel, avsluttes uventet eller føre til uforutsigbare resultater på plattformen den kjøres på.

Hvorfor er det mulig å kompilere og koble denne koden uten problemer? Årsaken er at kompilatoren følger in-setningen når main.cden kompilerer enheten som skal oversettes main.c. Og det utsagnet stemmer overens med formen til funksjonen. Senere, når linkeren slår sammen de allerede kompilerte oversettelsesenhetene main.cog (i de fleste implementeringer representert som eller print-heading.cfiler ), kunne den sannsynligvis oppdage inkonsekvensen - men ikke i C. C-implementeringer refererer funksjoner etter navn på nivået av objektfil og binær fil, dette inkluderer ikke returverdien eller argumentlisten. Linkeren finner en referanse til i og en passende funksjon i . På dette tidspunktet går all informasjon om funksjonsargumenttyper tapt. main.omain.objprint_heading()main.oprint-heading.o

Hvordan er det da mulig å utføre flere deklarasjoner uten problemer? Løsningen kalles header-fil . En moduls overskriftsfil deklarerer hver funksjon, objekt og datatype som er en del av modulens offentlige grensesnitt - for eksempel, i dette tilfellet vil overskriftsfilen bare inneholde erklæringen om add. Enhver kildefil som refererer til addbruker direktivet #includei overskriftsfilen :

/* File add.h */ #ifndef ADD_H #define ADD_H int add ( int , int ); #endif /* ADD_H */

(Merk at overskriftsfilen bruker en " Inkluder vakt ".)

/* Fil triple.c */ #inkluder "add.h" int trippel ( int x ) { return add ( x , add ( x , x )); }

Dette reduserer vedlikeholdsbyrden : når en definisjon endres, må bare en enkelt kopi av erklæringen (den i overskriftsfilen) oppdateres. Header-filen kan også inkluderes i kildefilen som inneholder de tilsvarende definisjonene, noe som gir kompilatoren en sjanse til å sjekke om erklæringen og definisjonen er konsistente.

/* Fil add.c */ #inkluder "add.h" int add ( int a , int b ) { returner a + b ; }

Vanligvis bruker programmerere bare overskriftsfiler for å spesifisere grensesnitt , og gir vanligvis minst en liten mengde informasjon som forklarer hvordan man bruker komponentene som er deklarert i filen. Som i dette eksemplet forblir subrutineimplementeringene i en separat kildefil, som fortsetter å bli kompilert uavhengig. (Et vanlig unntak mellom C og C++ er " innebygde funksjoner ", som ofte er inkludert i overskriftsfiler fordi de fleste implementeringer ikke kan sende ut innebygde funksjoner på riktig måte uten å se definisjonene deres på kompileringstidspunktet .)

Alternativer

Header-filer er ikke den eneste løsningen på problemet med å få tilgang til identifikatorer som er deklarert i forskjellige filer. De har den ulempen at programmerere fortsatt må gjøre endringer på to forskjellige steder (i kildefilen og i overskriftsfilen ) når de gjør endringer i en definisjon. Noen yngre språk (som Java ) slipper overskriftsfiler og bruker i stedet et navneskjema som lar kompilatoren finne kildefilene knyttet til implementeringer av klasser og grensesnitt (men dette begrenser friheten til å navngi filer). På disse språkene løses ODR-problemet vanligvis ved to teknikker: For det første legger kompilatoren all nødvendig informasjon om typene i den kompilerte koden, og denne informasjonen er tilgjengelig selv når programmet kjøres; For det andre har Java og andre moderne språk muligheten til å sjekke antall og type argumenter som metodekall. Alt dette har en pris: overflødig plass og utførelsestid som ikke er akseptabelt for enkelte applikasjoner der responstiden er kritisk.

COBOL og RPG IV har en måte å inkludere filer som kalles kopibøker . Programmerere "inkluderer" disse i programkilden på en lignende måte som overskriftsfiler , og lar også visse deler av teksten erstattes. COBOL - nøkkelordet for inkludering er copy, og erstatning gjøres via klausulen replacing...by.

Se også

Eksterne lenker