Operátory preprocesora

# dvojitý krížik – direktíva pre preprocesor

Operátory preprocesora alebo znak # je signálom pre preprocesor. Preprocesor sa spustí pri každom spustení kompilátora. Prejde si zdrojový kód a nájde riadky, ktoré začínajú symbolom dvojitého krížika (#). S týmito riadkami pracuje ešte pred spustením samotného kompilátora. Preprocesor spracováva vstupný text ako text, prevádza v ňom textové zmeny a jeho výstupom je opäť text. Od preprocesora teda nemôžeme čakať kontrolu syntaxu a ani typovú kontrolu. Preprocesor spracováva hlavičkové súbory, rozvíja makrá, neprepúšťa komentáre a umožňuje prevádzať podmienený preklad zdrojového textu.

Zdôraznime ešte jednu dôležitú skutočnosť. Direktíva preprocesoru nie je príkaz pre programovací jazyk a preto ju neukončujeme bodkočiarkou. A naviac musí byť v riadku prvým znakom a za ním by nemala byť medzera.

#include

Príkaz #include je inštrukcia pre preprocesor, ktorá mu hovorí: „To, čo nasleduje, je názov súboru. Nájdi tento súbor a vlož jeho obsah priamo na toto miesto programu.“ Týmto spôsobom sa definujú externé zdrojové kódy – knižnice.

#include <subor.h>   // vloženie textu zo špecifikovaného súboru zo systémového adresára
#include "subor.h"   // vloženie textu zo špecifikovaného súboru v adresári užívateľa

#define #undef

Makrá bez parametrov, príkaz #define, sú známejšie pod názvom symbolické konštanty. Kľúčom nech je skutočnosť, že makro, na rozdiel od symbolickej konštanty, má argumenty. Ich definovanie a oddefinovanie môžeme syntakticky popísať takto:

#define MENO_MAKRA [sekvencia]
#undef MENO_MAKRA

kde

  MENO_MAKRA predstavuje meno (identifikátor) makra

  sekvencia je nepovinný súvislý reťazec

Pri svojej činnosti prehľadáva preprocesor vstupný text a pri výskyte reťazca MENO_MAKRA prevádza jeho nahradenie reťazcom sekvencia. Tejto činnosti sa hovorí rozvoj (expanzia) makra. Z tohoto popisu je jasné, prečo sa preprocesoru niekedy zjednodušene hovorí makroprocesor.

#define DHT22_PIN 7        // reťazec DHT22_PIN zameň za reťazec 7
#define TFT_DC 9           // reťazec TFT_DC zameň za reťazec 9
#define TFT_CS 10          // reťazec TFT_CS zameň za reťazec 10

DHT22 myDHT22(DHT22_PIN);  // preprocesor z toho vytvorí pre kompilátor toto: DHT22 myDHT22(7);
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); // Adafruit_ILI9341 tft = Adafruit_ILI9341(10, 9);

V zdrojovom texte sa neodvolávame na nič nehovoriace čísla, ale na vhodne symbolicky pomenované makrá. Program to nielen sprehľadní, ale prípadnú zmenu hodnoty makra prevedieme na jednom mieste, preto sa uvádzajú na začiatku programu. Treba ale myslieť na túto vec. Ak urobíte niečo takéto:

#define NAMERANA_HODNOTA "Posledna namerana hodnota je:"

a použijete vo svojom zdrojovom kóde na 25 miestach NAMERANA_HODNOTA, dostanete 25 kópií reťazca “Posledna namerana hodnota je:” v pamäti SRAM!!! Miesto toho v tomto prípade je vhodné použiť Const char pole, čo má za následok iba jednu kópiu reťazca v pamäti SRAM. Alebo treba použiť PROGMEM.

A taktiež ešte si pripomeňme najčastejšie chyby pri zápise:

#define LED_PIN = 3   // toto je špatne, nesmie tam byt znamienko priradenia =
#define LED_PIN 3;    // aj toto je špatne, nesmie tam byť bodkočiarka ;

Názvy symbolických konštánt treba písať vždy VEĽKÝMI PÍSMENAMI. Názov je od hodnoty oddelený aspoň jednou medzerou, resp. sa zarovnáva na stĺpec (tabelátor) keď ich je viac pod sebou. Za hodnotou by mal byť komentár napr.:

#define ILI9341_BLACK       0x0000      //   0,   0,   0  čierna
#define ILI9341_YELLOW      0xFFE0      // 255, 255,   0  žltá 
#define ILI9341_DARKGREEN   0x03E0      //   0, 128,   0  tmavozelená
#define ILI9341_WHITE       0xFFFF      // 255, 255, 255  biela 
#define ILI9341_TFTWIDTH    240         // šírka TFT
#define ILI9341_TFTHEIGHT   320         // výška TFT

Preprocesor môže behom svojej činnosti vyhodnocovať, či je nejaké makro definované alebo nie. Pri použití kľúčového slova preprocesoru defined potom môžeme spájať také vyhodnotenia do rozsiahlejších logických výrazov. Argument nemusí byť uzavretý do zátvoriek. Môže sa však vyskytnúť len za #if alebo #elif. V závislosti na splnení či nesplnení podmienky môžeme určiť, či bude ohraničený úsek programu ďalej spracovaný alebo či bude odfiltrovaný a tak teda nebude preložený. Tejto možnosti použitia preprocesoru hovoríme podmienený preklad textu v závislosti na tom, či je makro definované.

#ifdef #ifndef #endif #elif

Test existencie definície symbolickej konštanty pomocou direktív #ifdef alebo #ifndef umožňuje zisťovať existenciu iba jedného symbolu (mena konštanty). Aby bolo možné vytvárať pri podmienenej kompilácii logické výrazy z viac symbolov, je nutné použiť operátor defined. Spôsob použitia operátoru defined ukazujú nasledujúce tri ekvivalentne príkazy:

pre direktívu #ifdef	           pre direktívu #ifndef
#ifdef MENO_MAKRA	           #ifndef MENO_MAKRA
#if defined MENO_MAKRA	           #if !defined MENO_MAKRA
#if defined(MENO_MAKRA)	           #if !defined(MENO_MAKRA)  

Vždy musí byť jasné, kde podmienená časť zdrojového textu začína a kde končí. Preto nesmieme zabúdať na #endif či #elif. Podmienené časti musia byť ukončené a obmedzené v rámci jedného zdrojového textu. Inak oznámi preprocesor chybu.

#if defined MENO_MAKRA    // podmienka
  #define INE_MAKRO       // tato definicia je zavisla od vysledku podmienky
#endif                    // ukoncenie podmienky

Tu máme príklady, kde podľa typu hardvéru si zadefinujeme vždy rôzne knižnice.

#if defined(ESP8266)
  #include <pgmspace.h>
#else
  #include <avr/pgmspace.h>
#endif
#if defined(__AVR__)
  #include <util/delay.h>
#endif
#if defined (__AVR__) || defined(TEENSYDUINO) || defined (__arm__)
  #define USE_FAST_PINIO
#endif

Podmienky veľmi pripomínajú konštrukcie jazyka C. Naviac je oproti C zavedená i podmienka #elif. Nenechajme sa však mýliť. Vyhodnotenie podmienok prevádza už preprocesor.

#error

#error je direktívou, ktorou môžeme zaistiť výstup nami zadaného chybového hlásenia. Najčastejšie sa používa v súvislosti s podmieneným prekladom. Má formát:

#error chybove hlasenie  

kde chybové hlásenie bude súčasťou protokolu o preklade. Táto direktíva sa typicky používa na kontrolu hodnôt symbolických konštánt ovplyvňujúcich podmienenú kompiláciu. Teraz bude uvedený príklad, ktorý by mal objasniť použitie direktív #elif a #error, tak i operátoru defined.

#if defined(ARDUINO_ARCH_AVR)
  // kod pre AVR
#elif defined(ARDUINO_ARCH_SAM)
  // kod pre SAM
#else
  #error "Tato kniznica podporuje len hardver s AVR alebo SAM procesorom."
  // alebo tu môže byť kód pre nešpecifikovanú platformu
#endif

V mnohých prípadoch sa zložitejšie programy píšu tak, že obsahujú ladiace časti. To sú najčastejšie pomocné výpisy, ktoré majú uľahčiť ladenie. Tieto časti sa do programov dávajú, aj keď máme k dispozícii výkonný debuger. Je dobrým zvykom počítať už pri návrhu programu s tým, že ho bude nutné ladiť a už pri návrhu programu tieto časti do programu zaraďovať. Po odladení však nastáva typický problém – ako tieto časti (už nepotrebné) z programu odstrániť. Najjednoduchšie je ich vymazať, ale pritom sa môže stať, že zmažeme aj dôležité časti programu a tým program znefunkčníme.

