LINUXSOFT.cz Přeskoč levou lištu

ARCHIV



   

> 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 40126×

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

#define MAKRO

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í

#include "hlavicka.h"

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

pridej.cz

 

DISKUZE

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 ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2024) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze