C/C++-förprocessor ( eng. preprocessor , preprocessor) - ett program som förbereder programkoden i C / C++ för kompilering .
Förprocessorn gör följande:
Villkorlig kompilering låter dig välja vilken kod som ska kompileras baserat på:
Förbehandlare steg:
C/C++-förprocessorspråket är inte Turing komplett, om så bara för att det är omöjligt att få förprocessorn att hänga sig med hjälp av direktiv. Se rekursiv funktion (beräkbarhetsteori) .
Ett förbehandlardirektiv (kommandorad) är en rad i källkoden som har följande format: #ключевое_слово параметры:
Sökordslista:
När direktiv #include "..."och hittas #include <...>, där "..." är ett filnamn, läser förbehandlaren innehållet i den angivna filen, utför direktiv och ersättningar (ersättningar), ersätter direktivet #includemed ett direktiv #lineoch det behandlade filinnehållet.
För att #include "..."söka efter en fil, utförs den i den aktuella mappen och mappar som anges på kompilatorns kommandorad. För att #include <...>söka efter en fil utförs den i mappar som innehåller standardbiblioteksfiler (sökvägarna till dessa mappar beror på kompilatorns implementering).
Om ett direktiv hittas som #include последовательность-лексем inte matchar någon av de tidigare formerna, betraktar det sekvensen av tokens som text, som, som ett resultat av alla makrosubstitutioner, bör ge #include <...>eller #include "...". Direktivet som genereras på detta sätt kommer att tolkas vidare i enlighet med den mottagna blanketten.
Inkluderade filer innehåller vanligtvis:
Direktivet #includeanges vanligtvis i början av filen (i rubriken), så inkluderade filer kallas rubrikfiler .
Ett exempel på att inkludera filer från C -standardbiblioteket .
#inkludera <math.h> // inkludera matematiska funktionsdeklarationer #include <stdio.h> // include I/O-funktionsdeklarationerAtt använda en förprocessor anses ineffektivt av följande skäl:
Med början på 1970-talet började metoder dyka upp som ersatte införandet av filer. Språken Java och Common Lisp använder paket (sökord package) (se paket i Java ), Pascal använder engelska. enheter (sökord unitoch uses), i Modula , OCaml , Haskell och Python , moduler. Designad för att ersätta språken C och C++ , D använder nyckelorden och . moduleimport
Preprocessorkonstanter och makron används för att definiera små bitar av kod.
// konstant #define BUFFER_SIZE ( 1024 ) // makro #define NUMBER_OF_ARRAY_ITEMS( array ) ( sizeof( array ) / sizeof( *(array) ) )Varje konstant och varje makro ersätts av dess motsvarande definition. Makron har funktionsliknande parametrar och används för att minska omkostnaderna för funktionsanrop i de fall där den lilla mängd kod som funktionen anropar räcker för att orsaka en märkbar prestandaträff.
Exempel. Definition av makrot max , som tar två argument: a och b .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )Ett makro kallas precis som vilken funktion som helst.
z = max ( x , y );Efter att ha ersatt makrot kommer koden att se ut så här:
z = ( ( x ) > ( y ) a ( x ) : ( y ) );Men tillsammans med fördelarna med att använda makron på C-språket, till exempel för att definiera generiska datatyper eller felsökningsverktyg, minskar de också effektiviteten i användningen något och kan till och med leda till fel.
Till exempel, om f och g är två funktioner, anropet
z = max ( f (), g () );kommer inte att utvärdera f() en gång och g() en gång , och sätter det största värdet i z , som du kan förvänta dig. Istället kommer en av funktionerna att utvärderas två gånger. Om en funktion har biverkningar är det troligt att dess beteende blir annorlunda än förväntat.
C-makron kan vara som funktioner, skapa ny syntax i viss utsträckning, och kan även utökas med godtycklig text (även om C-kompilatorn kräver att texten är i felfri C-kod eller formaterad som en kommentar), men de har vissa begränsningar som mjukvarustrukturer. Funktionsliknande makron kan till exempel kallas som "riktiga" funktioner, men ett makro kan inte skickas till en annan funktion med hjälp av en pekare, eftersom själva makrot inte har någon adress.
Vissa moderna språk använder vanligtvis inte den här typen av metaprogrammering med makron som teckensträngskompletteringar, och förlitar sig på antingen automatisk eller manuell koppling av funktioner och metoder, utan istället andra sätt för abstraktion som mallar , generiska funktioner eller parametrisk polymorfism . Speciellt inline-funktioner en av de stora bristerna med makron i moderna versioner av C och C++, eftersom en inline-funktion ger fördelen med makron för att reducera overheaden för ett funktionsanrop, men dess adress kan skickas i en pekare för indirekt anrop eller används som en parameter. På samma sätt är problemet med flera utvärderingar som nämns ovan i maxmakrot irrelevant för inbyggda funktioner.
Du kan ersätta #define-konstanter med enums och makron med funktioner inline.
Operatörer # och ##Dessa operatorer används när man skapar makron. Operatorn # före en makroparameter omsluter den med dubbla citattecken, till exempel:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );förprocessor konverterar till:
printf ( "42" );Operatorn ## i makron sammanfogar två tokens, till exempel:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition ( Object );förprocessor konverterar till:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; Formell beskrivning av makrosubstitutioner1) Kontrollraden i följande formulär tvingar förprocessorn att ersätta identifieraren med en sekvens av tokens genom resten av programtexten:
#define identifier token_sequenceI det här fallet slängs blanktecken i början och slutet av sekvensen av tokens. En upprepad #define-rad med samma identifierare anses vara ett fel om sekvenserna av tokens inte är identiska (felmatchningar i blanktecken spelar ingen roll).
2) En sträng av följande form, där det inte får finnas några blanktecken mellan den första identifieraren och den inledande parentesen, är en makrodefinition med parametrar specificerade av identifierare-listan.
#define identifier(list_of_identifiers) sequence_of_tokensSom i den första formen kasseras blankstegstecken i början och slutet av tokensekvensen, och makrot kan bara omdefinieras med samma nummer och namnparameterlista och samma tokensekvens.
En kontrollrad som denna säger till förprocessorn att "glömma" definitionen som ges till identifieraren:
#undef identifierareAtt tillämpa #undef-direktivet på en tidigare odefinierad identifierare anses inte vara ett fel.
{
Ersättningsprocessen påverkas av två speciella operatörstecken.
}
Ett utropstecken (!) markerar reglerna som ansvarar för rekursiv anrop och definitioner.
Exempel på makroexpansion #define cat( x, y ) x ## yMakroanropet "cat(var, 123)" kommer att ersättas med "var123". Men att anropa "cat(cat(1, 2), 3)" ger inte det önskade resultatet. Tänk på stegen för förprocessorn:
0: cat( cat( 1, 2 ), 3 ) 1: cat( 1, 2 ) ## 3 2: katt( 1, 2)3Operationen "##" förhindrade korrekt expansion av argumenten för det andra "cat"-anropet. Resultatet är följande sträng av tokens:
katt ( 1 , 2 ) 3där ")3" är resultatet av att sammanfoga den sista token i det första argumentet med den första token i det andra argumentet, är inte en giltig token.
Du kan ange den andra makronivån enligt följande:
#define xcat( x, y ) cat( x, y )Anropet "xcat(xcat(1, 2), 3)" kommer att ersättas med "123". Tänk på stegen för förprocessorn:
0: xcat( xcat( 1, 2 ), 3 ) 1: cat( xcat( 1, 2 ), 3 ) 2: cat( cat( 1, 2 ), 3 ) 3: katt( 1 ## 2, 3 ) 4: katt ( 12, 3 ) 5:12##3 6:123Allt gick bra, eftersom "##"-operatören inte deltog i expansionen av "xcat"-makrot.
Många statiska analysatorer kan inte behandla makron korrekt, så kvaliteten på statisk analys minskar .
Konstanter som genereras automatiskt av förprocessorn:
C-förprocessorn ger möjlighet att kompilera med villkor. Detta ger möjlighet till olika versioner av samma kod. Vanligtvis används detta tillvägagångssätt för att anpassa programmet för kompilatorplattformen, tillstånd (den felsökta koden kan markeras i den resulterande koden) eller möjligheten att kontrollera filanslutningen exakt en gång.
I allmänhet måste programmeraren använda en konstruktion som:
# ifndef FOO_H # definiera FOO_H ...( header file code )... # endifDetta "makroskydd" förhindrar att en rubrikfil dubbelinkluderas genom att kontrollera om det finns makrot, som har samma namn som rubrikfilen. Definitionen av makrot FOO_H inträffar när rubrikfilen först bearbetas av förprocessorn. Sedan, om denna rubrikfil återinkluderas, är FOO_H redan definierad, vilket gör att förprocessorn hoppar över hela texten i denna rubrikfil.
Detsamma kan göras genom att inkludera följande direktiv i rubrikfilen:
# pragma en gångFörprocessorvillkor kan specificeras på flera sätt, till exempel:
# ifdef x ... #annat ... # endifeller
# ifx ... #annat ... # endifDenna metod används ofta i systemhuvudfiler för att testa olika funktioner, vars definition kan variera beroende på plattformen; till exempel använder Glibc- biblioteket funktionskontrollmakron för att verifiera att operativsystemet och hårdvaran stöder dem (makron) korrekt samtidigt som de bibehåller samma programmeringsgränssnitt.
De flesta moderna programmeringsspråk drar inte nytta av dessa funktioner, förlitar sig mer på traditionella villkorliga uttalanden if...then...else..., vilket lämnar kompilatorn med uppgiften att extrahera värdelös kod från programmet som kompileras.
Se digrafer och trigrafer på C/C++-språk.
Förprocessorn bearbetar digraferna “ %:” (“ #”), “ %:%:” (“ ##”) och trigraferna “ ??=” (“ #”), “ ??/” (“ \”).
Förprocessorn anser att sekvensen " %:%: " är två tokens vid bearbetning av C-kod och en token vid bearbetning av C++-kod.
C programmeringsspråk | |
---|---|
Kompilatorer |
|
Bibliotek | |
Egenheter | |
Några ättlingar | |
C och andra språk |
|
Kategori:C programmeringsspråk |