Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Язык C

Препроцессор C++: как директивы формируют ваш код

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

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

Вы уже видели директиву #include во всех примерах. Этот макрос включает заголовочный файл в исходный файл.

Существует множество директив: #include, #define, #if, #else, #line и др. Рассмотрим важные.

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

Директива #define создаёт символические константы. Символическая константа называется макросом. Общая форма:

#define имя_макроса текст_замены

Когда эта строка встречается в файле, все последующие вхождения макроса в этом файле заменяются на текст_замены до компиляции. Пример:

#include <iostream>
using namespace std;
#define PI 3.14159
int main() {
    cout << "Value of PI :" << PI << endl;
    return 0;
}

Если сделать препроцессинг этого кода (компиляция с флагом -E и перенаправление в test.p), внизу файла можно увидеть замену:

$ gcc -E test.cpp > test.p
...
int main() {
    cout << "Value of PI :" << 3.14159 << endl;
    return 0;
}

Функциональные макросы

С помощью #define можно определить макрос, принимающий аргументы:

Live Demo
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
    int i = 100, j = 30;
    cout << "The minimum is " << MIN(i, j) << endl;
    return 0;
}

Результат:

The minimum is 30

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

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

Условная конструкция препроцессора похожа на структуру выбора if. Пример:

#ifndef NULL
#define NULL 0
#endif

Можно компилировать программу для отладки. Включение/выключение отладки через один макрос:

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

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

#if 0
код, исключённый из компиляции
#endif

Пример:

Live Demo
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
    int i = 100, j = 30;
    #ifdef DEBUG
    cerr << "Trace: Inside main function" << endl;
    #endif
    #if 0
    /* Это закомментированная часть */
    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;
}

Результат:

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

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

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

Рассмотрим макрос:

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

Результат:

HELLO C++

Как это сработало: препроцессор превращает строку

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

в

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

Оператор ## конкатенирует два токена. Пример:

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

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

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

Результат:

100

Препроцессор трансформирует

cout << concat(x, y);

в

cout << xy;

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

C++ предоставляет ряд предопределённых макросов:

№Макрос и описание
1

__LINE__

Содержит текущий номер строки программы при компиляции.

2

__FILE__

Содержит текущее имя файла программы при компиляции.

3

__DATE__

Содержит строку вида месяц/день/год — дата трансляции исходного файла в объектный код.

4

__TIME__

Содержит строку вида час:минута:секунда — время компиляции программы.

Пример для всех макросов:

Live Demo
#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;
}

Результат (пример):

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

Язык C

  1. Освоение директив препроцессора C#: практическое руководство
  2. Понимание типов данных C++: основы, модификаторы и лучшие практики
  3. Освоение операторов C++: полное руководство с практическими примерами
  4. Комментарии в C++: лучшие практики для читаемого и поддерживаемого кода
  5. Понимание конструкторов в C++: конструкторы по умолчанию, параметризованные и копирования
  6. Шаблоны классов в C++: практическое руководство
  7. Предпроцессор C (CPP): как он упрощает компиляцию
  8. Язык программирования C++: обзор и ключевые особенности
  9. Константы и литералы в C++: типы, синтаксис и лучшие практики
  10. Полное руководство по операторам C++: типы, использование и лучшие практики