C++ Препроцессор

Препроцессоры — это директивы, которые указывают компилятору выполнить предварительную обработку, необходимую перед фактической компиляцией.

Все директивы препроцессора начинаются со знака решётки (#). Перед директивой препроцессора могут находиться только пробельные символы. Директивы препроцессора не являются операторами C++, поэтому они не заканчиваются точкой с запятой (;).

Мы видели, что во всех предыдущих примерах,#includeДиректива. Этот макрос используется для включения заголовочного файла в исходный файл.

C++ также поддерживает множество директив препроцессора, таких как #include, #define, #if, #else, #line и т. д. Давайте взглянем на эти важные директивы.

#define Препроцессор

Директива препроцессора #define используется для создания символьных констант. Эта символьная константа часто называетсямакрособщий вид директивы:

#define macro-name replacement-text 

Когда эта строка кода появляется в файле, все последующие вхождения макроса в этом файле будут заменены на replacement-text перед компиляцией программы. Например:

#include <iostream> using namespace std; #define PI 3.14159 int main () { cout << "Значение PI :" << PI << endl; return 0; }

Теперь давайте протестируем этот код, чтобы увидеть результат предварительной обработки. Предполагая, что файл исходного кода уже существует, далее используйте опцию -E для компиляции и перенаправьте результат в test.p. Теперь, если вы посмотрите на файл test.p, вы увидите, что он уже содержит большой объём информации, и значение внизу файла было изменено на следующее:

$ gcc -E test.cpp > test.p

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

    return 0;
}

Параметризованный макрос

Вы можете использовать #define для определения макроса с параметрами, как показано ниже:

#include <iostream> using namespace std; #define MIN(a,b) (a<b ? a : b) int main () { int i, j; i = 100; j = 30; cout <<"Меньшее значение:" << MIN(i, j) << endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

较小的值为:30

Условная компиляция

Существует несколько директив, которые можно использовать для выборочной компиляции частей исходного кода программы. Этот процесс называется условной компиляцией.

Структура условного препроцессора очень похожа на структуру выбора if. Взгляните на следующий код препроцессора:

#ifdef NULL
   #define NULL 0
#endif

Можно компилировать только во время отладки. Переключатель отладки можно реализовать с помощью макроса, как показано ниже:

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

Если символьная константа DEBUG была определена до директивы #ifdef DEBUG, то операторы в программеcerrоператоры будут скомпилированы. Вы можете использовать инструкцию #if 0, чтобы закомментировать часть программы, как показано ниже:

#if 0
   不进行编译的代码
#endif

Давайте попробуем следующий пример:

Пример

#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 <<"Трассировка: внутри функции main" << endl; #endif #if 0 /*Это раздел комментариев*/ cout << MKSTR(HELLO C++) << endl; #endif cout <<"Минимум равен " << MIN(i, j) << endl; #ifdef DEBUG cerr <<"Трассировка: выход из функции main" << endl; #endif return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

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

Операторы # и ##

Операторы препроцессора # и ## доступны как в C++, так и в ANSI/ISO C. Оператор # преобразует токен текста замены в строку, заключённую в кавычки.

Рассмотрим следующее определение макроса:

Пример

#include <iostream> using namespace std; #define MKSTR( x ) #x int main () { cout << MKSTR(HELLO C++) << endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

HELLO C++

Давайте посмотрим, как это работает. Нетрудно понять, что препроцессор C++ берёт следующую строку:

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

Преобразуется в:

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

Оператор ## используется для конкатенации двух токенов. Вот пример:

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

Когда CONCAT появляется в программе, его аргументы конкатенируются и используются для замены макроса. Например, CONCAT(HELLO, C++) в программе будет заменено на "HELLO C++", как показано в примере ниже.

Пример

#include <iostream> using namespace std; #define concat(a, b) a ## b int main() { int xy = 100; cout << concat(x, y); return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

100

Давайте посмотрим, как это работает. Нетрудно понять, что препроцессор C++ берёт следующую строку:

cout << concat(x, y);

Преобразуется в:

cout << xy;

Предопределённые макросы в C++

C++ предоставляет некоторые предопределённые макросы, показанные в следующей таблице:

Макрос Описание
__LINE__ Это будет включать текущий номер строки при компиляции программы.
__FILE__ Это будет включать имя текущего файла при компиляции программы.
__DATE__ Это будет включать строку в формате месяц/день/год, которая представляет дату, когда исходный файл преобразуется в объектный код.
__TIME__ Он будет содержать строку в формате час:минута:секунда, которая представляет время, когда программа была скомпилирована.

Давайте рассмотрим примеры вышеуказанных макросов:

Пример

#include <iostream> using namespace std; int main () { cout << "Значение __LINE__ : " << __LINE__ << endl; cout << "Значение __FILE__ : " << __FILE__ << endl; cout << "Значение __DATE__ : " << __DATE__ << endl; cout << "Значение __TIME__ : " << __TIME__ << endl; return 0; }

Когда приведенный выше код скомпилирован и выполнен, он выдает следующие результаты:

Value of __LINE__ : 6
Value of __FILE__ : test.cpp
Value of __DATE__ : Feb 28 2011
Value of __TIME__ : 18:52:48
Другие расширения