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 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

#include <iostream> #include "otherfile.h"

A fenti két sor a nevezett fájlok tartalmát egyszerűen bemásolja az #include sor 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, a standard library-k között fogja keresni azt. A második példában idézőjelek közé helyeztük el a fájlnevet, így a jelenlegi fájl könyvtárából relatív útvonalon keresi azt a fordító.

Szimbólumok 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 saját szimbólumokat:

#define SAJAT_SZIMBOLUM

#ifdef SAJAT_SZIMBOLUM std::cout << "SAJAT_SZIMBOLUM definiálva van!\n"; #else std::cout << "SAJAT_SZIMBOLUM 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 együtt 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 SAJAT_SZIMBOLUM //Ha SAJAT_SZIMBOLUM nincs definiálva #define SAJAT_SZIMBOLUM #endif

Makrók

#include <iostream>

#define MAX2(x, y) (x > y ? x : y) // 2 érték közül a nagyobbat adja vissza

int main(int argc, char * argv[]) { std::cout << MAX2(10, 12) << std::endl; return 0; } // MAX2(++x,x) meghatározhatatlan eredményhez vezetne

Definiáltuk a MAX2 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 a # é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 // string #define CONC(x, y) x ## y // concatenate

Őrszemek Gyakran előfordul, hogy egy fejállomány áttételes include-ok 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 úgynevezett őrszemek (sentinel, header guard) használata:

// a "myheader.h" fájlban

#ifndef _MYHEADER_H_ #define _MYHEADER_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. 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 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őre definiált szimbólumok __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 C programozási nyelv C++ programozási nyelv

További információk MSDN preprocesszor referencia C++ preprocesszor C preprocesszor

Források Brian Kernigham, Dennis Richie: A C programozási nyelv (, 2006) ISO/IEC 9899:TC2 szabványa

Kategória:Számítógép-programozás