Preprocesor

Před vlastním překladem zdrojového kódu je kód upraven tzv. preprocesorem. Například v Linuxu lze spustit preprocesor příkazem cpp (C Pre Processor). Preprocesor si volá překladač sám, takže o tom ani nemusíte vědět. Preprocesor odstraní ze zdrojového kódu komentáře a rozvine makra, které jsou ve zdrojovém kódu. Všechny direktivy preprocesoru začínají znakem # (čti šarp).

Například, když zapíšete do zdrojového kódu #include <stdio.h>, tak preprocesor vloží na místo tohoto řádku obsah souboru stdio.h.

Direktiva preprocesoru musí být první na novém řádku, před ní mohou být jen „bílé znaky“ (mezera, tabulátor…).
Direktiva končí s koncem řádku. Pokud chcete pokračovat na novém řádku, pak můžete před „konec řádku“ napsat zpětné lomítko.

V následující tabulce jsou direktivy preprocesoru. Význam některých z nich proberu.

Direktivy preprocesoru
#define #elif #else #endif #error #ident #if
#ifdef #ifndef #include #line #pragma #undef #warning

Standardní makra

Podle normy ANSI C existují následující makra, které musí každý preprocesor jazyka C znát. Všimněte si, že standardní makra preprocesoru začínají a končí dvěma podtržítky.

Standardní makra
MakroVýznamdatový typ
__DATE__ Datum spuštění preprocesoru string
__TIME__ Čas spuštění preprocesoru string
__FILE__ Jméno aktuálního vstupního souboru string
__LINE__ Pořadové číslo aktuálního řádku int
__STDC__ Určuje, zda překladač splňuje ANSI C int (1 nebo 0)
__cplusplus Určuje, zda byl použit překladač C++ Překladač jazyka C toto makro nedefinuje, v C++ je __cplusplus číslo verze standardu jazyka

Existují i implementačně závislá makra, tedy makra, která jsou definována jen na některých OS a v některých preprocesorech jazyka C. Například makro __linux__ se může hodit ke zjištění, zda program překládáte v linuxu.

Nestandardní makra
MakroVýznamdatový typ
__linux__1) Určuje, zda je zdrojový kód překládán v Linuxu int (1 nebo 0)
__unix__1) Obdobně jako linux int (1 nebo 0)
__MSDOS__ Verse DOSu string
__i386__ Určuje, zda jde o procesor z řady i386 int (1 nebo 0)
__VERSION__ Verze překladače GNU C (gcc). Existuje pochopitelně jen u překladače GNU. string
_MSC_VER Verze překladače Visual Studia. string
_MSC_BUILD Revision number překladače pro Visual Studio. string
Visual Studio

Makra definovaná pro VS najdete třeba tady: Predefined Macros for Visual Studio.

Příklady použití budou následovat.

Definice maker

Kromě standardních maker si můžete vytvářet makra vlastní. Jejich užívání je velice rozšířené, například je lze použít pro definování počtu prvků pole. Takové makro se pak používá v celém zdrojovém kódu a při změně počtu prvků pole stačí změnit jen makro. Makro se definuje za direktivou #define a lze jej zrušit direktivou #undef.

V C++ je zvykem místo maker používat pro definici rozměru pole konstanty. Jak už jsem psal dříve, v jazyku C nemůžete použít konstantu pro definování rozměru pole, ale v C++ ano.

#define NAZEV makro
/* ... odtud dále se ve zdrojovém kód může makro používat ... */
/* kdekoliv použijete NAZEV, preprocesor ho nahradí za makro  */
#undef NAZEV
/* zde už makro zase není definované a nelze používat */

Názvy maker je zvykem psát velkými písmeny (stejně jako názvy konstant).

V příkladu ukáži použití standardních i uživatelských maker. Takto vypadá zdrojový kód před zpracováním preprocesorem.

Visual Studio

