C / C ++ előfeldolgozó ( eng. pre processor , preprocessor ) - olyan program , amely előkészíti a programkódot C / C ++ nyelven fordításhoz .
Az előfeldolgozó a következőket teszi:
A feltételes fordítás lehetővé teszi, hogy kiválassza, hogy melyik kódot fordítsa le:
Az előfeldolgozó lépései:
A C/C++ előfeldolgozó nyelv nem Turing-teljes, már csak azért is, mert az előfeldolgozót nem lehet lefagyni direktívák segítségével. Lásd rekurzív függvény (számítási elmélet) .
Az előfeldolgozó direktíva (parancssor) egy sor a forráskódban, amelynek formátuma a következő #ключевое_слово параметры:
Kulcsszólista:
#include "..."Az és direktívák megtalálásakor #include <...>, ahol a "..." egy fájlnév, az előfeldolgozó beolvassa a megadott fájl tartalmát, végrehajtja a direktívákat és helyettesítéseket (helyettesítéseket), lecseréli az direktívát #includeegy direktívára #lineés a feldolgozott fájl tartalmát.
A #include "..."fájl kereséséhez a fordító parancssorában megadott aktuális mappában és mappákban kell végrehajtani. A #include <...>fájl keresése szabványos könyvtári fájlokat tartalmazó mappákban történik (a mappák elérési útja a fordító megvalósításától függ).
Ha olyan direktívát találunk, #include последовательность-лексем amely nem egyezik az előző formák egyikével sem, akkor a tokenek sorozatát szövegnek tekinti, amelynek minden makróhelyettesítés eredményeként vagy -t kell #include <...>adnia #include "...". Az így generált direktíva a beérkezett forma szerint kerül további értelmezésre.
A mellékelt fájlok általában a következőket tartalmazzák:
Az irányelvet általában a fájl elején (a fejlécben) adják meg, ezért a benne foglalt fájlokat fejlécfájloknak#include nevezzük .
Példa a C szabványos könyvtárból származó fájlok felvételére .
#include <math.h> // matematikai függvények deklarációi #include <stdio.h> // tartalmazza az I/O függvény deklarációitAz előfeldolgozó használata a következő okok miatt nem hatékony:
Az 1970-es évektől kezdődően olyan módszerek kezdtek megjelenni, amelyek felváltották a fájlok felvételét. A Java és a Common Lisp nyelvek csomagokat használnak (kulcsszó package) (lásd a Java csomagot ), a Pascal pedig az angolt. egységek (kulcsszavak unités uses), a Modula , OCaml , Haskell és Python modulokban. A C és C++ nyelvek helyettesítésére tervezett D a és kulcsszavakat használja . moduleimport
Az előfeldolgozó konstansokat és makrókat kis kódrészletek meghatározására használják .
// állandó #define BUFFER_SIZE ( 1024 ) // makró #define NUMBER_OF_ARRAY_ITEMS( array ) ( sizeof( array ) / sizeof( *(array) ) )Minden állandót és makrót a megfelelő definícióval helyettesítenek. A makrók függvényszerű paraméterekkel rendelkeznek, és a függvényhívások többletterhelésének csökkentésére szolgálnak olyan esetekben, amikor a függvényhívás által meghívott kód kis mennyisége elegendő ahhoz, hogy észrevehető teljesítménycsökkenést okozzon.
Példa. A max makró definíciója , amely két argumentumból áll: a és b .
#define max( a, b ) ( (a) > (b) ? (a) : (b) )A makrót ugyanúgy hívják, mint minden függvényt.
z = max ( x , y );A makró cseréje után a kód így fog kinézni:
z = ( ( x ) > ( y ) ? ( x ) : ( y ) );A makrók C nyelvben való használatának előnyei mellett, például általános adattípusok vagy hibakereső eszközök meghatározásához, némileg csökkentik a használatuk hatékonyságát, és akár hibákhoz is vezethetnek.
Például, ha f és g két függvény, akkor a hívás
z = max ( f (), g () );nem fogja kiértékelni az f() -t egyszer és a g() -t egyszer , és a legnagyobb értéket adja z -be , ahogyan azt várhatnánk. Ehelyett az egyik függvény kétszer kerül kiértékelésre. Ha egy függvénynek vannak mellékhatásai, akkor valószínű, hogy viselkedése eltér a várttól.
A C makrók lehetnek függvények, bizonyos mértékig új szintaxist hoznak létre, és tetszőleges szöveggel is kiegészíthetők (bár a C fordító megköveteli, hogy a szöveg hibamentes C kódban legyen, vagy megjegyzésként formázza), de vannak korlátai. mint a szoftverstruktúrák. A függvényszerű makrókat például úgy hívhatjuk, mint "igazi" függvényeket, de egy makrót nem lehet mutató segítségével átadni egy másik függvénynek, mert magának a makrónak nincs címe.
Egyes modern nyelvek jellemzően nem használják ezt a fajta metaprogramozást , makrókat használva karaktersorozat-kiegészítésként, és a funkciók és módszerek automatikus vagy kézi bekötésére támaszkodnak, hanem az absztrakció más módjaira, például sablonokra , általános függvényekre vagy parametrikus polimorfizmusra . Az inline függvények különösen a makrók egyik fő hiányosságát a C és C++ modern verzióiban, mivel a beágyazott függvény a makrók előnyét biztosítja a függvényhívás többletköltségének csökkentésében, de a címe átadható egy mutatóban közvetett célokra. hívásokat, vagy paraméterként használják. Hasonlóképpen, a többszörös kiértékelés fent említett problémája a max makróban irreleváns a beépített függvények szempontjából.
A #define konstansokat enumokkal, a makrókat pedig függvényekkel helyettesítheti inline.
# és ## operátorEzeket az operátorokat makrók létrehozásakor használják. A makróparaméter előtti # operátor dupla idézőjelek közé teszi, például:
#define make_str( bar ) # bar printf ( make_str ( 42 ) );Az előfeldolgozó a következőkre konvertál:
printf ( "42" );A makrók ## operátora két tokent köt össze, például:
#define MakePosition( x ) x##X, x##Y, x##Width, x##Height int MakePosition ( Objektum );Az előfeldolgozó a következőkre konvertál:
int ObjectX , ObjectY , ObjectWidth , ObjectHeight ; A makróhelyettesítések formális leírása1) A következő űrlap vezérlősora arra kényszeríti az előfeldolgozót, hogy a programszöveg többi részében az azonosítót egy token sorozatra cserélje:
#define azonosító token_sequenceEbben az esetben a tokenek sorozat elején és végén lévő szóköz karaktereket el kell dobni. Az ismétlődő #define sor ugyanazzal az azonosítóval hibának minősül, ha a tokenek sorozatai nem azonosak (a szóközök közötti eltérések nem számítanak).
2) A következő formájú karakterlánc, ahol nem lehetnek szóközök az első azonosító és a nyitó zárójel között, egy makródefiníció az azonosítólista által meghatározott paraméterekkel.
#define identifier(azonosítók_listája) tokenek_sorrendjeAz első formához hasonlóan a token sorozat elején és végén lévő szóköz karakterek el lesznek vetve, és a makró csak ugyanazzal a szám- és névparaméter-listával és ugyanazzal a token sorozattal definiálható újra.
Az ehhez hasonló vezérlősor arra utasítja az előfeldolgozót, hogy "felejtse el" az azonosítónak adott definíciót:
#undef azonosítóAz #undef direktíva alkalmazása egy korábban nem definiált azonosítóra nem tekinthető hibának.
{
A helyettesítési folyamatot két speciális kezelőtábla befolyásolja.
}
Felkiáltójel (!) jelöli a rekurzív hívásért és definíciókért felelős szabályokat.
Makróbővítési példa #define cat( x, y ) x ## yA "cat(var, 123)" makróhívás helyére "var123" lép. A "cat(cat(1, 2), 3)" meghívása azonban nem hozza meg a kívánt eredményt. Fontolja meg az előfeldolgozó lépéseit:
0: macska( macska( 1, 2 ), 3 ) 1: macska( 1, 2 ) ## 3 2: macska( 1, 2 )3A "##" művelet megakadályozta a második "cat" hívás argumentumainak megfelelő kiterjesztését. Az eredmény a következő token-sorozat:
macska ( 1 , 2 ) 3ahol a ")3" az első argumentum utolsó jelzőjének és a második argumentum első jelzőjének összefűzésének eredménye, nem érvényes token.
A második makrószintet a következőképpen adhatja meg:
#define xcat( x, y ) cat( x, y )Az "xcat(xcat(1, 2), 3)" hívás helyére "123" lép. Fontolja meg az előfeldolgozó lépéseit:
0: xcat( xcat( 1, 2 ), 3 ) 1: macska( xcat( 1, 2 ), 3 ) 2: macska( macska( 1, 2 ), 3 ) 3: macska (1 ## 2, 3) 4: macska( 12, 3 ) 5:12##3 6:123Minden jól ment, mert a "##" operátor nem vett részt az "xcat" makró bővítésében.
Sok statikus analizátor nem képes helyesen feldolgozni a makrókat, így a statikus elemzés minősége csökken .
Az előfeldolgozó által automatikusan generált állandók:
A C előfeldolgozó biztosítja a feltételekkel történő fordítás lehetőségét. Ez lehetővé teszi ugyanazon kód különböző verzióinak lehetőségét. Általában ezt a megközelítést használják a program testreszabására a fordítóplatformhoz, az állapothoz (a hibakereső kód kiemelhető a kapott kódban), vagy a fájlkapcsolat pontos egyszeri ellenőrzésének képességéhez.
Általában a programozónak olyan konstrukciót kell használnia, mint:
# ifndef FOO_H # define FOO_H ...( fejlécfájl kódja ) ... # endifEz a „makróvédelem” megakadályozza, hogy egy fejlécfájl duplán kerüljön bele azáltal, hogy ellenőrzi a makró létezését, amelynek neve megegyezik a fejlécfájléval. A FOO_H makró meghatározása akkor következik be, amikor az előfeldolgozó először feldolgozza a fejlécfájlt. Ezután, ha ez a fejlécfájl újra szerepel, a FOO_H már definiálva van, így az előfeldolgozó kihagyja ennek a fejlécfájlnak a teljes szövegét.
Ugyanezt megteheti a következő direktíva beillesztésével a fejlécfájlba:
# pragma egyszerAz előfeldolgozó feltételei többféleképpen is megadhatók, például:
# ifdef x ... #egyéb ... # endifvagy
# ifx ... #egyéb ... # endifEzt a módszert gyakran használják rendszerfejléc-fájlokban a különféle képességek tesztelésére, amelyek meghatározása platformonként változhat; például a Glibc könyvtár szolgáltatás-ellenőrző makrókat használ annak ellenőrzésére, hogy az operációs rendszer és a hardver megfelelően támogatja-e ezeket (a makrókat), miközben ugyanazt a programozási felületet fenntartja.
A legtöbb modern programozási nyelv nem használja ki ezeket a funkciókat, inkább a hagyományos feltételes utasításokra hagyatkozik if...then...else..., így a fordító feladata, hogy haszontalan kódot vonjon ki a fordítandó programból.
Lásd a digráfokat és trigráfokat C/C++ nyelveken.
Az előfeldolgozó feldolgozza a „ %:” (“ #”), „ %:%:” (“ ##”) digráfusokat és a „ ??=” (“ #”), „ ??/” (“ \” trigráfokat).
Az előfeldolgozó a " %:%: " sorozatot két tokennek tekinti a C kód feldolgozása során, és egy tokennek a C++ kód feldolgozásakor.
C programozási nyelv | |
---|---|
Fordítók |
|
Könyvtárak | |
Sajátosságok | |
Néhány leszármazott | |
C és más nyelvek |
|
Kategória:C programozási nyelv |