C/C++ (12) - Preprocesor
Před vlastním překladem se dostane ke slovu preprocesor. S jeho pomocí můžeme například definovat konstantu, vložit soubor nebo překládat jen část zdrojového souboru.
18.1.2005 15:00 |
Jan Němec
| Články autora
| přečteno 40382×
Tři fáze překladu
Celý proces překladu od zdrojového kódu v C až po spustitelný soubor má 3 fáze.
Nejprve se dostane ke slovu preprocesor, následuje vlastní překlad a závěrečnou
fází je linkování. Kód programu tvoří jeden nebo více *.c souborů, preprocesor
zpracovává každý zvlášť. Provádí úpravy na textové úrovni, například vypouští
komentáře, vkládá soubory, nahrazuje jeden symbol za jiný atd. Překlad také
probíhá na jednotlivých souborech odděleně. Výsledkem je již téměř
hotový spustitelný binární kód, obsahuje ovšem odkazy na funkce a proměnné
z ostatních přeložených souborů a také knihoven. Závěrečnou fází je linkování,
kdy se z jednoho nebo několika takto "téměř přeložených" souborů vytvoří
jeden spustitelný program.
Preprocesor
Celá řada jazyků (například Pascal, Java, ...) preprocesor vůbec nemá. Já
osobně to považuji za chybu v návrhu těchto jazyků, která mohla vzniknout nejen
autorovou neznalostí nebo ideologickou zaslepeností, ale někdy i prostým
zděšením, k čemu všemu se v Céčku preprocesor používá. Pokud pracujete
s překladačem gcc (na Linuxu obvykle ano), zkuste některý z příkladů
předchozího dílu prohnat pouze preprocesorem bez dalších fází překladu.
gcc -E priklad.c
Díky příkazu preprocesoru #include <stdio.h>, který vkládá do našeho kódu
standardní soubor stdio.h a tím zpřístupňuje některé funkce standardní
knihovny, se na standardní výstup vyhrne přes 30 stránek kódu a až úplně
nakonec následuje několik řádek našeho příkladu. Ještě mnohem horší je to u
C++ a jeho standardních i nestandardních (napadá mě třeba Qt) knihoven.
Díky množství #include kódu, který jen zpřístupňuje volání knihovních funkcí,
trvá překlad i středně velkého projektu neúnosně dlouho, ačkoliv kód napsaný
programátorem projektu není příliš rozsáhlý.
Dalším vážným problémem Céčka, který preprocesor jen zhoršuje, je ochrana
identifikátorů. Snad každému zkušenějšímu C programátorovi se stalo, že si
definoval makro preprocesoru, jehož název kolidoval s identifikátorem
z nějaké knihovny.
Na druhou stranu preprocesor řadu věcí velmi usnadňuje. Vyzdvihl bych zejména
podmíněný překlad. Díky němu lze poměrně jednoduše psát multiplatformní
programy nebo psát ladící kód, který nebude v distribuční verzi přeloženého
programu.
Vložení souboru
Soubor vložíme pomocí příkazu #include, který má dvě varianty.
#include <soubor.h>
#include "soubor.h"
V prvním případě se vezme soubor z adresáře se standardními hlavičkovými
soubory (na Linuxu obvykle /usr/include), ve druhém pak z adresáře se zdrojovým
kódem programu. V obou případech mohou jména souborů obsahovat relativní
cestu, adresáře se oddělují obyčejným lomítkem a to i v DOSu a odvozených
systémech, kde se jinak používá lomítko zpětné.
#include <sys/socket.h>
#include "../moje_knihovna/knihovna.h"
Vkládané soubory mají obvykle příponu .h, ale jde pouze o programátorskou
konvenci, nikoli o vlastnost jazyka C. Obsah vkládaného souboru také není nijak
omezen. Lze tedy například rozčlenit velký projekt do více zdrojových souborů
a v jediném *.c souboru, který se bude překládat, mít jen několik #include
příkazů. Takto se ovšem v C v praxi neprogramuje, vkládání souborů se obvykle
používá pouze na zpřístupnění maker, typů, funkcí a proměnných. Podrobnosti
si povíme v některém z dalších dílů, zatím jen stačí vědět, že známý příkaz
preprocesoru #include <stdio.h>, kterým si zpřístupňujeme funkce
puts a printf, je ve skutečnosti vložení souboru /usr/include/stdio.h.
Generování chyby
Překlad můžeme explicitně přerušit pomocí příkazu #error. Význam má zejména
v souvislosti s podmíněným překladem, ale pro přepracované programátory
má jistě význam i konstrukce
#error tady jsem skončil před dovolenou
Makro bez hodnoty
Makro bez parametrů definujeme příkazem #define
Definice platí do konce souboru, přesněji řečeno do konce souboru ve smyslu
překladu nikoli preprocesoru. Pokud například makro definujeme v hlavičkovém
souboru hlavicka.h a ten pak pomocí
vložíme do zdroj.c, bude definice platit i pro kód ze souboru zdroj.c a to od
příkazu #include "hlavicka.h" dále, platná bude rovněž pro veškerý kód
z případných následujících #include příkazů.
Na makro se můžeme dotázat pomocí příkazů #ifdef, #ifndef, #else a #endif,
říká se tomu podmíněný překlad.
#define LADENI
/*
Tady je nějaký kód
*/
#ifdef LADENI
printf("Proměnná i má hodnotu %i\n", i);
#endif
Pomocí jediné definice makra LADENI, kterou umístíme nejlépe do nějakého
hlavičkového souboru, tak lze snadno zapnout ladící výpisy. V distribuční
verzi programu pak definici makra zrušíme (tj. provedeme jedinou změnu na
jednom místě) a program přestane ladící hlášky vypisovat.
Překladače navíc umožňují definovat makra externě mimo zdrojáky. V grafických
vývojových prostředích lze obvykle vše naklikat, "správný" linuxový programátor
použije parametr -D překladače gcc.
gcc program.c -DLADENI -o program
Pro přeložení distribuční verze zavolá standardně.
gcc program.c -o program
Tento způsob ovlivňování překladu je lepší než dotaz na ručně definované
makro v *.h souboru, ušetříme si jím i tu jedinou změnu v jednom souboru.
Překladače definují (nebo nedefinují) celou řadu maker v závislosti na
prostředí překladu. Z těch nejužitečnějších bych jmenoval
unix, _WIN32 nebo __cplusplus,
podrobnosti získáte v dokumentaci ke konkrétnímu překladači.
Následující kód demonstruje užitečnost podmíněného překladu při
multiplatformním programování. Na Windows pomocí API funkce z windows.h vyhodí
dialog a všude jinde vypíše řetězec na standardní výstup.
#ifdef _WIN32
MessageBox(NULL, "Ahoj světe!", "Zpráva", MB_OK);
#else
puts("Ahoj světe");
#endif
Bez použití podmíněného překladu by na Linuxu překlad skončil s chybou
kvůli neznámé funkci MessageBox a konstantě MB_OK.
Zda je program překládán jako C nebo jako C++ se zase můžeme zeptat pomocí
makra __cplusplus. Díky částečné jednosměrné kompatibilitě lze obvykle
C program přeložit i pomocí C++ překladače, pokud v něm nepoužíváme
(např. jako identifikátory proměnných) klíčová slova C++ ani některé
problematické konstrukce, které jsou v C++ zakázané.
#ifndef __cplusplus
puts("Přeloženo jako čisté C");
#else
puts("Přeloženo jako C++");
#endif
Makro s hodnotou
Makro může mít nějakou hodnotu, běžně se jako makra definují konstanty
používané na více místech nebo pokud chceme mít jakési nastavení programu
na jednom místě například v hlavičkovém souboru. Z hlediska vlastního překladu
je makro konstanta a nikoli proměnná, takže lze makra použít i jako meze polí.
#define N 10
int a[N];
int main(void) {
int i;
for(i = 0; i < N; i++)
a[i] = i;
return 0;
}
Hodnotou makra může být prakticky cokoli, takže lze napsat i následující
hrůzu:
#include <stdio.h>
#define KONEC return 0;
#define CYKLUS_PRES_I for(i = 0; i < N; i++)
#define RETEZEC "Ahoj světe"
#define NOVY_RADEK puts("");
#define N 10
int main(void) {
int i;
CYKLUS_PRES_I {
printf(RETEZEC);
NOVY_RADEK
}
KONEC
}
Hodnotu makra můžeme pomocí zpětného lomítka napsat i na více řádků.
#define ERR_STRING "Chyba 134\n" \
"syntaxe: prhlizec soubor.cfg url\n"\
"příklad: prohlizec /etc/prohlizec.cfg http://linuxsoft.cz"
Řídit překlad můžeme také pomocí #if a konstantního výrazu. Běžné komentáře v C
mají tu nevýhodu, že je nelze zanořovat. Pokud tedy chceme dočasně zakomentovat
kus kódu, který již obsahuje běžné komentáře, nelze použít
/*
int moje_funkce() {
/* Vnořený komentář - chyba !!! */
return 0;
}
*/
Nic nám však nebrání použít #if.
#if 0
/* Tak trochu jiný komentář... */
int moje_funkce() {
/* Vnořený komentář - OK */
return 0;
}
#endif
V konstantním výrazu v #if lze navíc použít i makra a také běžné operátory.
Ve spolupráci s direktivou #elif a operátorem defined lze již napsat opravdu
zajímavé věci. Ukážeme si to na příkladu.
Příklad pro dnešní díl
Při psaní větších projektů je třeba předem myslet na ladění. Náš příklad je
velmi jednoduchý, a ladící kód proto působí dost nepřirozeně. Ve skutečných
a složitých případech však může podobně opatrný přístup ušetřit spoustu
práce při hledání chyb.
#include <stdio.h>
#define N 10
/*
Tyto definice raději zadáme zvnějšku
#define LADIM
#define MALA 0
#define VELKA 1
#define UROVEN VELKA
*/
int a[N];
int main(void) {
int i;
for (i = 0; i <= N; i++) {
#ifdef LADIM
if (i >= sizeof(a)/sizeof(int)) {
printf("Pokus o přístup mimo pole na index %i\n", i);
return 1;
}
#if UROVEN==VELKA
printf("Do pole a na index %i píšu %i\n", i, i);
#elif UROVEN==MALA
putchar('.'); /* Aby bylo vidět, že se program nekousl */
#endif
#endif /* od #ifdef LADIM */
a[i] = 1;
}
#if defined LADIM && UROVEN==VELKA
puts("Konec programu");
#endif
return 0;
}
Zkuste si program přeložit třeba takhle:
gcc program.c -o program -DLADIM -DMALA=0 -DVELKA=1 -DUROVEN=VELKA
Pokud používáte jiný překladač a neumíte zadat makro zvnějšku, odkomentujte
definici maker ve zdrojovém kódu.
Pokračování příště
I v dalším dílu se zaměříme na preprocesor. Ukážeme si, jak se píší makra
s parametry.
Verze pro tisk
|
Nejsou žádné diskuzní příspěvky u dané položky.
Příspívat do diskuze mohou pouze registrovaní uživatelé.
|
|

