Урок3. Директивы препроцессора C++

16 октября 2015 13:29
6127
0

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

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

Директивы препроцессора C++

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

К основным директивам препроцессора 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

Похожие статьи