Dyrektywy preprocesora

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 20829 razy

Dyrektywy preprocesora w C++ są poleceniami, które kompilator wykonuje zanim zacznie on tworzyć kod programu. Dzięki takiemu rozwiązaniu można dość swobodnie sterować tym co zostanie dołączone do kodu źródłowego programu. Wszystkie dyrektywy zaczynają się od znaku #.

Dyrektywa #include

Jest to jedna z podstawowych dyrektyw, która służy do załączania do plików z rozszerzeniem .cpp i .h definicji struktur, klas i nagłówków funkcji zawartych w oddzielnych plikach lub bibliotekach systemowych. Pliki .h mogą być załączane na trzy sposoby:

  1. poprzez odwołanie się do plików znajdujących się w domyślnej lokalizacji podstawowych plików .h kompilatora, odwołanie takie ma następującą postać przykładową:
    #include <iostream>
  2. poprzez odwołanie się do pliku z lokalizacji domyślnej projektu
    #include "moje_funkcje.h"
  3. poprzez podanie bezwzględnej ścieżki do pliku, np.
    #include "C:\\moje_funkcje.h"

Ostatni z sposobów nie jest zbyt często stosowany ani też zalecany.

Dyrektywa #define

Dyrektywa ta jest często stosowana do tworzenia stałych oraz makr. Stałe definiowane są w następujący sposób:

#define ST_PI 3.141592654 #define ST_E 2.718281828

Powyższy kod tworzy stałe w postaci literałów, zanim kod programu zostanie skompilowany kompilator wszędzie tam gdzie wystąpi słowo ST_PI wstawi ciąg znaków 3.141592654 i to samo tyczy się ST_E. Deklarowanie powyżej wypisanych stałych mija się z celem, gdyż wszystkie stałe matematyczne znajdują się w pliku nagłówkowym math.h, aby jednak z nich skorzystać konieczne jest napisanie następującej dyrektywy:

#define _USE_MATH_DEFINES #include <math.h>

W ten sposób odblokowany zostanie następujący kod znajdujący się w pliku nagłówkowym math.h:

#define M_E 2.71828182845904523536 #define M_LOG2E 1.44269504088896340736 #define M_LOG10E 0.434294481903251827651 #define M_LN2 0.693147180559945309417 #define M_LN10 2.30258509299404568402 #define M_PI 3.14159265358979323846 #define M_PI_2 1.57079632679489661923 #define M_PI_4 0.785398163397448309616 #define M_1_PI 0.318309886183790671538 #define M_2_PI 0.636619772367581343076 #define M_2_SQRTPI 1.12837916709551257390 #define M_SQRT2 1.41421356237309504880 #define M_SQRT1_2 0.707106781186547524401

Dyrektywa #define umożliwia również tworzenie własnych makr, bardzo prosty i niezbyt przydatny przykład obrazujący zasadę definiowania makra napisałem poniżej:

#define POMNOZ(a, b) (a * b) int a = POMNOZ(2, 3); // poprawne użycie makra int b = POMNOZ(2 + 1, 3); // niepoprawne użycie makra

Powyżej pokazany został częsty błąd, jaki można popełnić wykorzystując makra tego typu, otóż w przypadku zmiennej int o nazwie b wynikiem będzie 5 a to dlatego, ze kompilator wstawi w miejsce POMNOZ(2 + 1, 3) kod 2 + 1 * 3. Z tego względu należałoby tutaj stworzyć makro następującej postaci:

#define POMNOZ(a, b) ((a) * (b))

Niezbyt zgrabne rozwiązanie, ale poprawniejsze składniowo.

Poniżej podaję przykłady standardowych makr:

UWAGA! Przyjęło się, że wszystkie nazwy stałych i makr są pisane DUŻYMI LITERAMI dla odróżnienia od nazewnictwa zmiennych zawartych w kodzie programu.

Dyrektywa #undef

Dyrektywa ta jest wykorzystywana do usunięcia deklaracji jakiejś zmiennej czy też makra utworzonego dyrektywą #define.

Instrukcja warunkowa #if #elif #else #endif

Tego typu instrukcja umożliwia sterowanie kodem programu, prosty przykład jest:

#define COMPILE 1 #if COMPILE == 0 // jeżeli stała COMPILE ma wartość 0 to // instrukcje do wykonania #elif COMPILE == 1 // w przeciwnym przypadku jeżeli stała COMPILE ma wartość 1 to // instrukcje do wykonania #else // w przeciwnym przypadku // instrukcje do wykonania #endif

Wystarczy w jednym miejscu zmienić jedną wartość, aby kod, który zostanie załączony do programu podczas jego kompilacji się zmienił.

Instrukcja warunkowa #ifdef #ifndef #else #endif

Jest to podobny zbiór instrukcji, z tą różnicą że sprawdza on czy dana stała została zadeklarowana, czy też nie. Często wykorzystywanym kodem jest zabezpieczenie przed podwójnym załączaniem pliku nagłówkowego h:

#ifndef NAZWA_PLIKU_H #define NAZWA_PLIKU_H // nagłówki funkcji, definicje struktur i klas #endif
Propozycje książek