Makrá a preprocesing

Pred tým, ako sa váš program skompiluje, robí sa časť nazývaná preprocesing. V tomto štádiu, sa dejú všeliaké podivnosti s riadkami, ktoré začínajú znakom #. Týmto znakom sa píšu príkazy pre preprocesor.

Z jedným z takýchto príkazov sme sa už stretli, volá sa #include. Tento príkaz nakopíruje do náško kódu obsah hlavičky knižnice ­— teda napr. deklarácie funkcií (ako napr printf v cstdio, či cout v iostreame), nie však definície, tie tam netreba. Pre nás tento príkaz nemá nejaký hlbší význam.

Ďalším príkazom je #define. Tento príkaz sa dá použiť napríklad na definovanie konštánt. Toto môže mať výhodu prehľadnejšieho kódu a zároveň, keď sa rozhodneme zmeniť hodnotu, stačí nám zmeniť ju na jednom mieste. To čo vyrobíme príkazom #define nazývame makro.

// preprocesor odstrani aj komentrare
#define MAXN 1047
#define INF 1023456789
#define eps 1e-8

int main(){
    int A[MAXN];
    for(int i = 0; i < MAXN; ++i)
        A[i] = INF;
}

Tento program sa po preprocessingu zmení na

int main() {
    int A[1047];
    for (int i = 0; i < 1047; ++i)
        A[i] = 1023456789;
}

a až potom sa kompiluje.

Príkaz #define vieme použiť aj na definíciu makier s parametrami. Tieto nám takisto môžu zjednodušiť prácu. Treba si však dávať pozor pri práci s nimi, ako uvidíme na príklade makier rozdiel1, rozdiel2 a rozdiel3. Skôr než si pozriete zvyšok kódu, zamyslite sa, prečo sú makrá rozdiel1 a rozdiel2 zlé.

#define rozdiel1(a, b) a - b
#define rozdiel2(a, b) (a - b)
#define rozdiel3(a, b) ((a) - (b))
// namiesto predoslych makier sa daju pouzit funkcie
// oproti funkciam ma makro vyhodu, ze funguje bez ohladu na typ premennej

// nasledovnu konstrukciu uz funkciou spravime len tazko
#define FOR(i, n) for (int i = 0; i < (n); ++i)

int main() {
    int a = 0, b = 0, c = 0, d = 0;
    // pre toto je makro rozdiel1 zle:
    // namiesto (a-b)*c budeme mat a - b*c;
    a = rozdiel1(a, b) * c;

    // pre toto je makro rozdiel2 zle:
    // namiesto a-(b+c) budeme mat a - b + c;
    a = rozdiel2(a, b + c);

    // konecne spravne makro:
    a = rozdiel3(a, b + c) * d;

    int celkovyrozdiel = 0;
    FOR(i,n) FOR(j, n) {
        celkovyrozdiel = rozdiel3(i, j);
    }
    // na zamyslenie: aka bude hodnota celkovyrozdiel teraz?
}

Posledné, čo si o preprocesingu ukážeme, sú ifdefy. Jednoducho sa vďaka ním dajú vypínať kusy kódu. Napríklad vypínateľné debugovacie výpisy:

#include <iostream>

using namespace std;

// skus zakomentovat nasledujuci riadok, co sa zmeni?
#define DEBUG

#ifdef DEBUG
#define DBG(x) cout << #x << ": " << (x) << endl;
#else
#define DBG(x)
#endif

int main() {
    int a = 7;
    DBG(a);
    DBG(a + 7);
}