Preprocesor C ++

Preprocesory to dyrektywy, które przekazują kompilatorowi instrukcje dotyczące wstępnego przetworzenia informacji przed rozpoczęciem właściwej kompilacji.

Wszystkie dyrektywy preprocesora zaczynają się od #, a przed dyrektywą preprocesora w wierszu mogą pojawić się tylko znaki odstępu. Dyrektywy preprocesora nie są instrukcjami C ++, więc nie kończą się średnikiem (;).

Widziałeś już #includedyrektywy we wszystkich przykładach. To makro służy do dołączania pliku nagłówkowego do pliku źródłowego.

Istnieje wiele dyrektyw preprocesora obsługiwanych przez C ++, takich jak #include, #define, #if, #else, #line, itd. Zobaczmy ważne dyrektywy -

#Define Preprocessor

Dyrektywa #define preprocesora tworzy symboliczne stałe. Symboliczna stała nazywana jest amacro a ogólna forma dyrektywy to -

#define macro-name replacement-text

Kiedy ta linia pojawi się w pliku, wszystkie kolejne wystąpienia makra w tym pliku zostaną zastąpione tekstem zastępczym przed skompilowaniem programu. Na przykład -

#include <iostream>
using namespace std;

#define PI 3.14159

int main () {
   cout << "Value of PI :" << PI << endl; 

   return 0;
}

Teraz wykonajmy wstępne przetwarzanie tego kodu, aby zobaczyć wynik, zakładając, że mamy plik kodu źródłowego. Skompilujmy go więc z opcją -E i przekierujmy wynik do test.p. Teraz, jeśli zaznaczysz test.p, będzie on zawierał wiele informacji, a na dole znajdziesz wartość zamienioną w następujący sposób -

$gcc -E test.cpp > test.p

...
int main () {
   cout << "Value of PI :" << 3.14159 << endl; 
   return 0;
}

Makra podobne do funkcji

Możesz użyć #define, aby zdefiniować makro, które będzie przyjmować argument w następujący sposób -

#include <iostream>
using namespace std;

#define MIN(a,b) (((a)<(b)) ? a : b)

int main () {
   int i, j;
   
   i = 100;
   j = 30;
   
   cout <<"The minimum is " << MIN(i, j) << endl;

   return 0;
}

Jeśli skompilujemy i uruchomimy powyższy kod, dałoby to następujący wynik -

The minimum is 30

Kompilacja warunkowa

Istnieje kilka dyrektyw, których można użyć do kompilacji wybranych fragmentów kodu źródłowego programu. Ten proces nazywa się kompilacją warunkową.

Konstrukcja preprocesora warunkowego jest podobna do struktury selekcji „if”. Rozważmy następujący kod preprocesora -

#ifndef NULL
   #define NULL 0
#endif

Możesz skompilować program do celów debugowania. Możesz także włączyć lub wyłączyć debugowanie za pomocą jednego makra w następujący sposób -

#ifdef DEBUG
   cerr <<"Variable x = " << x << endl;
#endif

To powoduje cerrinstrukcja do wkompilowania w programie, jeśli stała symboliczna DEBUG została zdefiniowana przed dyrektywą #ifdef DEBUG. Możesz użyć instrukcji #if 0, aby skomentować część programu w następujący sposób -

#if 0
   code prevented from compiling
#endif

Wypróbujmy następujący przykład -

#include <iostream>
using namespace std;
#define DEBUG

#define MIN(a,b) (((a)<(b)) ? a : b)

int main () {
   int i, j;
   
   i = 100;
   j = 30;

#ifdef DEBUG
   cerr <<"Trace: Inside main function" << endl;
#endif

#if 0
   /* This is commented part */
   cout << MKSTR(HELLO C++) << endl;
#endif

   cout <<"The minimum is " << MIN(i, j) << endl;

#ifdef DEBUG
   cerr <<"Trace: Coming out of main function" << endl;
#endif

   return 0;
}

Jeśli skompilujemy i uruchomimy powyższy kod, dałoby to następujący wynik -

The minimum is 30
Trace: Inside main function
Trace: Coming out of main function

Operatory # i ##

Operatory preprocesorów # i ## są dostępne w językach C ++ i ANSI / ISO C. Operator # powoduje, że token zastępujący tekst jest konwertowany na ciąg w cudzysłowie.

Rozważ następującą definicję makra -

#include <iostream>
using namespace std;

#define MKSTR( x ) #x

int main () {

   cout << MKSTR(HELLO C++) << endl;

   return 0;
}

Jeśli skompilujemy i uruchomimy powyższy kod, dałoby to następujący wynik -

HELLO C++

Zobaczmy, jak to działało. Łatwo zrozumieć, że preprocesor C ++ zmienia linię -

cout << MKSTR(HELLO C++) << endl;

Powyższa linia zostanie zamieniona w następującą linię -

cout << "HELLO C++" << endl;

Operator ## służy do łączenia dwóch tokenów. Oto przykład -

#define CONCAT( x, y )  x ## y

Gdy w programie pojawi się CONCAT, jego argumenty są łączone i używane do zastąpienia makra. Na przykład CONCAT (HELLO, C ++) jest zastępowane w programie przez „HELLO C ++” w następujący sposób.

#include <iostream>
using namespace std;

#define concat(a, b) a ## b
int main() {
   int xy = 100;
   
   cout << concat(x, y);
   return 0;
}

Jeśli skompilujemy i uruchomimy powyższy kod, dałoby to następujący wynik -

100

Zobaczmy, jak to działało. Łatwo zrozumieć, że preprocesor C ++ przekształca -

cout << concat(x, y);

Powyższa linia zostanie przekształcona w następującą linię -

cout << xy;

Wstępnie zdefiniowane makra C ++

C ++ udostępnia szereg wstępnie zdefiniowanych makr wymienionych poniżej -

Sr.No Makro i opis
1

__LINE__

Zawiera aktualny numer wiersza programu podczas kompilacji.

2

__FILE__

Zawiera aktualną nazwę pliku programu podczas kompilacji.

3

__DATE__

Zawiera ciąg w postaci miesiąc / dzień / rok, który jest datą tłumaczenia pliku źródłowego na kod wynikowy.

4

__TIME__

Zawiera ciąg w postaci godzina: minuta: sekunda, czyli czas, w którym program został skompilowany.

Zobaczmy przykład dla wszystkich powyższych makr -

#include <iostream>
using namespace std;

int main () {
   cout << "Value of __LINE__ : " << __LINE__ << endl;
   cout << "Value of __FILE__ : " << __FILE__ << endl;
   cout << "Value of __DATE__ : " << __DATE__ << endl;
   cout << "Value of __TIME__ : " << __TIME__ << endl;

   return 0;
}

Jeśli skompilujemy i uruchomimy powyższy kod, dałoby to następujący wynik -

Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48