Na řádcích 10 až 12 definuji makro __STDC__ jako 0, pokud není definováno (nemusí být) pomocí podmíněného překladu (který vysvětlím dále).
Řádky 15 až 25 jsou tu kvůli Visual Studiu. Definují makra, která v něm definována nejsou. (Spoléhám se na to, že makro _MSC_VER je definováno jen překladačem Visual Studio.)

  1. /*------------------------------------------------*/
  2. /* c11/makra.c                                    */
  3.  
  4. #include <stdio.h>
  5. #include <stddef.h>
  6.  
  7. #define N 5
  8. #define VERSE "1.0.0"
  9.  
  10. #ifndef __STDC__
  11. #define __STDC__ 0
  12. #endif
  13.  
  14. /* Definice pro Visual Studio */
  15. #ifdef _MSC_VER
  16.         #define ZU "Iu"
  17.         #define SZU "lu"
  18.         #define __VERSION__ "Visual Studio"
  19.         #ifdef _M_IX86
  20.                 #define __i386__ 1
  21.         #else
  22.                 #define __i386__ 0
  23.         #endif
  24. #else
  25.         #define ZU "zu"
  26.         #define SZU "zu"
  27. #endif
  28.  
  29. #ifndef __i386__
  30. #define __i386__ 0
  31. #endif
  32.  
  33. int main(void)
  34. {
  35.     int pole[N];
  36.     size_t x;
  37.  
  38.     printf("ANSI C:\t%s\t%i\n", __STDC__ ? "ANO" : "NE ", __STDC__);
  39.     printf("i386:\t%s\t%s\n", __i386__ ? "ANO" : "NE ", __VERSION__);
  40.     printf("Tento program byl prelozen %s v %s.\n"
  41.            "Prave jsme na radku %i.\n", __DATE__, __TIME__, __LINE__);
  42.  
  43.     printf("Zadejte index pole <O, %u>: ", N - 1);
  44.     scanf("%" SZU, &x);
  45.  
  46.     x = x >= N ? N - 1 : x;
  47.  
  48.     printf("Zadejte hodnotu pole[%" ZU "]: ", x);
  49.     scanf("%i", &pole[x]);
  50.     printf("verse programu %s\n", VERSE);
  51.  
  52.     return 0;
  53. }
  54.  
  55. /*------------------------------------------------*/
Visual Studio

Důvod pro definování ZU SZU viz datový typ pro ukazatel.

A takto po zpracování preprocesorem před vlastním překladem. Direktiva #include <stdio.h> je nahrazena zdrojovým kódem souboru <stdio.h>, ale ten jsem z výpisu vypustil (bylo by to moc dlouhé).

int main(void)
{    int pole[5];
    size_t x;

    printf("ANSI C:\t%s\t%i\n", 1 ? "ANO" : "NE ", 1);
    printf("i386:\t%s\t%s\n", 1 ? "ANO" : "NE ",
           "2.95.4 20011002 (Debian prerelease)");
    printf("Tento program byl prelozen %s v %s.\n"
           "Prave jsme na radku %i.\n", "Aug 10 2003", "21:43:55", 17);

    printf("Zadejte index pole <O,%zu>: ", 5 - 1);
    scanf("%zu", &x);

    x = x >= 5 ? 5 - 1 : x;

    printf("Zadejte hodnotu pole[%zu]: ", x);
    scanf("%i", &pole[x]);
    printf("verse programu %s\n", "1.0.0");

    return 0;
}

Teprve tento kód je předán překladači k vytvoření programu.

Výstup z programu:

ANSI C: ANO     1
i386:   ANO     2.95.4 20011002 (Debian prerelease)
Tento program byl prelozen Aug 10 2003 v 21:43:55.
Prave jsme na radku 15.
Zadejte index pole <0,4>: 3
Zadejte hodnotu pole[3]: 50
verse programu 1.0.0

Makra s argumenty

Makra mohou mít také argumenty. Jsou uzavřeny v kulatých závorkách za jménem makra a pokud je více jak jeden argument, jsou odděleny čárkou.

