czwartek, 16 czerwca 2016

C - Preprocesor

W tym poście omówię zagadnieniu preprocesora w języku C.

Teoria


Proprocesor jest to oddzielny krok wykonywany podczas kompilacji. Składa się na niego zestaw instrukcji jakie są wykonywane za nim proces kompilacji się rozpocznie. 

Komendy jakie się wykorzystuje są poprzedzone symbolem "#". Poniżej przedstawiam listę często spotykanych kodów wraz z przykładami.



#define - pozwala na zdefiniowanie makra wykorzystywanego w programie, może nim być np ciąg znaków, wartość stała itp.

Linijki pokazane poniżej zastępują zdefiniowany ciąg znaków wartością która jest wprowadzona po niej.

#define dlugosc 20
#define znak k

Najczęściej jest wykorzystywana do przypisywania odpowiednich wyprowadzeń z mikrokontrolera, definiowania wartości stałej czy definicję odpowiednich rejestrów.

#include - pozwala na dołączenie bibliotek (.h) do programu.

#include <stdio.h>
#include <stdint.h>
#include "stm32f10x.h"

Można pobrać bibliotekę z tych zdefiniowanych w standardowych bibliotekach dla danego języka, bądź dla tych umieszczonych w projekcie.

#undef - pozwala na wyłączenie aktualnej definicja dla określonego makra, i zastąpienie go inną

#undef zmienna
#define zmienna 15

Kasuje ona wartość zdefiniowanej zmiennej, po czym w nowej linijce można wprowadzić do niej inną nazwę.

#ifndef - zwraca wartość TRUE jeśli dane makro nie zostało zdefiniowane.

#ifndef zdefiniowana
   #define zdefiniowana "Jeszcze nie"
#endif 

Najpierw następuje sprawdzenie czy dana zmienna została już zdefiniowana, jeśli nie to następuje wejście do if'a,

#ifdef - zwraca wartość TRUE jeśli dane makro zostało zdefiniowane.

#if - sprawdzenie czy wartość jest prawdziwa

#else -  jeśli nie if, to wykonaj instrukcje z else

#elif - czyli taki else if.

#if k == 10
    podziel();
#elif k > 10
    mnozenie;
#else
    dzielenie();
#endif

#endif - zakończenie procedury preprocesora, po if należy ją dawać

Poniżej wykorzystanie kilku instrukcji zdefiniowanych powyżej:

#if zmienna > 10
    #define wartosc  1
    #if dana == 1
        #define licznik   200
    #elif dana < 3
        #define licznik 150
    #else
        #define licznik   100
    #endif
#else
    #define wartosc  0
    #if dana == 1
        #define licznik   80
    #else
        #define licznik  40
    #endif
#endif

#error - wyświetla wiadomość z błędem, po jej definicji wprowadza się tekst jaki będzie wyświetlany po czym kompilacja zostaje przerwana. Często wykorzystuje się ją if else, ponieważ umieszczona bez zdefiniowanego warunku automatycznie zakończy proces kompilacji.

#if defined(zmienna)
   #define dana 9
#else
   #error "Zla deklaracja"
#endif

#warning - podobnie jak error wyświetla ostrzeżenie, natomiast nie powoduje przerwania kompilacji.

#warning "Zla definicja parametru moga wystapic bledy"

#pragma - specjalne komendy dla kompilatora. Służy do dodawania wątków z wykorzystaniem OpenML.

Kolejnym ciekawym i dosyć często stosowanym rozwiązaniem jest definiowanie makr za pomocą komendy #define. Można za tego pomocą wprowadzać np. jakieś wzory, z których dokonywane będą obliczenia dla czujników czy też zwykłe operacje arytmetyczne, wywoływanie wiadomości itp.

#define mnozenie(x,y) (x * y)

Innym rodzajem są tzw. makra przedefiniowane. Do nich zaliczamy:

  • __DATE__ - wyświetlenie daty jaka jest w momencie kompilacji. W formacie mm dd rrrr
  • __TIME__ - wyświetlenie godziny w momencie kompilacji
  • __FILE__ - zawiera nazwę pliku, który jest aktualnie kompilowany. Przechowuje go w postaci stringu.
  • __LINE__ - pozwala na zdefiniowanie numeru linii
  • __STDC__ - dla kompilatorów obsługujących standard ANSI przyjmuje on wartość 1
Dodatkowo są jeszcze stosowane pewne operatory jak: \, #, ##. Pierwszy z nich jest stosowany aby rozdzielić makro na dwie linie, jeśli nie można zapisać całości w jednej. Drugi zamienia wartość jaka jest parametrem makra na wartość string. Działa tylko jak dla określonego makra zdefiniowany jest konwertowany argument. Operator ## pozwala na połączenie dwóch oddzielnych tokenów w jeden.