Kapitola 8. Preprocesor

Obsah

8.1. Makra
8.2. Zabudovaná makra jazyka C
8.3. Podmíněný překlad
8.4. Makro #include
8.5. Makro #pragma
8.6. Opakování

Časová náročnost
Časová náročnost kapitoly: 40 minut

Preprocesor zpracovává hlavičkové soubory, rozvíjí makra, nepropouští komentáře a umožňuje provádět podmíněný překlad zdrojového textu. Preprocesor předchází překladač. Preprocesor zpracovává vstupní text jako text, provádí v něm textové změny a jeho výstupem je opět text. Od preprocesoru tedy nemůžeme čekat kontrolu syntaxe, natož pak typovou kontrolu.

Preprocesor přijímá následující direktiva:

Důležité

Tabulka 8.1.

#define#elif#else#endif
#error#if#ifdef#ifndef
#include#line#pragma#undef

Direktiva preprocesoru není příkaz jazyka C. Neukončujeme ji proto středníkem. Direktiva preprocesoru musí být vždy uvozena znakem #. # navíc musí být na řádku prvním jiným znakem než jsou odsazovače. Od direktivy samotné jej opět mohou oddělovat odsazovače.

Potřebné znalosti
Potřebné znalosti

K zvládnutí obsahu této kapitoly je nutné mít znalosti z kapitoly 2 až 7 - pochopit doposud probíranou teorii. Umět psát programy umožňující vstup a výstup řádku a to jak formátovaných tak standardních. A umět také provádět vstupně výstupní operace v paměti.

8.1. Makra

Časová náročnost
Časová náročnost: 3 minuty

Makra se v programech využívají velice často a to proto, že zbavují programy tzv. "Magických čísel", tj. konstant, které se bez vysvětlení objevují v programu. Většinou jsou konstanty použity na začátku programu a jejich rozumné použití zvyšuje čitelnost zdrojového kódu.

Makra využíváme hlavně pro:

Důležité
  • definici konstant pro podmíněný překlad
  • definici složitějších výrazů nebo příkazů, které se mají rozvinout v místě použití
  • pro určení mezí polí, které musí být zapsány konstantním výrazem

Pokud se text makra nevejde na jeden řádek, můžeme jej rozdělit na více následujících řádků. Skutečnost, že makro pokračuje na následujícím řádku, se určí umístěním znaku \ jako posledního znaku na řádku.

8.2. Zabudovaná makra jazyka C

Časová náročnost
Časová náročnost: 6 minut

Pokud Váš překladač splňuje normu ISO C, bude mít nejméně pět předdefinovaných maker, která lze v programech použít. Jsou to:

Důležité

Tabulka 8.2.

__LINE__definuje celočíselnou hodnotu, která je rovna číslu právě překládaného řádku zdrojového souboru
__FILE__definuje řetězec, který je jménem právě překládaného souboru
__DATE__definuje řetězec, který obsahuje systémové datum. Řetězec má obecný tvar měsíc/den/rok
__TIME__definuje řetězec, který obsahuje čas zahájení překladu programu. Řetězec má obecný tvar hodiny:minuty:sekundy
__STDC__je definováno jako hodnota 1, pokud překladač splňuje normu ISO C

Ukážeme si program, který předvádí výše popsaná makra.

src/c_macros.c
Příklad 8.1.
/******************************
*	c_macros.c
*	19.03.2002
******************************/

#include <stdio.h>

int main(void)
{
 printf("Preklad: %s, radek: %d, dne: %s, v: %s", __FILE__, __LINE__,
 __DATE__, __TIME__);
 
 return 0;
}

Je důležité vědět, že hodnoty maker jsou pevně určeny v době překladu. Proto bude program vypisovat neustále stejné hodnoty bez ohledu na to, kdy bude spuštěn. Hlavní využití těchto maker je pro vytváření časových a datumových razítek, která ukazují, kdy byl daný program přeložen.

8.3. Podmíněný překlad

Časová náročnost
Časová náročnost: 10 minut

Preprocesor může během své činnosti vyhodnocovat, je-li nějaké makro definováno či nikoliv. Při použití klíčového slova preprocesoru defined pak může spojovat taková vyhodnocení do rozsáhlejších logických výrazů. Argument defined nemusí být uzavřen do závorek. Může se však vyskytnout jen za #if nebo #elif.

Jednoduchý příklad
Příklad:

#if defined LIMIT &amp;&amp; defined OSTRA &amp;&amp; LIMIT==10

V závislosti na splnění či nesplnění podmínky můžeme určit, bude-li ohraničený úsek programu dále zpracován, nebo bude-li přeskočen a tak nebude přeložen. Této možnosti použití preprocesoru říkáme podmíněný překlad.