#define NAZEV(argument1, argument2, ...) makro

Mezi jménem makra a závorkou obsahující argumenty nesmí být mezera. Podívejte se na rozdíl následujících maker (převzato z manuálu k preprocesoru cpp-2.95):

#define FOO(x) - 1 / (x)
#define BAR (x) - 1 / (x)

Jejich použití …

a = FOO(10);
b = BAR;

... se vyhodnocení preprocesorem takto:

a = - 1 / (10);
b = (x) - 1 / (x);

Použití maker s argumenty má své záludnosti se kterými je třeba počítat. Vždy si musíte dát pozor na to, jakým způsobem je makro ve zdrojovém kódu rozvinuto. Podívejte se na definici následujících maker:

#define mocnina(X) x*x
#define min(X, Y) ((X) < (Y) ? (X) : (Y))

Jejich použití:

a = mocnina(3+2);
b = mocnina(++n);
c = min (x+y,foo(z));

Vyhodnocení preprocesorem:

a = 3+2*3+2;              // tento problém lze vyřešit závorkami v makru
b = ++n*++n;              // proměnná n se inkrementuje dvakrát
c = ((x+y) < (foo(z)) ? (x+y) : foo(z)); // funkce foo() je volána dvakrát

Kvůli výše uvedeným problémům se nedoporučuje makry nahrazovat funkce. V jazyce C++ se vůbec od používání maker ustupuje, kde to jen jde.

Operátory # a ##

Operátory # a ## se používají v makrech s parametry. Za operátor # se dosadí řetězec, který je stejný jako argument makra. Operátor ## spojí své dva argumenty v jeden řetězec.

Definice maker:

#define VYPOCET(X,Y) printf("%s = %i\n",#X " + " #Y,(X)+(Y));
#define SPOJENI(X,Y) printf("%i %i\n",X ## Y, cislo ## X);

Použití maker:

int cislo5 = 10;
VYPOCET(5,6)
SPOJENI(5,6)

Vyhodnocení preprocesorem:

int cislo5 = 10;
printf("%s = %i\n", "5" " + " "6", (5)+(6));
printf("%i %i\n", 56, cislo5);

Podmíněný překlad

Direktiva #if vyhodnocuje podmínku, která za ní následuje (tedy preprocesor ji vyhodnocuje). Pokud je tato podmínka vyhodnocena jako FALSE, pak vše mezi #if a #endif je ze zdrojového kódu vypuštěno.
V podmínce musí být výrazy, které může vyhodnotit preprocesor (tj. nelze tam používat hodnoty proměnných atp.). Pomocí klíčového slova defined lze vyhodnocovat, zda existuje nějaké makro (zda bylo definováno). Výraz #if defined lze zkrátit výrazem #ifdef. Za podmínkou #if může být další (a další) podmínka #elif. Ta se vyhodnotí v případě, že předchozí podmínka nebyla splněna.
Pokud nejsou splněny žádné podmínky, může být jako poslední direktiva #else, která již žádný výraz nevyhodnocuje a provede se právě v případě, že všechny podmínky za #if a #elif byly vyhodnoceny jako FALSE. Podmíněný překlad se ukončuje direktivou #endif. Popsaná syntaxe vypadá následovně:

#if PODMINKA1
   //... zdrojovy kod pro splnenou podminku1
#elif PODMINKA2
   //... zdrojovy kod pro splnenou podminku2 (kdyz podminka1 nebyla splnena)
#elif PODMINKA3
   //.... zdrojovy kod pro splnenou podminku3 ...
#else
   //... zdrojovy kod, pokud zadna z predchozich podminek nebyla splnena ...
#endif

Podmínky lze negovat pomocí operátoru ! (vykřičník).
Podmínky jsou vyhodnocovány preprocesorem, tedy ještě před tím, než se zdrojový kód předá překladači.

/*------------------------------------------------*/
/* c11/makra2.c                                   */

#include <stdio.h>
#define PT 6

