dyrektywy #if, #elif, #else i #endif (C/C++)
Dyrektywa #if zawierająca dyrektywy #elif, #else i #endif kontroluje kompilację fragmentów pliku źródłowego. Jeśli wyrażenie pisane (po #if) ma wartość niezerową, grupa wierszy bezpośrednio po dyrektywie #if jest przechowywana w jednostce tłumaczenia.
Gramatyka
warunkowe :
if-part elif-partsopt else-partopt endif-line
if-part :
tekst w wierszu if
if-line :
#if wyrażenie-stałe
identyfikator #ifdef
identyfikator #ifndef
elif-parts :
tekst elif-line
elif-parts elif-line text
elif-line :
#elif wyrażenie-stałe
else-part :
tekst w innym wierszu
else-line :
#else
endif-line :
#endif
Uwagi
Każda dyrektywa #if w pliku źródłowym musi być zgodna z dyrektywą #endif zamykającą. Dowolna liczba dyrektyw #elif może występować między dyrektywami #if i #endif, ale dozwolona jest co najwyżej jedna dyrektywa #else. Dyrektywa #else , jeśli istnieje, musi być ostatnią dyrektywą przed #endif.
Dyrektywy #if, #elif, #else i #endif mogą zagnieżdżać fragmenty tekstu innych dyrektyw #if . Każda zagnieżdżona dyrektywa #else, #elif lub #endif należy do najbliższej poprzedniej dyrektywy #if .
Wszystkie dyrektywy kompilacji warunkowej, takie jak #if i #ifdef, muszą być zgodne z dyrektywą #endif zamykającą przed końcem pliku. W przeciwnym razie jest generowany komunikat o błędzie. Gdy dyrektywy kompilacji warunkowej są zawarte w plikach dołączanych, muszą spełniać te same warunki: na końcu pliku dołączania nie musi być żadnych niezgodnych dyrektyw kompilacji warunkowej.
Zamiana makr jest wykonywana w części wiersza, która jest zgodna z #elif polecenia, więc w wyrażeniu stałym można użyć wywołania makra.
Preprocesor wybiera jedno z podanych wystąpień tekstu do dalszego przetwarzania. Blok określony w tekście może być dowolną sekwencją tekstu. Może zajmować więcej niż jedną linię. Zazwyczaj tekst jest tekstem programu, który ma znaczenie dla kompilatora lub preprocesora.
Preprocesor przetwarza zaznaczony tekst i przekazuje go do kompilatora. Jeśli tekst zawiera dyrektywy preprocesora, preprocesor wykonuje te dyrektywy. Kompilowane są tylko bloki tekstowe wybrane przez preprocesor.
Preprocesor wybiera pojedynczy element tekstowy , oceniając wyrażenie stałe po każdej dyrektywie #if lub #elif , dopóki nie znajdzie wyrażenia stałej true (nonzero). Wybiera cały tekst (w tym inne dyrektywy preprocesora rozpoczynające się #od ) do skojarzonych #elif, #else lub #endif.
Jeśli wszystkie wystąpienia wyrażenia stałego są fałszywe lub jeśli nie zostaną wyświetlone żadne dyrektywy #elif, preprocesor wybiera blok tekstowy po klauzuli #else. Jeśli nie ma klauzuli #else, a wszystkie wystąpienia wyrażenia stałego w bloku #if są fałszywe, nie jest zaznaczony żaden blok tekstowy.
Wyrażenie stałe jest wyrażeniem stałej liczby całkowitej z następującymi dodatkowymi ograniczeniami:
Wyrażenia muszą mieć typ całkowity i mogą zawierać tylko stałe całkowite, stałe znaków i zdefiniowany operator.
Wyrażenie nie może użyć
sizeof
ani operatora rzutowania typu.Środowisko docelowe może nie reprezentować wszystkich zakresów liczb całkowitych.
Tłumaczenie reprezentuje typ
int
w taki sam sposób, jak typlong
, iunsigned int
tak samo jakunsigned long
.Tłumacz może tłumaczyć stałe znaków na zestaw wartości kodu różnić się od zestawu dla środowiska docelowego. Aby określić właściwości środowiska docelowego, użyj aplikacji utworzonej dla tego środowiska, aby sprawdzić wartości limitów. Makra H .
Wyrażenie nie może wykonywać zapytań względem środowiska i musi pozostać odizolowane od szczegółów implementacji na komputerze docelowym.
Operatory preprocesora
Definicja
Zdefiniowany operator preprocesora może być używany w specjalnych wyrażeniach stałych, jak pokazano w następującej składni:
defined( identyfikator )
zdefiniowany identyfikator
To wyrażenie stałe jest uznawane za prawdziwe (niezerowe), jeśli identyfikator jest obecnie zdefiniowany. W przeciwnym razie warunek ma wartość false (0). Identyfikator zdefiniowany jako pusty tekst jest uznawany za zdefiniowany. Zdefiniowany operator może być używany w #if i dyrektywie #elif, ale nigdzie indziej.
W poniższym przykładzie dyrektywy #if i #endif kontrolują kompilację jednej z trzech wywołań funkcji:
#if defined(CREDIT)
credit();
#elif defined(DEBIT)
debit();
#else
printerror();
#endif
Wywołanie funkcji do credit
jest kompilowane, jeśli jest zdefiniowany identyfikator CREDIT
. Jeśli identyfikator DEBIT
jest zdefiniowany, wywołanie funkcji jest debit
kompilowane. Jeśli żaden z identyfikatorów nie jest zdefiniowany, wywołanie metody printerror
jest kompilowane. Zarówno identyfikatory, jak CREDIT
i credit
są odrębnymi identyfikatorami w językach C i C++, ponieważ ich przypadki są różne.
Instrukcje kompilacji warunkowej w poniższym przykładzie zakładają wcześniej zdefiniowaną stałą symboliczną o nazwie DLEVEL
.
#if DLEVEL > 5
#define SIGNAL 1
#if STACKUSE == 1
#define STACK 200
#else
#define STACK 100
#endif
#else
#define SIGNAL 0
#if STACKUSE == 1
#define STACK 100
#else
#define STACK 50
#endif
#endif
#if DLEVEL == 0
#define STACK 0
#elif DLEVEL == 1
#define STACK 100
#elif DLEVEL > 5
display( debugptr );
#else
#define STACK 200
#endif
Pierwszy blok #if przedstawia dwa zestawy zagnieżdżonych dyrektyw #if, #else i #endif . Pierwszy zestaw dyrektyw jest przetwarzany tylko wtedy, gdy DLEVEL > 5
ma wartość true. W przeciwnym razie instrukcje po #else są przetwarzane.
Dyrektywy #elif i #else w drugim przykładzie służą do dokonywania jednej z czterech opcji na podstawie wartości DLEVEL
. Stała STACK
jest ustawiona na 0, 100 lub 200 w zależności od definicji DLEVEL
. Jeśli DLEVEL
wartość jest większa niż 5, instrukcja
#elif DLEVEL > 5
display(debugptr);
jest kompilowany i STACK
nie jest zdefiniowany.
Typowym zastosowaniem kompilacji warunkowej jest zapobieganie wielokrotnym dołączaniu tego samego pliku nagłówka. W języku C++, gdzie klasy są często definiowane w plikach nagłówkowych, konstrukcje takie jak ten mogą służyć do zapobiegania wielu definicjom:
/* EXAMPLE.H - Example header file */
#if !defined( EXAMPLE_H )
#define EXAMPLE_H
class Example
{
//...
};
#endif // !defined( EXAMPLE_H )
Powyższy kod sprawdza, czy jest zdefiniowana stała EXAMPLE_H
symboliczna. Jeśli tak, plik został już dołączony i nie wymaga ponownego przetwarzania. Jeśli nie, stała EXAMPLE_H
jest zdefiniowana do oznaczania EXAMPLE. H jako już przetworzone.
__has_include
Program Visual Studio 2017 w wersji 15.3 lub nowszej: określa, czy nagłówek biblioteki jest dostępny do dołączania:
#ifdef __has_include
# if __has_include(<filesystem>)
# include <filesystem>
# define have_filesystem 1
# elif __has_include(<experimental/filesystem>)
# include <experimental/filesystem>
# define have_filesystem 1
# define experimental_filesystem
# else
# define have_filesystem 0
# endif
#endif