Direktivy které můžeme použít u podmíněného překladu:

  • #if
  • #else
  • #elif
  • #endif
  • #ifdef
  • #ifndef

Důležité

Syntaktické definice popisující podmíněný překlad jsou:

#if text
[[#elif text] [#else text]]
#endif

Části zapsané v hranatých závorkách [] jsou nepovinné. Část označená jako text musí být vyhodnotitelná při činnosti preprocesoru.

Vždy musí být jasno, kde podmíněná část zdrojového textu začíná a kde končí. Proto nesmíme zapomínat na #endif či #elif. Podmíněné části musí být ukončeny a omezeny v rámci jednoho zdrojového textu. Jinak oznámí preprocesor chybu. Dále je zde oproti C zavedena i podmínka #elif.

Napíšeme si program na kterém si demonstrujeme pravidla podmíněného překladu. Naším úkolem bude zobrazit samotnou ASCII sadu, nebo sadu rozšířenou na základě hodnoty obsažené v proměnné CHAR_SET.

src/cond_trn.c
Příklad 8.2.
/******************************
*	cond_trn.c
*	19.03.2002
******************************/

#include <stdio.h>

/* Definujem CHAR_SET jako 256 nebo 128 */
#define CHAR_SET 256

int main(void)
{
 int i;
#if CHAR_SET == 256
 printf("Zobrazeni uplne sady ASCII znaku plus rozsireni.\n");
#else
 printf("Zobrazeni jen samotne sady ASCII znaku.\n");
#endif

 for(i=0;i<CHAR_SET;i++)
    printf("%c ",i);
	
 return 0;
} 

8.4. Makro #include

Časová náročnost
Časová náročnost: 4 minuty

Důležité

Makro #include je direktivou naprosto nepostradatelnou. Používáme ji pro včlenění zdrojového textu jiného souboru. Tento soubor může být určen více způsoby.

  1. #include <hlavičkový_soubor> - hlavičkový_soubor je hledán ve standardním adresáři pro include. Takto se zpravidla začleňují standardní hlavičkové soubory. Není-li soubor nalezen, je ohlášena chyba.
  2. #include "hlavičkový_soubor" - hlavičkový_soubor je hledán v aktivním (pracovním) adresáři. Není-li tam nalezen, postupuje se podle výše uvedené možnosti. Takto se zpravidla začleňují naše (uživatelské) hlavičkové soubory.
  3. #include jméno_makra - jméno_makra je nahrazeno expandovaným makrem. Další činnost pokračuje podle některé z výše uvedených možností.

8.5. Makro #pragma

Časová náročnost
Časová náročnost: 1 minuta

Makro #pragma je speciální direktivou, která má uvozovat všechny implementačně závislé direktivy. Pokud jiný překladač speciální direktivu nezná, prostě ji bez chybového stavu ignoruje.

8.6. Opakování

Shrnutí
Shrnutí

Časová náročnost
Časová náročnost: 3 minuty

Preprocesor jazyka C

  • Zpracovává zdrojový text programu před použitím překladače
  • Nekontroluje syntaktickou správnost programu
  • Provádí pouze zpracování maker - nahrazování konstant za odpovídající číselné hodnoty
  • Vypouští ze zdrojového textu všechny komentáře
  • Provádí podmíněný překlad

A dále:

  • Všechny argumenty v definici makra by měly být uzavřeny do závorek
  • Vyhýbejte se možnosti vzniku vedlejších efektů při vyhodnocení argumentů makra
  • Každou použitou konstantu definujte jako symbolickou a to hned ze začátku programu - zvyšuje to jeho čitelnost
  • Používejte podmíněnou kompilaci pro vynechání ladících částí programu
  • Hlásí-li překladač nějakou chybu na kterou nemůžete přijít, je vhodné opět si prohlédnout soubor a zkontrolovat způsob, jakým preprocesor rozvíjí makra

Cvičení
Cvičení

Úkol k textu

Zadání 1)

Co je __FILE__ a co představuje?
Řešení

Úkol k textu

Zadání 2)

Pomocí #ifdef ukažte, jak podmíněně přeložit tento úsek programu v závislosti na tom, zda je nebo není definováno DEBUG.
if(!(j%2))
{
 printf("j = %d\n",j);
 j = 0;
}
Řešení

Úkol k textu

Zadání 3)

Je tento úsek správný? Pokud ne, ukažte jak jej opravit.
#define NUMBER

#ifdef !NUMBER
.
.
.
#endif
Řešení

Úkol k textu

Zadání 4)

Jaký je rozdíl mezi použitím uvozovek a úhlových závorek u direktivy #include?
Řešení
test8.swf
Test
Kliknutím na ikonu spustíte test.