Урок3. Директивы препроцессора C++
В этой статье мы продолжим постигать искусство программирования на языке С++. На этом этапе обучения пора познакомится с такими вещами, как директивы препроцессора. Забегая наперед скажу, что в предыдущих уроках мы уже использовали директиву #include, которая служит для подключения заголовочных файлов.
Вначале дадим определение, что такое препроцессор. Компиляция любой программы происходит в несколько этапов, при чем один из первых — обработка препроцессором. Если говорить простыми словами, то препроцессор это такая программа, которая считывает исходный код программы и на основе директив изменяет его. Схематически весь процесс сборки программы можно представить следующим образом.
Как видно перед самой компиляцией исходный текст программы обрабатывает препроцессор, давайте познакомимся с его инструкциями поближе.
К основным директивам препроцессора C++ относятся:
- #include — вставляет исходный код из файла
- #define — позволяет создать символическую константу или макроопределение (макрос)
- #ifdef и #ifndef— проверяет задана ли символическая константа при компиляцию
- #undef — отменяет действие команды #define
- #if — выполняется при условии истинности выражение
- #else — выполняется, если выражение в предыдущем #if
- #elif — позволяет произвести дополнительную проверку
- #endif — указывает на окончание ветки условной компиляции
- #line — позволяет переопределить встроенные константы __LINE__ и __FILE__
- #error — выдача ошибки
- #pragma — указывает действие, которое зависит от реализации конкретного компилятора.
Директива #include
Начнем с директивы #include, которая заменяется препроцессором на содержимое следующего за ней файла. Пример использования #include:
#include <header1.h> #include «header2.h»
Если имя файл заключено в угловые скобки, то препроцессор ищет файл в предопределенном месте. Использование двойных скобок предполагает подключение файла с того же каталога, где лежит исходный код компилируемой программы. Стоит также заметить, что подключаемые файлы также могут содержать в себе директивы препроцессора, в частности директиву #include, поэтому могут возникнуть проблемы с многократным подключением одного и того же файла. Для избежания подобного рода путаницы были введены условные директивы, давайте рассмотрим пример их использования:
Директивы #ifdef и #define
#ifndef CUCUMBLER_H #define CUCUMBLER_H /* содержимое файла cucumbler.h */ #endif
Директива #ifndef выполняет проверку не была ли определена константа CUCUMBLER_H ранее, и если ответ отрицательный, то выполняется определение данной константы, и прочего кода, который следует до директивы #endif. Как не сложно догадаться директива #define определяет константу CUCUMBLER_H. В данном случае подобный кусок кода помогает избежать многократного включения одного и того же кода, так как после первого включения проинициализируется константа CUCUMBLER_H и последующие проверки #ifndef CUCUMBLER_H будут возвращать FALSE.
Директива #define широко применяется и при отладке программы.
#include <iostream> #include <string> #include <vector> using namespace std; int main() { #ifdef IN_DEBUG cout << "Начало функции main()\n"; #endif string text; vector<string> text_array; while ( cin >> text ) { #ifdef IN_DEBUG cout << "Прочитан текст: " << text << "\n"; #endif text_array.push_back(text); } return 0; }
Если константа IN_DEBUG не задана, то препроцессор сгенерирует следующий исходник:
#include <iostream> #include <string> #include <vector> using namespace std; int main() { string text; vector<string> text_array; while ( cin >> text ) { text_array.push_back(text); } return 0; }
Но если определить IN_DEBUG, то текст программы кардинальным образом поменяется
#include <iostream> #include <string> #include <vector> using namespace std; int main() { cout << "Начало функции main()\n"; string text; vector<string> text_array; while ( cin >> text ) { cout << "Прочитан текст: " << text << "\n"; text_array.push_back(text); } return 0; }
Задать препроцессорную константу можно прямо из консоли. Например для компилятора g++ применяется следующий формат
$g++ -D IN_DEBUG ./main.cpp.
Директива препроцессора #line
В языке C++ есть ряд предопределенных констант. Директива #line позволяет изменить значение __LINE__ - номер текущей строки в файле и __FILE__ - имя текущего файла:
#line 1000 "filename.h"
Директива #error
Директива позволяет прервать процесс процесс компиляции. Используется следующим образом:
#ifndef END_DATE #error Need to set end date #endif