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.
#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.
Makro | Význam | datový 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.
Makro | Význam | datový 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 |
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.
/* ... 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.
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.)
- /*------------------------------------------------*/
- /* c11/makra.c */
- #include <stdio.h>
- #include <stddef.h>
- #define N 5
- #define VERSE "1.0.0"
- #ifndef __STDC__
- #define __STDC__ 0
- #endif
- /* Definice pro Visual Studio */
- #ifdef _MSC_VER
- #define ZU "Iu"
- #define SZU "lu"
- #define __VERSION__ "Visual Studio"
- #ifdef _M_IX86
- #define __i386__ 1
- #else
- #define __i386__ 0
- #endif
- #else
- #define ZU "zu"
- #define SZU "zu"
- #endif
- #ifndef __i386__
- #define __i386__ 0
- #endif
- {
- size_t x;
- "Prave jsme na radku %i.\n", __DATE__, __TIME__, __LINE__);
- x = x >= N ? N - 1 : x;
- }
- /*------------------------------------------------*/
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 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.
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 BAR (x) - 1 / (x)
Jejich použití …
b = BAR;
... se vyhodnocení preprocesorem takto:
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 min(X, Y) ((X) < (Y) ? (X) : (Y))
Jejich použití:
b = mocnina(++n);
c = min (x+y,foo(z));
Vyhodnocení preprocesorem:
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 SPOJENI(X,Y) printf("%i %i\n",X ## Y, cislo ## X);
Použití maker:
Vyhodnocení preprocesorem:
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ě:
//... 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.
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):
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.
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.
#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
.