Vyhledávání software

Vyhledávání článků
28.11.2018 23:56 /František Kučera Prosincový sraz spolku OpenAlt se koná ve středu 5.12.2018 od 16:00 na adrese Zikova 1903/4, Praha 6. Tentokrát navštívíme organizaci CESNET. Na programu jsou dvě přednášky: Distribuované úložiště Ceph (Michal Strnad) a Plně šifrovaný disk na moderním systému (Ondřej Caletka). Následně se přesuneme do některé z nedalekých restaurací, kde budeme pokračovat v diskusi.
Komentářů: 1
12.11.2018 21:28 /Redakce Linuxsoft.cz 22. listopadu 2018 se koná v Praze na Karlově náměstí již pátý ročník konference s tématem Datová centra pro business, která nabídne odpovědi na aktuální a často řešené otázky: Jaké jsou aktuální trendy v oblasti datových center a jak je optimálně využít pro vlastní prospěch? Jak si zajistit odpovídající služby datových center? Podle jakých kritérií vybírat dodavatele služeb? Jak volit vhodné součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně datové centrum spravovat? Jak co nejlépe eliminovat možná rizika? apod. Příznivci LinuxSoftu mohou při registraci uplatnit kód LIN350, který jim přinese zvýhodněné vstupné s 50% slevou.
Přidat komentář
6.11.2018 2:04 /František Kučera Říjnový pražský sraz spolku OpenAlt se koná v listopadu – již tento čtvrtek – 8. 11. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma umění a technologie, IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
4.10.2018 21:30 /Ondřej Čečák LinuxDays 2018 již tento víkend, registrace je otevřená.
Přidat komentář
18.9.2018 23:30 /František Kučera Zářijový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 20. 9. 2018 od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5). Tentokrát bez oficiální přednášky, ale zato s dobrým jídlem a pivem – volná diskuse na téma IoT, CNC, svobodný software, hardware a další hračky.
Přidat komentář
9.9.2018 14:15 /Redakce Linuxsoft.cz 20.9.2018 proběhne v pražském Kongresovém centru Vavruška konference Mobilní řešení pro business.
Návštěvníci si vyslechnou mimo jiné přednášky na témata: Nejdůležitější aktuální trendy v oblasti mobilních technologií, správa a zabezpečení mobilních zařízení ve firmách, jak mobilně přistupovat k informačnímu systému firmy, kdy se vyplatí používat odolná mobilní zařízení nebo jak zabezpečit mobilní komunikaci.
Přidat komentář
12.8.2018 16:58 /František Kučera Srpnový pražský sraz spolku OpenAlt se koná ve čtvrtek – 16. 8. 2018 od 19:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát jsou tématem srazu databáze prezentaci svého projektu si pro nás připravil Standa Dzik. Dále bude prostor, abychom probrali nápady na využití IoT a sítě The Things Network, případně další témata.
Přidat komentář
16.7.2018 1:05 /František Kučera Červencový pražský sraz spolku OpenAlt se koná již tento čtvrtek – 19. 7. 2018 od 18:00 v Kavárně Ideál (Sázavská 30, Praha), kde máme rezervovaný salonek. Tentokrát bude přednáška na téma: automatizační nástroj Ansible, kterou si připravil Martin Vicián.
Přidat komentář
Více ...
Přidat zprávičku
 Poslední diskuze
31.7.2023 14:13 /
Linda Graham iPhone Services
30.11.2022 9:32 /
Kyle McDermott Hosting download unavailable
13.12.2018 10:57 /
Jan Mareš Re: zavináč
2.12.2018 23:56 /
František Kučera Sraz
5.10.2018 17:12 /
Jakub Kuljovsky Re: Jaký kurz a software by jste doporučili pro začínajcího kodéra?
Více ...
|