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 5
int
main()
{
for
(
int
i = 0; i < LIMIT; i++) {
std::cout << i <<
"\n"
;
}
return
0;
}
#include <stdio.h>
#define LIMIT 5
int
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 func2
void
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.