C/C++ preprocessor ( eng. preprocessor , preprocessor) - et program , der forbereder programkoden i C / C++ til kompilering .
Forprocessoren gør følgende:
Betinget kompilering giver dig mulighed for at vælge, hvilken kode du vil kompilere baseret på:
Preprocessor-trin:
C/C++ præprocessorsproget er ikke Turing komplet, om ikke andet fordi det er umuligt at få præprocessoren til at hænge ved hjælp af direktiver. Se rekursiv funktion (beregnelighedsteori) .
Et præprocessordirektiv (kommandolinje) er en linje i kildekoden, der har følgende format: #ключевое_слово параметры:
Søgeordsliste:
Når direktiver #include "..."og findes #include <...>, hvor "..." er et filnavn, læser præprocessoren indholdet af den specificerede fil, udfører direktiver og substitutioner (substitutioner), erstatter direktivet #includemed et direktiv #lineog det behandlede filindhold.
For at #include "..."søge efter en fil udføres det i den aktuelle mappe og mapper, der er angivet på kompilatorens kommandolinje. For at #include <...>søge efter en fil udføres den i mapper, der indeholder standardbiblioteksfiler (stierne til disse mapper afhænger af implementeringen af compileren).
Hvis der findes et direktiv , #include последовательность-лексем der ikke matcher nogen af de foregående former, betragter det sekvensen af tokens som tekst, der som følge af alle makrosubstitutioner skal give #include <...>eller #include "...". Direktivet genereret på denne måde vil blive yderligere fortolket i overensstemmelse med den modtagne formular.
Inkluderede filer indeholder normalt:
Direktivet #includeer normalt angivet i begyndelsen af filen (i headeren), så inkluderede filer kaldes header -filer .
Et eksempel på at inkludere filer fra C -standardbiblioteket .
#include <math.h> // inkludere matematiske funktionserklæringer #include <stdio.h> // include I/O-funktionserklæringerBrug af en præprocessor anses for ineffektiv af følgende årsager:
Fra 1970'erne begyndte der at dukke metoder op, der erstattede medtagelsen af filer. Java- og Common Lisp-sprogene bruger pakker (søgeord package) (se pakke i Java ), Pascal bruger engelsk. enheder (søgeord unitog uses), i Modula , OCaml , Haskell og Python , moduler. Designet til at erstatte C- og C++- sprogene, bruger D nøgleordene og . moduleimport
Preprocessor konstanter og makroer bruges til at definere små stykker kode.
// konstant #define BUFFER_SIZE ( 1024 ) // makro #define NUMBER_OF_ARRAY_ITEMS( array ) ( sizeof( array ) / sizeof( *(array) ) )Hver konstant og hver makro erstattes af dens tilsvarende definition. Makroer har funktionslignende parametre og bruges til at reducere overhead af funktionskald i tilfælde, hvor den lille mængde kode, som funktionen kalder, er nok til at forårsage et mærkbart præstationshit.
Eksempel. Definition af makroen max , som tager to argumenter: a og b .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )En makro kaldes ligesom enhver funktion.
z = max ( x , y );Efter udskiftning af makroen vil koden se sådan ud:
z = ( ( x ) > ( y ) a ( x ) : ( y ) );Men sammen med fordelene ved at bruge makroer i C-sproget, for eksempel til at definere generiske datatyper eller fejlfindingsværktøjer, reducerer de også effektiviteten af deres brug noget og kan endda føre til fejl.
For eksempel, hvis f og g er to funktioner, kaldes
z = max ( f (), g () );vil ikke evaluere f() én gang og g() én gang , og sætter den største værdi i z , som man kunne forvente. I stedet vil en af funktionerne blive evalueret to gange. Hvis en funktion har bivirkninger, er det sandsynligt, at dens adfærd vil være anderledes end forventet.
C-makroer kan ligne funktioner, skabe ny syntaks til en vis grad, og kan også udvides med vilkårlig tekst (selvom C-kompileren kræver, at teksten er i fejlfri C-kode eller formateret som en kommentar), men de har nogle begrænsninger som softwarestrukturer. Funktionslignende makroer kan for eksempel kaldes som "rigtige" funktioner, men en makro kan ikke overføres til en anden funktion ved hjælp af en markør, fordi selve makroen ikke har nogen adresse.
Nogle moderne sprog bruger typisk ikke denne form for metaprogrammering ved hjælp af makroer som tegnstrengsfuldførelser, der er afhængige af enten automatisk eller manuel ledning af funktioner og metoder, men i stedet andre måder at abstrakte på såsom skabeloner , generiske funktioner eller parametrisk polymorfi . Især inline-funktioner en af de største mangler ved makroer i moderne versioner af C og C++, da en inline-funktion giver makroer fordelen ved at reducere overheaden af et funktionskald, men dens adresse kan sendes i en pointer for indirekte kalder eller bruges som parameter. Ligeledes er problemet med flere evalueringer nævnt ovenfor i maks makroen irrelevant for indbyggede funktioner.
Du kan erstatte #define konstanter med enums og makroer med funktioner inline.
Operatører # og ##Disse operatorer bruges ved oprettelse af makroer. Operatoren # før en makroparameter omslutter den i dobbelte anførselstegn, for eksempel:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );forprocessor konverterer til:
printf ( "42" );Operatoren ## i makroer sammenkæder to tokens, for eksempel:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition ( Object );forprocessor konverterer til:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; Formel beskrivelse af makrosubstitutioner1) Kontrollinjen i følgende formular tvinger præprocessoren til at erstatte identifikatoren med en sekvens af tokens gennem resten af programteksten:
#define identifier token_sequenceI dette tilfælde kasseres hvide tegn i begyndelsen og slutningen af sekvensen af tokens. En gentagen #define-linje med samme identifikator betragtes som en fejl, hvis sekvenserne af tokens ikke er identiske (uoverensstemmelser i mellemrumstegn betyder ikke noget).
2) En streng med følgende form, hvor der ikke må være mellemrum mellem den første identifikator og den indledende parentes, er en makrodefinition med parametre specificeret af identifier-list.
#define identifier(list_of_identifiers) sequence_of_tokensSom i den første form kasseres mellemrumstegnene i begyndelsen og slutningen af tokensekvensen, og makroen kan kun omdefineres med den samme nummer- og navneparameterliste og den samme tokensekvens.
En kontrollinje som denne fortæller præprocessoren at "glemme" definitionen givet til identifikatoren:
#undef identifikatorAnvendelse af #undef-direktivet på en tidligere udefineret identifikator betragtes ikke som en fejl.
{
Substitutionsprocessen påvirkes af to specielle operatørskilte.
}
Et udråbstegn (!) markerer de regler, der er ansvarlige for rekursiv invokation og definitioner.
Eksempel på makroudvidelse #define cat( x, y ) x ## yMakrokaldet "cat(var, 123)" vil blive erstattet med "var123". At kalde "cat(cat(1, 2), 3)" vil dog ikke give det ønskede resultat. Overvej forprocessorens trin:
0: kat( kat( 1, 2 ), 3 ) 1: kat( 1, 2 ) ## 3 2: kat(1, 2)3"##"-operationen forhindrede korrekt udvidelse af argumenterne for det andet "cat"-kald. Resultatet er følgende række af tokens:
kat ( 1 , 2 ) 3hvor ")3" er resultatet af sammenkædning af det sidste token af det første argument med det første token af det andet argument, er ikke et gyldigt token.
Du kan angive det andet makroniveau som følger:
#define xcat( x, y ) cat( x, y )Kaldet "xcat(xcat(1, 2), 3)" vil blive erstattet med "123". Overvej forprocessorens trin:
0: xcat( xcat( 1, 2 ), 3 ) 1: kat( xcat( 1, 2 ), 3 ) 2: kat( kat( 1, 2 ), 3 ) 3: kat( 1 ## 2, 3) 4: kat ( 12, 3 ) 5:12##3 6:123Alt gik godt, fordi "##"-operatøren ikke deltog i udvidelsen af "xcat"-makroen.
Mange statiske analysatorer er ikke i stand til at behandle makroer korrekt, så kvaliteten af statisk analyse reduceres .
Konstanter genereret automatisk af præprocessoren:
C-forprocessoren giver mulighed for at kompilere med betingelser. Dette giver mulighed for forskellige versioner af den samme kode. Typisk bruges denne tilgang til at tilpasse programmet til compilerplatformen, tilstanden (den debuggede kode kan fremhæves i den resulterende kode) eller evnen til at kontrollere filforbindelsen nøjagtigt én gang.
Generelt skal programmøren bruge en konstruktion som:
# ifndef FOO_H # definer FOO_H ... ( header filkode )... # Afslut HvisDenne "makrobeskyttelse" forhindrer en header-fil i at blive dobbeltinkluderet ved at kontrollere, om der findes den makro, som har samme navn som header-filen. Definitionen af FOO_H-makroen opstår, når header-filen først behandles af præprocessoren. Så, hvis denne header-fil er medtaget igen, er FOO_H allerede defineret, hvilket får præprocessoren til at springe hele teksten i denne header-fil over.
Det samme kan gøres ved at inkludere følgende direktiv i header-filen:
# pragma én gangPreprocessor-betingelser kan specificeres på flere måder, for eksempel:
# ifdef x ... #andet ... # Afslut Hviseller
# ifx ... #andet ... # Afslut HvisDenne metode bruges ofte i systemhovedfiler til at teste for forskellige muligheder, hvis definition kan variere afhængigt af platformen; f.eks. bruger Glibc - biblioteket funktionskontrol-makroer til at verificere, at operativsystemet og hardwaren understøtter dem (makroerne) korrekt, mens de bevarer den samme programmeringsgrænseflade.
De fleste moderne programmeringssprog udnytter ikke disse funktioner, idet de stoler mere på traditionelle betingede sætninger if...then...else..., hvilket efterlader compileren med opgaven at udtrække ubrugelig kode fra det program, der kompileres.
Se digrafer og trigrafer på C/C++ sprog.
Forprocessoren behandler digraferne “ %:” (“ #”), “ %:%:” (“ ##”) og trigraferne “ ??=” (“ #”), “ ??/” (“ \”).
Forprocessoren anser sekvensen " %:%: " for at være to tokens ved behandling af C-kode og et token ved behandling af C++-kode.
C programmeringssprog | |
---|---|
Kompilere |
|
Biblioteker | |
Ejendommeligheder | |
Nogle efterkommere | |
C og andre sprog |
|
Kategori:C programmeringssprog |