C előfordító

A Wikipédiából, a szabad enciklopédiából

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]