Amint a neve is mutatja, a preprocesszorok olyan programok, amelyek a fordítás előtt feldolgozzák a forráskódunkat. A program megírása és a program végrehajtása között számos lépés van C / C++ nyelven. Nézzük meg ezeket a lépéseket, mielőtt ténylegesen elkezdenénk tanulni a preprocesszorokról.

A fenti ábrán láthatjuk a köztes lépéseket. A programozók által írt forráskódot a program.c fájlban tároljuk, majd ezt a fájlt a preprocesszorok feldolgozzák, és egy kibővített forráskódfájlt generálnak program néven. Ezt a kibővített fájlt a fordítóprogram lefordítja, és egy program .obj nevű objektumkód-fájlt hoz létre. Végül a linkelő összekapcsolja ezt az objektumkódfájlt a könyvtári függvények objektumkódjával, hogy létrehozza a program.exe futtatható fájlt.
A preprocesszor programok preprocesszor direktívákat adnak meg, amelyek megmondják a fordítónak, hogy a forráskódot a fordítás előtt előfeldolgozza. Ezek a preprocesszor direktívák mindegyike egy ‘#’ (hash) szimbólummal kezdődik. A ‘#’ szimbólum azt jelzi, hogy bármilyen utasítás #-sel kezdődik, az a preprocesszor programhoz kerül, és a preprocesszor program végre fogja hajtani ezt az utasítást. Példák néhány preprocesszor direktívára: #include, #define, #ifndef stb. Ne feledje, hogy a # szimbólum csak egy utat ad meg, hogy a preprocesszorhoz fog menni, és az olyan parancsokat, mint az include, a preprocesszor program dolgozza fel. Például az include extra kódot fog beépíteni a programodba. Ezeket a preprocesszor direktívákat bárhol elhelyezhetjük a programunkban.
A preprocesszor direktíváknak 4 fő típusa van:
- Makrók
- File Inclusion
- Conditional Compilation
- Egyéb direktívák
Most ismerjük meg részletesen az egyes direktívákat.
- Makrók: A makrók olyan kódrészletek a programban, amelyeknek valamilyen nevet adunk. Amikor a fordító ezzel a névvel találkozik, a fordító a nevet a tényleges kódrészlettel helyettesíti. A ‘#define’ utasítás egy makró definiálására szolgál. Értelmezzük most a makródefiníciót egy program segítségével:
#include <iostream>#define LIMIT 5int main() { for (int i = 0; i < LIMIT; i++) { std::cout << i << "\n"; } return 0; }
#include <stdio.h>#define LIMIT 5int main() { for (int i = 0; i < LIMIT; i++) { printf("%d \n",i); } return 0; }
Kimenet:
01234
A fenti programban, amikor a fordító a LIMIT szót végrehajtja, azt 5-re cseréli. A ‘LIMIT’ szót a makródefinícióban makrósablonnak nevezzük, az ‘5’ pedig makróbővítésnek.
Megjegyzés: A makródefiníció végén nincs pontosvessző(‘;’). A makródefinícióknak nincs szükségük pontosvesszőre a befejezéshez.
Makrók argumentumokkal: A makróknak érveket is átadhatunk. Az argumentumokkal definiált makrók hasonlóan működnek, mint a függvények. Értelmezzük ezt egy program segítségével:
#include <iostream>#define AREA(l, b) (l * b)int main() { int l1 = 10, l2 = 5, area; area = AREA(l1, l2); std::cout << "Area of rectangle is: " << area; return 0; }
#include <stdio.h>#define AREA(l, b) (l * b)int main() { int l1 = 10, l2 = 5, area; area = AREA(l1, l2); printf("Area of rectangle is: %d", area); return 0; }
Kimenet:
Area of rectangle is: 50
A fenti programból láthatjuk, hogy amikor a fordító a programban AREA(l, b)-t talál, azt a (l*b) utasítással helyettesíti. Nem csak ez, hanem az AREA(l, b) makrósablonhoz átadott értékek is az (l*b) utasítással lesznek helyettesítve. Ezért az AREA(10, 5) egyenlő lesz a 10*5-tel.
- Fájlbővítés: Ez a típusú előfeldolgozói utasítás azt mondja a fordítónak, hogy egy fájlt építsen be a forráskódú programba. Kétféle fájltípust lehet a felhasználó által a programba bevonni:
- Fejlécfájl vagy szabványos fájlok: Ezek az állományok olyan előre definiált függvények definícióját tartalmazzák, mint a printf(), scanf() stb. Ezeket a fájlokat be kell építeni a függvényekkel való munkához. A különböző függvények különböző fejlécfájlokban vannak deklarálva. Például a szabványos I/O funkciók az ‘iostream’ fájlban vannak, míg a string műveleteket végző funkciók a ‘string’ fájlban.
Szintaktika:
- Fejlécfájl vagy szabványos fájlok: Ezek az állományok olyan előre definiált függvények definícióját tartalmazzák, mint a printf(), scanf() stb. Ezeket a fájlokat be kell építeni a függvényekkel való munkához. A különböző függvények különböző fejlécfájlokban vannak deklarálva. Például a szabványos I/O funkciók az ‘iostream’ fájlban vannak, míg a string műveleteket végző funkciók a ‘string’ fájlban.
#include< file_name >
ahol a file_name a beépítendő fájl neve. A ‘<‘ és ‘>’ zárójelek azt mondják a fordítónak, hogy a fájlt a standard könyvtárban keresse.
- felhasználó által definiált fájlok: Amikor egy program nagyon nagy lesz, jó gyakorlat, ha kisebb fájlokra osztjuk fel, és akkor csatoljuk be, amikor csak szükséges. Az ilyen típusú fájlok a felhasználó által definiált fájlok. Ezeket a fájlokat úgy lehet bevonni, mint:
#include"filename"
- Feltételes fordítás: A feltételes fordítási direktívák olyan típusú direktívák, amelyek segítenek a program egy adott részének lefordításában, vagy a program egy adott részének fordításának kihagyásában bizonyos feltételek alapján. Ezt két előfeldolgozási parancs, az ‘ifdef’ és az ‘endif’ segítségével lehet megtenni.
Szintaktika:
#ifdef macro_name statement1; statement2; statement3; . . . statementN;#endif
Ha a ‘macroname’ nevű makró definiálva van, akkor az utasításblokk normálisan végrehajtódik, de ha nincs definiálva, akkor a fordító egyszerűen kihagyja ezt az utasításblokkot.
- Egyéb direktívák: A fenti direktívákon kívül van még két másik direktíva, amelyeket nem gyakran használnak. Ezek a következők:
- #undef irányelv: A #undef direktívát egy meglévő makró undefiniálására használjuk. Ez a direktíva a következőképpen működik:
#undef LIMIT
Ezzel az utasítással a meglévő LIMIT makrót vonjuk vissza. Ezt az utasítást követően minden “#ifdef LIMIT” utasítás értéke hamis lesz.
- #pragma irányelv: Ez az utasítás egy speciális célú utasítás, és bizonyos funkciók be- vagy kikapcsolására szolgál. Az ilyen típusú direktívák fordítóspecifikusak, azaz fordítóról fordítóra változnak. Az alábbiakban néhány #pragma direktívát tárgyalunk:
- #pragma startup és #pragma exit: Ezek a direktívák segítenek megadni azokat a függvényeket, amelyeket a program indítása előtt( mielőtt a vezérlés átkerül a main() parancsra) és közvetlenül a program kilépése előtt (közvetlenül azelőtt, hogy a vezérlés visszatér a main() parancsból) kell futtatni.
Figyelem: Az alábbi program nem fog működni a GCC fordítóval.
Nézzük meg az alábbi programot:
- #pragma startup és #pragma exit: Ezek a direktívák segítenek megadni azokat a függvényeket, amelyeket a program indítása előtt( mielőtt a vezérlés átkerül a main() parancsra) és közvetlenül a program kilépése előtt (közvetlenül azelőtt, hogy a vezérlés visszatér a main() parancsból) kell futtatni.
#include <bits/stdc++.h>using namespace std; void func1(); void func2(); #pragma startup func1#pragma exit func2 void func1() { cout << "Inside func1()\n"; } void func2() { cout << "Inside func2()\n"; } int main() { void func1(); void func2(); cout << "Inside main()\n"; return 0; }
#include <stdio.h>void func1(); void func2(); #pragma startup func1#pragma exit func2void func1() { printf("Inside func1()\n"); }void func2() { printf("Inside func2()\n"); }int main() { void func1(); void func2(); printf("Inside main()\n"); return 0; }
Kimenet:
Inside func1()Inside main()Inside func2()
A fenti kód a GCC fordítóprogramon futtatva az alábbi kimenetet fogja eredményezni:
Inside main()
Ez azért történik, mert a GCC nem támogatja a #pragma indítást vagy kilépést. Az alábbi kódot azonban használhatja a GCC fordítóprogramokon hasonló kimenethez.
#include <stdio.h>void func1(); void func2(); void __attribute__((constructor)) func1(); void __attribute__((destructor)) func2(); void func1() { printf("Inside func1()\n"); }void func2() { printf("Inside func2()\n"); }int main() { printf("Inside main()\n"); return 0; }
- #pragma warn irányelv: Ez a direktíva a fordítás során megjelenő figyelmeztető üzenetek elrejtésére szolgál.
A figyelmeztetéseket az alábbiak szerint rejthetjük el:- #pragma warn -rvl: Ez az utasítás elrejti azokat a figyelmeztetéseket, amelyek akkor jelennek meg, ha egy olyan függvény, amelynek értéket kellene visszaadnia, nem ad vissza értéket.
- #pragma warn -par:
- #pragma warn -rch: Ez az utasítás elrejti azokat a figyelmeztetéseket, amelyek akkor jelennek meg, ha egy függvény nem használja fel a neki átadott paramétereket: Ez az utasítás elrejti azokat a figyelmeztetéseket, amelyek akkor jelennek meg, ha egy kód elérhetetlen. Például: egy függvényben a return utasítás után írt bármely kód elérhetetlen.
Ez a cikk Harsh Agarwal közreműködésével készült. Ha tetszik a GeeksforGeeks és szeretnél hozzájárulni, írhatsz cikket a contribute.geeksforgeeks.org címen, vagy elküldheted a cikkedet a [email protected] címre. Láthatod, hogy a cikked megjelenik a GeeksforGeeks főoldalán, és segíthetsz más geekeknek.