Tento problémom vyriešime tak, že pomocou príkazov preprocesora môžeme určiť, ktoré časti programu sa majú prekladať podmienene. To znamená, že všetky ladiace časti už pri vytváraní programu označíme ako podmienene prekladané a pri ladení ich prekladáme, ale po odladení už nie. Ladiace časti sú tak trvalou súčasťou zdrojového programu, ale sú zároveň voliteľnou súčasťou. Preprocesor teda na jediný príkaz všetky tieto časti vypustí sám, ale budú k dispozícií do budúcnosti. V prípade potreby ich jedným príkazom zase do programu zaradíme.

//#define DEBUG         // odkomentovaním sa bude vykonávať i kód napr. pre výpis na sériovú linku

#ifdef DEBUG      
  Serial.print("Testovanie kodu.");     // odošleme text na sériovú linku
#endif

#else

Podmienený preklad riadený konštantným výrazom, čo môže byť číslo, symbolická konštanta alebo i podmienený výraz z týchto možností, má túto syntax:

#if konstantny_vyraz
    cast_1
#else
    cast_2
#endif

Prekladač bude túto časť programu spracovávať tak, že ak je hodnota konstantny_vyraz rovná 0 (FALSE, nepravda), prekladá sa iba cast_2 a v opačnom prípade (nenulová hodnota – TRUE) sa prekladá iba cast_1. Samozrejme, že časť #else a cast_2 môžu byť vynechané. Často sa podmienený preklad používa pri vývoji programov, ktoré sú síce závisle na konkrétnom hardvéri, ale mali by po minimálnych zmenách fungovať i na hardvéroch iných. Alebo podľa verzie programového prostredia.

#if ARDUINO >= 100        // podmienka verzie ARDUINO IDE 
 #include "Arduino.h"     // pre novú verziu ARDUINO IDE 1.00 a vyššie použi knižnicu Arduino.h
#else
 #include "WProgram.h"    // pre staršie verzie ARDUINO IDE použi knižnicu WProgram.h
#endif

Súbory, ktoré sa dajú vkladať pomocou príkazu #include, môžu byť ľubovolné textové súbory. Význam má samozrejme vkladanie len zdrojových súborov s príponou .C. Druhá a oveľa častejšia možnosť, je vkladanie tzv. hlavičkových súborov s príponou .h. Vkladanie hlavičkových súborov je veľmi užitočný mechanizmus, ako program pozostávajúci z viacerých súborov udržať čitateľný. Napríklad všetky definície konštánt využívané viacerými súbormi sa uvedú iba raz do súboru typu .h, ktorý sa pomocou #include pripojí do všetkých súborov, ktoré tieto definície konštánt potrebujú. To má obrovskú výhodu, že prípadná zmena konštánt sa potom uskutoční iba v jednom súbore .h a ostatné súbory, využívajúce tieto konštanty, stačí iba preložiť. Tu je príklad definície konštánt popisujúce pomery na obrazovke a sú uložené v súbore obraz.h:

#ifndef OBRAZ
#define OBRAZ

#define SIRKA_OBRAZOVKY 240
#define VYSKA_OBRAZOVKY 320

#endif    // koniec súboru obraz.h 

Vo všetkých súboroch, ktoré potrebujú pracovať s obrazovkou sa potom použije príkaz:

#include "obraz.h"

Avšak vyššie popísaný spôsob je veľmi užitočný “trik”, ako zabrániť niekoľkonásobnému natiahnutiu súboru obraz.h do jedného modulu. Tento omyl sa stane veľmi ľahko, zvlášť, ak sú súbory s príkazmi #include do seba ponorené. Pokiaľ máme hlavičkové súbory vytvárané týmto spôsobom, nič sa nemôže stať ani pri niekoľkonásobnom natiahnutí súboru obraz.h do jedného súboru. Pri prvom natiahnutí sa totiž definuje symbolická konštanta OBRAZ a tá v spolupráci s príkazom#ifndef zabráni opätovnému natiahnutiu obsahu súboru.

