| ← operátory a výrazy | C/C++ | podmínky a cykly → |
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>,
pak preprocesor vloží na místo tohoto řádku obsah standardní knihovny <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 před „konec řádku“ napište zpětné lomítko.
V následující tabulce jsou direktivy preprocesoru. Význam některých z nich si probereme.
| #define | #elif | #else | #endif | #error | #if |
| #ifdef | #ifndef | #include | #line | #pragma | #undef |
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++ | int (1 nebo 0) |
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áme 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 | string |
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.
#define NAZEV makro ... zdrojový kód kde makro používáme ... #undef NAZEV ... zde už makro není definované ...
V příkladu si ukážeme použití standardních i uživatelských maker. Takto vypadá zdrojový kód před zpracováním preprocesorem.
1: /*------------------------------------------------*/ 2: /* makra.c */ 4: #include <stdio.h> 6: #define N 5 7: #define VERSE "1.0.0" 9: int main(void) 10: { 11: int pole[N]; 12: unsigned int x; 14: printf("ANSI C:\t%s\t%i\n", __STDC__ ? "ANO" : "NE ", __STDC__); 15: printf("i386:\t%s\t%s\n", __i386__ ? "ANO" : "NE ", __VERSION__); 16: printf("Tento program byl prelozen %s v %s.\n" 17: "Prave jsme na radku %i.\n", __DATE__, __TIME__, __LINE__); 19: printf("Zadejte index pole <0,%u>: ", N - 1); 20: scanf("%u", &x); 22: x = x >= N ? N - 1 : x; 24: printf("Zadejte hodnotu pole[%u]: ", x); 25: scanf("%i", &pole[x]); 26: printf("verse programu %s\n", VERSE); 28: return 0; 29: } 31: /*------------------------------------------------*/
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>. To jsem z výpisu vypustil (je moc dlouhá).
1: /*------------------------------------------------*/ 3: int main(void) 4: { 5: int pole[5]; 6: unsigned int x; 8: printf("ANSI C:\t%s\t%i\n", 1 ? "ANO" : "NE ", 1); 9: printf("i386:\t%s\t%s\n", 1 ? "ANO" : "NE ", 10: "4.2.3 (Ubuntu 4.2.3-2ubuntu7)"); 11: printf("Tento program byl prelozen %s v %s.\n" 12: "Prave jsme na radku %i.\n", "Sep 18 2008", "10:35:06", 17); 14: printf("Zadejte index pole <0,%u>: ", 5 - 1); 15: scanf("%u", &x); 17: x = x >= 5 ? 5 - 1 : x; 19: printf("Zadejte hodnotu pole[%u]: ", x); 20: scanf("%i", &pole[x]); 21: printf("verse programu %s\n", "1.0.0"); 23: return 0; 24: } 26: /*------------------------------------------------*/
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 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:
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 ## 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:
printf("%s = %i\n", "5" " + " "6", (5)+(6));
printf("%i %i\n", 56, cislo5);
Direktiva #if vyhodnocuje podmínku, která za ní
následuje (tedy preprocesor jí 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
Poznámka: Podmínky lze negovat pomocí operátoru ! (vykřičník).
1: /*------------------------------------------------*/ 2: /* makra2.c */ 4: #include <stdio.h> 5: #define PT 6 7: int main(void) 8: { 9: #if PT > 5 10: printf("Makro PT je vetsi jak 5\n"); 11: #endif 13: #if defined unix && defined linux 14: printf("Hura, ja bezim na unixovem stroji a snad pod linuxem!\n"); 15: #elif defined unix 16: printf("Hura, ja bezim pod unixem!\n"); 17: #else 18: printf("Hmm, kde jsem se to kruci ocitl??\n"); 19: #endif 21: return 0; 22: } 24: /*------------------------------------------------*/
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:
1: /*------------------------------------------------*/ 2: /* makra.h */ 3: #define POZDRAV "Nazdarek" 4: #define COPYRIGHT "(c) 2003" 5: #define LICENCE 6: #define TISK(a) printf("%s\n",a); 7: #define SOUBOR __FILE__ 8: #define BEGIN { 9: #define END } 10: /*------------------------------------------------*/
Poté vytvořte soubor s následujícím obsahem (ve stejném adresáři), do kterého knihovnu "makra.h" začleníme.
1: /*------------------------------------------------*/ 2: /* makra3.c */ 3: #include <stdio.h> 4: #include "makra.h" 6: int main(void) 7: BEGIN TISK(POZDRAV) TISK(SOUBOR) 8: #ifdef LICENCE 9: TISK(COPYRIGHT) 10: #endif 11: return 0; 12: END 13: /*------------------------------------------------*/
Výstup z programu:
Nazdarek makra3.c (c) 2003
Všimněte si, jak jsme definovali makro LICENCE bez toho, že by jsme mu přiřadili nějakou hodnotu. Pro podmínku #ifdef nám 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. A to je k preprocesoru vše.
Direktiva #pragma uvozuje implementačně závislé direktivy. Pokud překladač narazí na tuto direktivu a nerozumí jí, pak jí ignoruje (nezpůsobí to žádnou chybu).
Ukázka (z C++ Builderu): #pragma hdrstop
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__.
| ← operátory a výrazy | C/C++ | podmínky a cykly → |
