C előfordító
A C előfordító a C és C++ programozási nyelvek tényleges fordítása előtt végrehajtott speciális program. Az előfordító felel más forrásfájlok kezeléséért, felhasználható szimbólumok és makrók definiálására illetve a feltételes fordítás lehetővé tételéért. A hagyományos C programok erősen építenek rá, míg a C++-ban a típushelyesség miatt csak speciális esetekben használják.
Az előfordító feladatai
[szerkesztés]- Az ún. trigráf szekvenciák (??* alakú kifejezések, ??= → #, ??/ → \ stb.) lefordítása a nekik megfelelő karakterre.
- A forrásfájlban fizikailag több sorban elhelyezkedő forráskód logikailag egy sorba történő csoportosítása (ha szükséges).
- Preprocesszor tokenekre bontás, a kommentek helyettesítése whitespace karakterekkel.
- Az előfordítónak a programozó által megadott feladatok végrehajtása (szimbólumok behelyettesítése, feltételes fordítás, makrók, stb.).
Az előfordítónak szóló utasítások első sora a kettőskereszt (#, hashmark) karakterrel kezdődik és alapesetben a sor végén véget is ér, de a \ jellel semlegesíthetjük a sorvége jelet.
Fájlok beolvasztása
[szerkesztés]#include <iostream>
#include "otherfile.h"
A fenti két sor a nevezett fájlokat egyszerűen bemásolja az #include
helyére. Az első esetben a forrásfájl nevét a kisebb-nagyobb (<, >) jelek közé írtuk, ezért a fordító a fejállományok szabványos helyén fogja keresni azt. A második példában idézőjelek közé helyeztük el a fájlnevet, így a program saját könyvtárából relatíve keresi azt a fordító.
Szimbólumok
[szerkesztés]Az ún. szimbólumtábla tartalmazza a forráskódban lévő és a fordítóban előre definiált szimbólumokat. Magunk is definiálhatunk ilyen szimbólumokat:
#define SYMBOL
#ifdef SYMBOL
std::cout << "SYMBOL definiálva van!\n";
#else
std::cout << "SYMBOL nincs definiálva!\n";
#endif
Ebben az esetben a define utasítással definiáltunk egy szimbólumot, a további részekben pedig megvizsgáljuk, hogy létezik-e. Ha igen, akkor a megfelelő programrész továbbadódik a fordítónak, ha nem, akkor az esetleges else rész kerül fordításra. A lehetséges utasítások:
- #define: egy szimbólum definiálása és értékének megadása, utóbbi opcionális.
- #undef: egy szimbólum eltávolítása a szimbólumtáblázatból
- #ifdef: if define egy szimbólum létezését vizsgálja, csak endif -fel együtt használható.
- #ifndef: if not define egy szimbólum nemlétét ellenőrzi, csak endif -fel használható.
- #else: az else lezárja a feltételt, majd az ellenkező esetnek kezd egy ágat.
- #endif: feltétel lezárása.
A szimbólumokhoz értéket is rendelhetünk, a fordító a forráskódban az azonosító összes előfordulási helyét lecseréli az értékére (ha nem adtunk meg, akkor semmire):
#define LENGTH 10
int * v = new int [LENGTH]; //10 elemű dinamikus tömb
Egyszerű szövegbehelyettesítést végez, ezért nem várt hatásai lehetnek, ha a tapasztalatlan programozó túl sok logikát feltételez róla.
A feltételekben használhatóak a defined és !defined predikátumok is.
#if !defined SYMBOL //Ha SYMBOL nincs definiálva
#define SYMBOL
Makrók
[szerkesztés]#include <iostream>
#define MAX(x, y) (x > y ? x : y)
int main(int argc, char * argv[])
{
std::cout << MAX(10, 12) << std::endl;
return 0;
}
//MAX(++x,x) meghatározhatatlan eredményhez vezetne
Definiáltuk a MAX makrót, amely a feltételes operátor segítségével a két érték közül a nagyobbikat adja vissza. Mivel a makró egy egyszerű szövegbehelyettesítés, bármely típusra meghívhatóak, fordítási és futásidejű hiba keletkezhet.
Makrók esetében használhatjuk az # és ## operátorokat. Előbbi a megadott paramétert adja vissza stringként, utóbbi összefűz két paramétert:
#define STR(x) #x
#define CONC(x, y) x ## y
Őrszemek
[szerkesztés]Gyakran előfordul, hogy egy fejállomány áttételes includeok hatására többször is bekerülne egy forrásfájlba, ami fordítási hibát okozna, bevett gyakorlat ennek kivédésére ún. őrszemeket (sentinel, header guard) használata:
#ifndef _HEADERFILE_H_
#define _HEADERFILE_H_
/*
Osztály-, függvénydefiníciók, stb.
*/
#endif
Amikor a fordító először találkozik a fejállománnyal megvizsgálja, hogy létezik -e már a szimbólum. Ha nem akkor definiálja és beírja a szimbólumtáblázatba és amikor legközelebb utasítást talál a fájl használatára egyszerűen figyelmen kívül hagyja azt.
#line, #pragma, #error
[szerkesztés]A #line direktíva lehetővé teszi, hogy tetszőleges hibaüzenetet használjunk, ha fordítási hiba következik be:
#line 1 "bad operator"
a === b;
A #pragma segítségével utasításokat hagyhatunk a fordítónak. Nem minden fordító használja ugyanazokat a kapcsolókat, ezért ha ismeretlen utasítást tartalmaz, akkor a #pragma figyelmen kívül kerül.
Az #error megszakítja a fordítási folyamatot:
#ifndef SOMETHINGWHATNEED
#error "Something missing!"
#endif
Előredefiniált szimbólumok
[szerkesztés]- __LINE__: egész szám, annak a sornak a számát tartalmazza amiben szerepel.
- __FILE__: a forrásfájl nevét tartalmazza.
- __DATE__: a dátumot tartalmazza MMM:DD:YYYY formátumban.
- __TIME__: az időt tartalmazza HH:MM:SS formában.
- __cplusplus: egész érték. Ha a fordító teljes mértékben megfelel a szabványnak, akkor az értéke nagyobb vagy egyenlő mint 199711L az alkalmazott szabványtól függően.
Kapcsolódó hivatkozások
[szerkesztés]További információk
[szerkesztés]Források
[szerkesztés]- Brian Kernigham, Dennis Richie: A C programozási nyelv (ISBN 9789631605525, 2006)
- ISO/IEC 9899:TC2 szabványa