int main(void)
{
#if PT >  5
    printf("Makro PT je vetsi jak 5\n");
#endif

#if defined unix && defined linux
    printf("Hura, ja bezim na unixovem stroji a snad pod linuxem!\n");
#elif defined unix
    printf("Hura, ja bezim pod unixem!\n");
#else
    printf("Hmm, kde jsem se to kruci ocitl??\n");
#endif

    return 0;
}

/*------------------------------------------------*/

Užití knihovny

Knihovnami (hlavičkovými soubory) v jazyce C se nazývají soubory obsahující zdrojový kód. Tyto soubory se do jiného zdrojového kódu začleňují pomocí direktivy #include. Tu používáme již od samého začátku pro začlenění standardní knihovny <stdio.h>.

Standardní knihovny hledá překladač na standardních místech. Nemusíte se tedy zajímat o to kde jsou, to už překladač ví. Jména standardních knihoven jsou uzavřeny ve špičatých závorkách <>.
Knihovny, jejichž jména jsou uzavřeny ve dvojitých uvozovkách, se hledají v aktuálním adresáři. Pokud není nalezen, hledá se ve standardních adresářích.

Vyzkoušejte si vytvoření knihovny. Vytvořte nejdříve knihovnu s názvem makra.h a vložte do něj následující kód:

/*------------------------------------------------*/
/* c11/makra.h                                    */
#define POZDRAV "Nazdarek"
#define COPYRIGHT "(c) 2003"
#define LICENCE
#define TISK(a) printf("%s\n",a);
#define SOUBOR __FILE__
#define BEGIN {
#define END }
/*------------------------------------------------*/

Poté vytvořte soubor s následujícím obsahem (ve stejném adresáři), do kterého soubor "makra.h" includnete.

  1. /*------------------------------------------------*/
  2. /* c11/makra3.c                                   */
  3. #include <stdio.h>
  4. #include "makra.h"
  5.  
  6. int main(void)
  7. BEGIN TISK(POZDRAV) TISK(SOUBOR)
  8. #ifdef LICENCE
  9. TISK(COPYRIGHT)
  10. #endif
  11. END
  12. /*------------------------------------------------*/

Výstup z programu:

Nazdarek
makra3.c
(c) 2003

Všimněte si, jak jsem definoval makro LICENCE bez toho, že bych mu přiřadil nějakou hodnotu. Pro podmínku #ifdef stačilo, že je definováno. Záměna špičatých závorek za BEGIN a END možná udělalo radost znalcům PASCALu, ale jinak to zdrojový kód značně znepřehledňuje, takže si takové konstrukce raději odpusťte. Leda že byste chtěli potrápit svého učitele programování :-).

Ostatní direktivy

#pragma

Direktiva #pragma uvozuje implementačně závislé direktivy. Pokud překladač narazí na tuto direktivu a nerozumí jí, pak ji ignoruje (nezpůsobí to žádnou chybu).

Ukázka (z C++ Builderu):

#pragma hdrstop

Direktivy #pragma často používají různá vývojová prostředí k uložení informací, které potřebují pro svůj (optimální) běh a se zdrojovým kódem nemají zase až tak moc společného.

#line

Direktiva #line nastavuje hodnotu makra __LINE__ (a __FILE__). Není to příliš užitečná direktiva.

#line N ["jméno"]

N je číslo, kterým se nastaví __LINE__ pro následující řádek a "jméno" (nepovinné) je jméno, kterým se nastaví makro __FILE__.

#warning a #error

Tyto direktivy vypíší při překladu programu varování. Direktiva #error navíc překlad ukončí. Používají se spolu s podmíněným překladem.
Jako argument mají text chybového hlášení, který se vypíše.

#ifndef __cplusplus
#error Tento program se musi prekladat pomoci prekladace g++ a ne gcc
#endif

1) Makra __linux__ a __unix__ byla dříve definována jako linux a unix.

Komentář Hlášení chyby
Created: 29.8.2003
Last updated: 18.6.2014
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..