Pokiaľ sa text makra nevojde na jeden riadok, môžeme ho rozdeliť na viac nasledujúcich riadkov. Skutočnosť, že makro pokračuje na nasledujúcom riadku, sa určí umiestnením znaku \ ako posledného znaku na riadku.

#define DLHA_KONSTANTA Toto je dlha konstanta, ktora sa \
					 nevojde do jedneho riadku.

K tomuto všetkému však uveďme, že sme skôr popísali možnosť nie nástroj. Makrá majú zvýšiť čitateľnosť programu, nemajú za úlohu urobiť s programu ťažko zrozumiteľný rébus.

Tento text je preložený a upravený zo stránok arduino.cc pod licenciou Creative Commons Attribution-ShareAlike 3.0.

Výber na čítanie...

Arduino MEGA 2560
Arduino MEGA 2560

Arduino MEGA 2560 je navrhnutý pre komplexnejšie projekty. Má väčší priestor pre program, takže sa využíva pre 3D tlačiarne a projekty robotiky. To dáva projektom dostatok priestoru. K tejto doske nájdete aj rôzne shieldy. Medzi najpoužívanejšie patrí napr. ...

Čítať viac...

Atmel Studio 7
Atmel Studio 7

Firma Atmel vydala novú verziu svojho editora Atmel Studio 7. Pre nás fanúšikov Arduina je to dobrou správou, pretože sa viac sústreďuje na Arduino. Nové možnosti sú aj v oblasti moderných 32-bitových mikrokontrolérov. Úžasné na Atmel Studiu je aj to, že nepotrebujete mať ...

Čítať viac...

Arduino IDE - návod na inštaláciu
Arduino IDE – návod na inštaláciu

Úspech Arduina a jeho veľké rozšírenie nie je len vďaka zaujímavému hardvéru, ale predovšetkým jednoduchému vývojovému prostrediu (skrátene IDE, čo doslova znamená Integrated Development Environment). Arduino IDE je aplikácia, ktorá nám umožní jednoducho s doskou Arduino ...

Čítať viac...

Obľúbené produkty...

Keyestudio Smart Little Turtle Robot V3

73.15 EUR

61.44 EUR

Keyestudio Ultimate IOT Super Starter Kit

74.97 EUR

53.98 EUR

Vyhľadať články
Programovanie...
  • Digitálne IO funkcie

    Keďže je Arduino určené k ďalšiemu rozširovaniu, obsahuje vstupy a výstupy (nazývané piny), ku kt...

  • Globálne premenné

    Premenné aj funkcie majú svoj rozsah platnosti, čiže oblasť, v ktorej fungujú a v ktorej nie. Vid...

  • Bitové operátory

    Bitové operátory umožňujú vykonať operácie nad jednotlivými bitmi. Túto možnosť zďaleka nemajú vš...

  • Logické operátory

    Logické operátory sú && || ! (and or a not). Vykonávajú výpočet logických výrazov tvorených ich o...

  • Zložené operátory

    Použitie zložených priraďovacích operátorov zjednodušuje a sprehľadňuje zdrojový kód, no použitie...

Podpora webu
Na kávu už prispeli
Dátum Meno Suma
08.01.2024 Veres Dusan 10€
15.05.2023 Ivan Danis 10€
28.09.2022 Ivan Vrab 7€
14.05.2022 Nemcic Marian 10€
04.02.2022 Robert Bilko 5€
29.01.2022 Peter Buffa 5€
19.11.2021 Rastislav Rehak 5€
16.09.2021 Anton Strela 5€
13.09.2021 Juraj Jedlak 5€
02.09.2021 Michal Marek 7€
08.08.2021 František Uhrík 5€
21.07.2021 Juraj Hrdina 5€
25.03.2021 Jan Nemec 10€
16.03.2021 Igor Pavlov 5€
25.02.2021 Lukas Lacuch 5€
06.11.2020 Pavol Balint 5€
05.11.2020 Marek Horečný 5€
05.10.2020 Jan Kusnir 5€
27.04.2020 Jan Zuskin 15€
26.04.2020 Dušan Sojka 5€
24.02.2020 Juraj Lackanič 5€
22.01.2020 František Žilinec 10€
20.05.2018 Tomáš F. 2€
17.12.2018 Pavol P. 5€
QR Donate 10Eur