C/C++ preprocessor ( angl. preprocessor , preprocessor) - program , který připravuje kód programu v C / C++ pro kompilaci .
Preprocesor dělá následující:
Podmíněná kompilace vám umožňuje vybrat, který kód se má zkompilovat na základě:
Kroky preprocesoru:
Jazyk preprocesoru C/C++ není Turing kompletní, už jen proto, že je nemožné zablokovat preprocesor pomocí direktiv. Viz rekurzivní funkce (teorie vyčíslitelnosti) .
Direktiva preprocesoru (příkazový řádek) je řádek ve zdrojovém kódu, který má následující formát: #ключевое_слово параметры:
Seznam klíčových slov:
Když jsou nalezeny direktivy #include "..."a #include <...>, kde "..." je název souboru, preprocesor načte obsah zadaného souboru, provede direktivy a substituce (substituce), nahradí direktivu #includedirektivou #linea obsah zpracovaného souboru.
Hledání #include "..."souboru se provádí v aktuální složce a složkách zadaných na příkazovém řádku kompilátoru. Hledání #include <...>souboru se provádí ve složkách obsahujících soubory standardní knihovny (cesty k těmto složkám závisí na implementaci kompilátoru).
Pokud je nalezena direktiva , která #include последовательность-лексем neodpovídá žádné z předchozích forem, považuje posloupnost tokenů za text, který by v důsledku všech makro substitucí měl dát #include <...>nebo #include "...". Takto vygenerovaná směrnice bude dále interpretována v souladu s obdrženým formulářem.
Zahrnuté soubory obvykle obsahují:
Direktiva #includeje obvykle uvedena na začátku souboru (v hlavičce), takže zahrnuté soubory se nazývají hlavičkové soubory .
Příklad zahrnutí souborů ze standardní knihovny C .
#include <math.h> // zahrnout deklarace matematických funkcí #include <stdio.h> // zahrnout deklarace I/O funkcePoužití preprocesoru je považováno za neefektivní z následujících důvodů:
Počínaje 70. léty se začaly objevovat metody nahrazující vkládání souborů. Jazyky Java a Common Lisp používají balíčky (klíčové slovo package) (viz balíček v Javě ), Pascal používá angličtinu. jednotky (klíčová slova unita uses), v modulech Modula , OCaml , Haskell a Python . Navrženo k nahrazení jazyků C a C++ , D používá klíčová slova a . moduleimport
K definování malých částí kódu se používají konstanty a makra preprocesoru .
// konstanta #define BUFFER_SIZE ( 1024 ) // makro #define NUMBER_OF_ARRAY_ITEMS( pole ) ( sizeof( pole ) / sizeof( *(pole) ) )Každá konstanta a každé makro je nahrazeno odpovídající definicí. Makra mají parametry podobné funkcím a používají se ke snížení režie volání funkcí v případech, kdy malé množství kódu, který funkce volá, stačí k tomu, aby způsobil znatelný výkon.
Příklad. Definice makra max , které má dva argumenty : aab .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )Makro se volá stejně jako každá funkce.
z = max ( x , y );Po nahrazení makra bude kód vypadat takto:
z = ( ( x ) > ( y ) ? ( x ) : ( y ) );Spolu s výhodami použití maker v jazyce C například pro definování generických datových typů nebo ladicích nástrojů však také poněkud snižují efektivitu jejich použití a mohou dokonce vést k chybám.
Pokud jsou například f a g dvě funkce, volání
z = max ( f (), g () );nevyhodnotí f() jednou a g() jednou a vloží největší hodnotu do z , jak byste mohli očekávat. Místo toho bude jedna z funkcí vyhodnocena dvakrát. Pokud má funkce vedlejší účinky, je pravděpodobné, že její chování bude jiné, než se očekávalo.
Makra C mohou být jako funkce, do určité míry vytvářejí novou syntaxi a mohou být také rozšířena o libovolný text (ačkoli kompilátor C vyžaduje, aby byl text v bezchybném kódu C nebo formátován jako komentář), ale mají určitá omezení. jako softwarové struktury. Například makra podobná funkcím mohou být nazývána jako „skutečné“ funkce, ale makro nelze předat jiné funkci pomocí ukazatele, protože samotné makro nemá žádnou adresu.
Některé moderní jazyky obvykle nepoužívají tento druh metaprogramování pomocí maker jako doplňování řetězců znaků, spoléhají na automatické nebo ruční zapojení funkcí a metod, ale místo toho jiné způsoby abstrakce, jako jsou šablony , generické funkce nebo parametrický polymorfismus . Zejména inline funkce jednomu z hlavních nedostatků maker v moderních verzích C a C++, protože inline funkce poskytuje výhodu maker při snižování režie volání funkce, ale její adresa může být předána v ukazateli pro nepřímé volá nebo se používá jako parametr. Stejně tak problém vícenásobného vyhodnocení uvedený výše v makru max je pro vestavěné funkce irelevantní.
Konstanty #define můžete nahradit výčty a makra funkcemi inline.
Operátoři # a ##Tyto operátory se používají při vytváření maker. Operátor # před parametrem makra jej uzavírá do dvojitých uvozovek, například:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );preprocesor se převede na:
printf ( "42" );Operátor ## v makrech zřetězí dva tokeny, například:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition ( Object );preprocesor se převede na:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; Formální popis makro substitucí1) Řídicí řádek následujícího formuláře nutí preprocesor nahradit identifikátor sekvencí tokenů ve zbytku textu programu:
#define identifikátor token_sequenceV tomto případě jsou prázdné znaky na začátku a na konci sekvence tokenů zahozeny. Opakovaný řádek #define se stejným identifikátorem je považován za chybu, pokud sekvence tokenů nejsou identické (neshody v mezerách nezáleží).
2) Řetězec následujícího tvaru, kde mezi prvním identifikátorem a úvodní závorkou nesmí být žádné mezery, je definice makra s parametry určenými seznamem identifikátorů.
#define identifikátor(seznam_identifikátorů) sekvence_tokenůStejně jako v prvním formuláři jsou prázdné znaky na začátku a na konci sekvence tokenů zahozeny a makro lze předefinovat pouze se stejným seznamem parametrů čísla a názvu a stejnou sekvencí tokenů.
Řídicí řádek, jako je tento, říká preprocesoru, aby „zapomněl“ definici daného identifikátoru:
#undef identifikátorPoužití direktivy #undef na dříve nedefinovaný identifikátor se nepovažuje za chybu.
{
Proces substituce je ovlivněn dvěma speciálními operátorskými znaky.
}
Vykřičník (!) označuje pravidla zodpovědná za rekurzivní vyvolání a definice.
Příklad rozšíření makra #define cat( x, y ) x ## yVolání makra "cat(var, 123)" bude nahrazeno "var123". Volání "cat(cat(1, 2), 3)" však nepřinese požadovaný výsledek. Zvažte kroky preprocesoru:
0: cat( cat( 1, 2), 3) 1: cat( 1, 2) ## 3 2: kat( 1, 2)3Operace "##" zabránila správnému rozšíření argumentů druhého volání "cat". Výsledkem je následující řetězec tokenů:
kočka ( 1 , 2 ) 3kde ")3" je výsledkem zřetězení posledního tokenu prvního argumentu s prvním tokenem druhého argumentu, není platný token.
Druhou úroveň makra můžete určit následovně:
#define xcat( x, y ) cat( x, y)Volání "xcat(xcat(1, 2), 3)" bude nahrazeno "123". Zvažte kroky preprocesoru:
0: xcat( xcat( 1, 2), 3) 1: cat( xcat( 1, 2), 3) 2: cat( cat( 1, 2), 3) 3: cat( 1 ## 2, 3 ) 4: kočka ( 12, 3 ) 5:12##3 6:123Vše proběhlo v pořádku, protože operátor „##“ se nepodílel na rozšíření makra „xcat“.
Mnoho statických analyzátorů není schopno správně zpracovat makra, takže kvalita statické analýzy je snížena .
Konstanty generované automaticky preprocesorem:
C preprocesor poskytuje schopnost kompilace s podmínkami. To umožňuje možnost různých verzí stejného kódu. Obvykle se tento přístup používá k přizpůsobení programu pro platformu kompilátoru, stav (odladěný kód lze zvýraznit ve výsledném kódu) nebo možnost přesně jednou zkontrolovat připojení souboru.
Obecně platí, že programátor potřebuje použít konstrukci jako:
# ifndef FOO_H # definovat FOO_H ...( kód hlavičkového souboru )... # endifTato "ochrana maker" zabraňuje dvojitému zahrnutí souboru záhlaví kontrolou existence tohoto makra, které má stejný název jako soubor záhlaví. K definici makra FOO_H dochází, když preprocesor poprvé zpracuje hlavičkový soubor. Pak, pokud je tento hlavičkový soubor znovu zahrnut, FOO_H je již definován, což způsobí, že preprocesor přeskočí celý text tohoto hlavičkového souboru.
Totéž lze provést zahrnutím následující směrnice do hlavičkového souboru:
#pragma jednouPodmínky preprocesoru lze zadat několika způsoby, například:
# ifdef x ... #jinak ... # endifnebo
# ifx ... #jinak ... # endifTato metoda se často používá v souborech hlaviček systému k testování různých schopností, jejichž definice se může lišit v závislosti na platformě; například knihovna Glibc používá makra pro kontrolu funkcí k ověření, zda je operační systém a hardware správně podporují (makra) při zachování stejného programovacího rozhraní.
Většina moderních programovacích jazyků tyto funkce nevyužívá, spoléhá se spíše na tradiční podmíněné příkazy if...then...else..., takže kompilátor má za úkol extrahovat nepotřebný kód z kompilovaného programu.
Viz digrafy a trigrafy v jazycích C/C++.
Preprocesor zpracovává digrafy „ %:“ (“ #”), „ %:%:“ (“ ##”) a trigrafy „ ??=“ (“ #”), „ ??/“ (“ \”).
Preprocesor považuje sekvenci " %:%: " za dva tokeny při zpracování kódu C a jeden token při zpracování kódu C++.
C programovací jazyk | |
---|---|
Kompilátory |
|
Knihovny | |
Zvláštnosti | |
Někteří potomci | |
C a další jazyky |
|
Kategorie:C programovací jazyk |