C / C ++ - esiprosessori ( eng . preprocesor , preprocessor ) - ohjelma , joka valmistelee ohjelmakoodin C / C ++ -kielellä kääntämistä varten .
Esiprosessori tekee seuraavaa:
Ehdollisen kääntämisen avulla voit valita, mikä koodi käännetään seuraavien perusteella:
Esikäsittelyvaiheet:
C/C++-esiprosessorikieli ei ole Turingin valmis, jo pelkästään siksi, että esiprosessoria ei voida saada roikkumaan direktiiveillä. Katso rekursiivinen funktio (laskettavuusteoria) .
Esikäsittelyohje (komentorivi) on rivi lähdekoodissa, jonka muoto on seuraava #ключевое_слово параметры:
Avainsanaluettelo:
Kun käskyt #include "..."ja löytyvät #include <...>, jossa "..." on tiedoston nimi, esiprosessori lukee määritetyn tiedoston sisällön, suorittaa käskyt ja korvaukset (korvaukset), korvaa direktiivin #includekäskyllä #lineja käsitellyn tiedoston sisällön.
Tiedoston #include "..."etsiminen suoritetaan nykyisessä kansiossa ja kääntäjän komentorivillä määritetyissä kansioissa. Tiedoston #include <...>etsiminen suoritetaan kansioissa, jotka sisältävät vakiokirjastotiedostoja (polut näihin kansioihin riippuvat kääntäjän toteutuksesta).
Jos löytyy käsky, joka #include последовательность-лексем ei vastaa mitään aikaisemmista muodoista, se pitää merkkijonoa tekstinä, jonka tulee kaikkien makrokorvausten seurauksena antaa #include <...>tai #include "...". Näin muodostettua ohjetta tulkitaan edelleen vastaanotetun lomakkeen mukaisesti.
Mukana olevat tiedostot sisältävät yleensä:
Ohje #includeon yleensä määritetty tiedoston alussa (otsikossa), joten mukana olevia tiedostoja kutsutaan otsikkotiedostoiksi .
Esimerkki tiedostojen sisällyttämisestä C - standardikirjastosta .
#include <math.h> // sisällyttää matemaattisten funktioiden määritykset #include <stdio.h> // sisältää I/O-funktion määrityksetEsiprosessorin käyttöä pidetään tehottomana seuraavista syistä:
1970-luvulta lähtien alkoi ilmaantua menetelmiä, jotka korvasivat tiedostojen sisällyttämisen. Java- ja Common Lisp -kielet käyttävät paketteja (avainsana package) (katso paketti Javassa ), Pascal käyttää englantia. yksiköt (avainsanat unitja uses), Modula , OCaml , Haskell ja Python , moduuleissa. Suunniteltu korvaamaan C- ja C++- kielet, D käyttää avainsanoja ja . moduleimport
Esiprosessorin vakioita ja makroja käytetään määrittämään pieniä koodinpätkiä .
// vakio #define BUFFER_SIZE ( 1024 ) // makro #define NUMBER_OF_ARRAY_ITEMS( array ) ( sizeof( array ) / sizeof( *(array) ) )Jokainen vakio ja jokainen makro korvataan sitä vastaavalla määritelmällä. Makroilla on funktion kaltaiset parametrit, ja niitä käytetään vähentämään funktiokutsujen ylimääräisiä kustannuksia tapauksissa, joissa funktiokutsujen pieni määrä koodia riittää aiheuttamaan huomattavan suoritusosuman.
Esimerkki. Makron max määritelmä , jossa on kaksi argumenttia: a ja b .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )Makroa kutsutaan aivan kuten mitä tahansa funktiota.
z = max ( x , y );Makron vaihtamisen jälkeen koodi näyttää tältä:
z = ( ( x ) > ( y ) ? ( x ) : ( y ) );Kuitenkin yhdessä C-kielessä olevien makrojen etujen ohella esimerkiksi yleisten tietotyyppien tai virheenkorjaustyökalujen määrittelyssä ne myös vähentävät jonkin verran käytön tehokkuutta ja voivat jopa johtaa virheisiin.
Jos esimerkiksi f ja g ovat kaksi funktiota, kutsu
z = max ( f (), g () );ei arvioi f() :ta kerran ja g() :tä kerran , ja laita suurimman arvon z :hen , kuten saatat odottaa. Sen sijaan yksi funktioista arvioidaan kahdesti. Jos funktiolla on sivuvaikutuksia, on todennäköistä, että sen käyttäytyminen on erilaista kuin odotettiin.
C-makrot voivat olla funktioita, jotka luovat jossain määrin uutta syntaksia, ja niitä voidaan myös täydentää mielivaltaisella tekstillä (vaikka C-kääntäjä edellyttää tekstin olevan virheetöntä C-koodia tai muotoiltu kommentiksi), mutta niillä on joitain rajoituksia. kuten ohjelmistorakenteet. Esimerkiksi funktion kaltaisia makroja voidaan kutsua "oikeiksi" funktioiksi, mutta makroa ei voi siirtää toiselle funktiolle osoittimen avulla, koska makrolla itsessään ei ole osoitetta.
Jotkut modernit kielet eivät tyypillisesti käytä tällaista metaohjelmointia käyttämällä makroja merkkijonojen täydennyksinä, vaan luottavat joko automaattiseen tai manuaaliseen toimintojen ja menetelmien johdotukseen, vaan sen sijaan muihin abstraktiotapoihin, kuten malleihin , yleisiin funktioihin tai parametriseen polymorfismiin . Erityisesti inline-funktiot yhden makrojen suurimmista puutteista nykyaikaisissa C- ja C++-versioissa, koska inline-funktio tarjoaa makrojen edun funktiokutsun ylikuormituksen vähentämisessä, mutta sen osoite voidaan välittää osoittimessa epäsuoraan. kutsuja tai käytetään parametrina. Samoin edellä max -makrossa mainittu useiden arvioiden ongelma ei ole merkityksellinen sisäänrakennetuille funktioille.
Voit korvata #define vakiot enumilla ja makrot funktioilla inline.
Operaattorit # ja ##Näitä operaattoreita käytetään luotaessa makroja. #-operaattori ennen makroparametria sulkee sen lainausmerkkeihin, esimerkiksi:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );esiprosessori muuntaa muotoon:
printf ( "42" );##-operaattori makroissa ketjuttaa kaksi merkkiä, esimerkiksi:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition ( Objekti );esiprosessori muuntaa muotoon:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; Makrokorvausten muodollinen kuvaus1) Seuraavan lomakkeen ohjausrivi pakottaa esiprosessorin korvaamaan tunnisteen merkkijonolla koko muussa ohjelmatekstissä:
#define identifier token_sequenceTässä tapauksessa merkkijonon alussa ja lopussa olevat välilyönnit hylätään. Toistuva #define rivi, jolla on sama tunniste, katsotaan virheeksi, jos merkkijonot eivät ole identtisiä (välilyöntien eroilla ei ole merkitystä).
2) Seuraavan muotoinen merkkijono, jossa ensimmäisen tunnisteen ja avaussulkujen välissä ei saa olla välilyöntejä, on makromääritelmä, jonka parametrit määrittävät tunniste-listan.
#define identifier(tunnisteiden_luettelo) merkkien_sekvenssiKuten ensimmäisessä muodossa, merkkijonon alussa ja lopussa olevat välilyönnit hylätään, ja makro voidaan määrittää uudelleen vain samalla numero- ja nimiparametriluettelolla ja samalla merkkijonolla.
Tällainen ohjausrivi käskee esiprosessorin "unohtamaan" tunnukselle annetun määritelmän:
#undef tunniste#undef-direktiivin soveltamista aiemmin määrittelemättömään tunnisteeseen ei pidetä virheenä.
{
Korvausprosessiin vaikuttaa kaksi erityistä käyttäjäkylttiä.
}
Huutomerkki (!) merkitsee säännöt, jotka vastaavat rekursiivisesta kutsumisesta ja määritelmistä.
Esimerkki makrolaajennuksesta #define cat( x, y ) x ## yMakrokutsu "cat(var, 123)" korvataan nimellä "var123". Kuitenkaan "kissa(kissa(1, 2), 3)" kutsuminen ei tuota toivottua tulosta. Harkitse esiprosessorin vaiheita:
0: kissa( kissa( 1, 2 ), 3 ) 1: kissa( 1, 2 ) ## 3 2: kissa( 1, 2 )3Toiminto "##" esti toisen "kissa"-kutsun argumenttien oikean laajentamisen. Tuloksena on seuraava merkkijono:
kissa ( 1 , 2 ) 3jossa ")3" on tulos ensimmäisen argumentin viimeisen merkkien ketjuttamisesta toisen argumentin ensimmäiseen merkkiin, ei ole kelvollinen merkki.
Voit määrittää toisen makrotason seuraavasti:
#define xcat( x, y ) cat( x, y )Kutsu "xcat(xcat(1, 2), 3)" korvataan nimellä "123". Harkitse esiprosessorin vaiheita:
0: xcat( xcat( 1, 2 ), 3 ) 1: kissa( xcat( 1, 2 ), 3 ) 2: kissa(kissa(1,2),3) 3: kissa( 1 ## 2, 3) 4: kissa( 12, 3) 5:12 ##3 6:123Kaikki meni hyvin, koska "##"-operaattori ei osallistunut "xcat"-makron laajentamiseen.
Monet staattiset analysaattorit eivät pysty käsittelemään makroja oikein, joten staattisen analyysin laatu heikkenee .
Esiprosessorin automaattisesti luomat vakiot:
C-esiprosessori tarjoaa mahdollisuuden kääntää ehtoja. Tämä mahdollistaa saman koodin eri versioiden mahdollisuuden. Tyypillisesti tätä lähestymistapaa käytetään ohjelman mukauttamiseen kääntäjäalustalle, tilalle (virheenkorjattu koodi voidaan korostaa tuloksena olevassa koodissa) tai kykyä tarkistaa tiedostoyhteys täsmälleen kerran.
Yleensä ohjelmoijan on käytettävä rakennetta, kuten:
# ifndef FOO_H # määritä FOO_H ... ( otsikkotiedoston koodi )... # loppu JosTämä "makrosuojaus" estää otsikkotiedoston sisällyttämisen kahteen kertaan tarkistamalla kyseisen makron olemassaolon, jolla on sama nimi kuin otsikkotiedostolla. FOO_H-makron määritelmä tapahtuu, kun esiprosessori käsittelee ensin otsikkotiedoston. Sitten, jos tämä otsikkotiedosto sisällytetään uudelleen, FOO_H on jo määritetty, jolloin esiprosessori ohittaa tämän otsikkotiedoston koko tekstin.
Sama voidaan tehdä sisällyttämällä seuraava direktiivi otsikkotiedostoon:
#pragma kerranEsiprosessorin ehdot voidaan määrittää useilla tavoilla, esimerkiksi:
# ifdef x ... #muuta ... # loppu Jostai
# ifx ... #muuta ... # loppu JosTätä menetelmää käytetään usein järjestelmän otsikkotiedostoissa erilaisten ominaisuuksien testaamiseen, joiden määritelmä voi vaihdella alustasta riippuen. Esimerkiksi Glibc - kirjasto käyttää ominaisuudentarkistusmakroja varmistaakseen, että käyttöjärjestelmä ja laitteisto tukevat niitä (makroja) oikein, samalla kun ne säilyttävät saman ohjelmointirajapinnan.
Useimmat nykyaikaiset ohjelmointikielet eivät hyödynnä näitä ominaisuuksia, vaan luottavat enemmän perinteisiin ehdollisiin lausekkeisiin if...then...else...ja jättävät kääntäjän tehtäväksi poimia turhaa koodia käännettävästä ohjelmasta.
Katso digrafit ja trigrafit C/C++-kielillä.
Esiprosessori käsittelee digrafit “ %:” (“ #”), “ %:%:” (“ ##”) ja trigrafit “ ??=” (“ #”), “ ??/” (“ \”).
Esiprosessori pitää sekvenssiä " %:%: " kahdeksi tunnukseksi C- koodia käsiteltäessä ja yhden merkin käsitellessä C++-koodia.
C-ohjelmointikieli | |
---|---|
Kääntäjät |
|
Kirjastot | |
Erikoisuudet | |
Jotkut jälkeläiset | |
C ja muut kielet |
|
Luokka: C-ohjelmointikieli |