The English encyclopedia Allmultimedia.org will be launched in two phases.
The final launch of the Allmultimedia.org will take place on February 24, 2026
(shortly after the 2026 Winter Olympics).

Preprocesor jazyka C

Z Multimediaexpo.cz

Preprocesor jazyka C (anglicky: The C preprocessor, zkratka cpp) je v informatice preprocesor používaný zejména při překladu zdrojových kódů programů napsaných v jazyce C. V mnoha implementacích se jedná o samostatný program spouštěný překladačem v rámci první fáze překladu. Preprocesor interpretuje jednoduché direktivy pro vložení zdrojového kódu z jiného souboru (#include), definice maker (#define) a podmíněné vložení kódu (#if). Jazyk direktiv preprocesoru není vázán na syntaxi jazyka C, takže preprocesor C lze využít i na zpracování jiných typů souborů.

Obsah

Fáze překladu zdrojového kódu v C

  1. Nahrazení trigramů (v podstatě jde o escape sekvence, které umožňují zadávání potřebných 7-bit ASCII znaků i z jiných, většinou starších kódových tabulek).
  2. Spojení řádků – fyzické řádky zdrojového textu, které jsou zakončeny escape-sekvencí nového řádku (v podstatě jde o obrácené lomítko \ použité jako poslední znak na řádku), jsou spojeny do jediného logického řádku.
  3. Tokenizace – preprocesor zalomí výsledek do posloupnosti tzv. tokenů preprocesoru a bílých znaků (mezery, tabelátory a konce řádků). Komentáře jsou nahrazeny bílým znakem.
  4. Přepsání maker a provedení direktiv - v této fázi jsou expandována makra a provedeny direktivy, jako např. vložení zdrojového kódu z jiného souboru nebo vynechání části zdrojového kódu v případě podmíněného překladu.

Direktivy preprocesoru

Vkládání souborů

Nejčastěji využívanou direktivou preprocesoru je vložení jiného souboru:

#include <stdio.h>
 
int main (void)
{
    printf("Hello, world!\n");
    return 0;
}

V tomto případě direktiva #include vložila na začátek zdrojového souboru obsah souboru stdio.h, který musí být v systému uložen ve specifickém adresáři (v unixových systémech v adresáři /usr/include).

Podmíněný překlad

Direktivy #if, #ifdef, #ifndef, #else, #elif a #endif umožňují podmíněný překlad vybraných částí zdrojového kódu.

#define __WINDOWS__
 
#ifdef __WINDOWS__
#include <windows.h>
#else
#include <unistd.h>
#endif

Symbol není nutné definovat ve zdrojovém kódu, jako je tomu v předcházejícím příkladě s #define __WINDOWS__, ale lze jej definovat až při překladu jako jeden z atributů preprocesoru, popřípadě kompilátoru. Viz následující příklad:

#include <stdio.h>
 
int main(void){
#ifdef CZ   
    printf("Ahoj\n");
#else
    printf("Hello\n");
#endif
 
    return 0;
}

Při překladu pak použijeme:

$gcc -Wall ahoj.c -o ahoj
$./ahoj
Hello
 
$gcc -Wall ahoj.c -DCZ -o ahoj
$./ahoj
Ahoj

Poprvé je soubor s kompilován bez definovaného symbolu CZ a v kódu bude tedy použit řádek printf("Hello\n");. V druhém případě je jedním z parametrů při volání kompilátoru -DCZ, takže symbol CZ bude nadefinován ještě před spuštěním preprocesoru a v kódu se proto použije řádek printf("Ahoj\n");. Následující příkaz ./ahoj spouští překompilovaný soubor.

Podmíněný překlad se často používá k tomu, aby zabránil vícenásobnému vložení téhož kódu. Následující příklad zajistí, že kód bude vložen pouze jednou, bez ohledu na to, kolikrát bude direktivou #include vložen soubor, který jej obsahuje. Konstrukce je užitečná při tvorbě složitějších programů, složených z mnoha souborů:

#ifndef SOUBOR_A
#define SOUBOR_A
...
kód
...
#endif

Definice a expanze maker

Používají se dva základní typy definice maker. Jednak lze definovat makra bez parametru, která expandují vždy do stejné sekvence znaků (resp. tokenů), jednak lze definovat makra s parametry, které se z praktického hlediska chovají podobně jako funkce. Obecná syntaxe direktivy #define je:

#define <identifikátor> <seznam nahrazujících tokenů>
#define <identifikátor>(<seznam parametrů>) <seznam nahrazujících tokenů>

Speciálním případem je definice samotného identifikátoru, kterému není přiřazen žádný další seznam tokenů – příklad použití takovéto definice byl uveden v sekci Podmíněný překlad.

Příkladem makra, které nepřijímá žádné parametry, může být následující použití preprocesoru k definici číselné konstanty:

#define PI 3.14159

Příkladem makra, které přijímá parametr (argument):

#define RADTODEG(x) ((x) * 57.29578)

Při použití takto definovaného makra se nesmí mezi názvem makra a závorkou, ve které je uveden jeden nebo více parametrů (argumentů), vyskytovat žádné bílé znaky (tj. nesmí tam být uvedena mezera).

Specifickým rysem maker C preprocesoru z hlediska programování v jazyce C je to, že jde pouze o zestručněný zápis zdrojového kódu (podobně jako v případě některých skriptovacích jazyků) – nikoliv o skutečnou definici funkce. Proto parametry maker nepodléhají typové kontrole jazyka C. Tento zápis se nechová jako funkce a někdy to může vést k obtížně odhalitelným chybám. Například v tomto případě, kde je makro NASOBEK definováno takto:

#define NASOBEK(a,b) a*b

a je použité ve výrazu:

c=NASOBEK(2+2,5+5)

bude expandováno jako:

c=2+2*5+5

Výsledek není mylně očekávaných 40 ale 17. Takové chybě se dá v tomto konkrétním případě zabránit uzavřením matematického výrazu do závorek (podobně jako u ukázkového makra RADTODEG(x) v předchozím příkladu). Obecným řešením je nepoužívání nadměrně komplikovaných maker. V tomto konkrétním případě stačí dané makro přepsat jako:

#define NASOBEK(a,b) ((a)*(b))

Obtížněji řešitelným problémem je tzv. vícenásobná evaluace (vyhodnocení) parametrů makra – pokud je parametrem makra např. volání funkce, tak na rozdíl od obyčejného volání funkce, kdy by byla funkce zavolána jen jednou a pak se pracovalo s její návratovou hodnotu, je v případě makra funkce zavolána pokaždé, když se definice makra odkazuje na daný parametr. Kromě volání funkcí je toto chování nepříjemné např. i při použití specifických C operátorů jako ++, −− apod.

Pokud je to potřeba, lze definici makra zrušit (odstraňuje makra s parametry i makra bez parametru):

#undef <identifikátor>

Ustálené zvyklosti

Existuje nezávazná konvence využití dostupného prostoru jmen, podle které jsou v názvech maker preprocesoru jazyka C vždy používána pouze velká písmena A-Z (spolu s číslicemi a podtržítky), zatímco názvy funkcí a proměnných v C jsou obvykle tvořená pouze malými písmeny a-z (spolu s číslicemi a podtržítky, někdy je použita kombinace velkých a malých písmen). Nicméně jde pouze o konvenci usnadňující čtení zdrojového kódu: lze použít i názvy maker tvořené malými písmeny nebo kombinací velkých a malých písmen (stejně tak lze pojmenovat funkce a proměnné v C naopak velkými písmeny).

Související články