Препроцессор. Общие сведения. Директивы препроцессора. Обзор.
Директивы #define, #error, #include, #undef, #import, #using, #pragma, #line
Содержание
- 1. Препроцессор. Назначение. Директивы
- 2. Директива #define. Определение имени макроса
- 3. Директива #error. Отображение сообщения об ошибке
- 4. Директива #include. Включение заголовка или иного исходного файла
- 5. Директива #undef
- 6. Директива #import. Включение сведений из библиотеки типов
- 7. Директива #using. Включение *.dll, *.obj, *.exe, *.net module файлов
- 8. Директива #pragma. Задание компилятору функций выполнения
- 9. Директива #line. Установить строку на название файла в сообщениях об ошибке
- Связанные темы
1. Препроцессор. Назначение. Директивы
В языке программирования C++ препроцессор – это часть компилятора, управляющая формированием исходного кода в объектный. Препроцессор C++ унаследован из языка программирования C. Препроцессор имеет набор команд, называемых директивами препроцессора. С помощью этих директив препроцессор управляет изменениями в трансляции исходного кода в объектный.
Любая директива препроцессора начинается с символа #. В C++ препроцессор содержит следующие директивы:
- #define;
- #if;
- #endif;
- #undef;
- #error;
- #else;
- #ifdef;
- #line;
- #include;
- #elif;
- #ifndef;
- #pragma;
- #using;
- #line;
- # або NULL.
⇑
2. Директива #define. Определение имени макроса
Директива #define определяет идентификатор и последовательность символов, которые будут заменять этот идентификатор в программе. Более подробно о директиве #define и примерах ее использования можно прочитать здесь.
Общий вид использования директивы следующий
#define macro_name character_sequence
здесь
- macro_name – имя, которое будет использовано в тексте программы;
- character_sequence – последовательность символов, которая будет заменять имя macro_name каждый раз, когда оно встретится в программе.
Пример.
#include <iostream> using namespace std; // Возвести x в степень 4 #define ABC(x) x*x*x*x // Вычислить длину линии по двум точкам #define LENGTH_LINE(x1, y1, x2, y2) std::sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)) void main() { // 1. Использовать макрос ABC int t = 3; int x = ABC(t); // x = 81 cout << "x = " << x << endl; // 2. Использовать макрос LENGTH_LINE double x1, y1, x2, y2; x1 = 3.8; y1 = 2.7; x2 = -1.4; y2 = 5.5; double len = LENGTH_LINE(x1, y1, x2, y2); cout << "len = " << len << endl; }
⇑
3. Директива #error. Отображение сообщения об ошибке
Использование директивы #error заставляет компилятор приостановить компиляцию. Эта директива используется для отладки программы.
Общая форма использования директивы #error имеет вид
#error error_message
где
- error_message – текст сообщения об ошибке.
Пример.
В примере компиляция программы будет выполняться до директивы #error. Далее компиляция программы остановится и выдаст сообщение об ошибке
#error: Compilation error
Таким образом *.exe-файл создан не будет.
#include <iostream> using namespace std; void main() { // Выполнение вычислений double a = 3, b = 10, c; c = a * b + 5; // Вывести сообщение об ошибке #error Compilation error. }
⇑
4. Директива #include. Включение заголовка или иного исходного файла
С помощью директивы #include можно подключать к исходному тексту текущего файла другие внешние файлы. Текст этих внешних файлов будет использоваться при формировании исполнительного модуля исходного файла.
В свою очередь, включаемые файлы в своем коде также могут содержать директивы #include. Эти директивы называются вложенными. Стандарт языка C++ допускает до 256 уровней вложения.
В наиболее общем случае подключение файла с именем filename.h может производиться одним из двух способов:
#include "filename.h"
или
#include <filename.h>
Наличие кавычек «» или угловых ограничителей <> определяет способ поиска файла для его подключения.
При первом способе («filename.h») поиск файла производится в каталоге с текущей программой (рабочем каталоге). Если файл не найден, используется второй способ поиска файла (с помощью ограничителей <>).
При втором способе (ограничителе <>) поиск файла осуществляется в каталоге, указанном компилятором. Как правило, это каталог со всеми заголовочными файлами под названием INCLUDE.
При подключении файлов собственно разработанные библиотеки файлов включаются в двойные кавычки «», а файлы стандартной библиотеки включаются в ограничители <>.
При проектировании программы, размещаемой в одном файле, хорошей практикой считается разбивка программного кода на два файла:
- файл с расширением *.h. Здесь содержатся объявления (только объявления) функций, которые предоставляются в общий доступ (доступны для клиента);
- файл с расширением *.cpp. Здесь содержатся реализации всех разработанных функций. Сюда могут входить не только функции, предоставленные для общего доступа, но и любые другие дополнительные внутренние функции проекта.
Пример.
// Подключение стандартных библиотек time.h, iostream #include <time.h> #include <iostream> // Подключение пространства имен using namespace std; // Подключение пользовательского файла my_file.h #include "my_file.h" void main() { }
⇑
5. Директива #undef
Директива #undef используется для удаления имени макроса, определенного директивой #define. После использования директивы, имя удаляемого макроса становится неопределенным.
Общая форма директивы #undef следующая
#undef macros_name
здесь
- macros_name – имя макроса, которое нужно удалить (сделать неопределенным).
Пример.
#include <iostream> using namespace std; // Директива #undef // Макрос, умножающий два числа между собой #define Mult2(x, y) (x * y) // Макрос, додающий два числа #define Add2(x, y) (x + y) void main() { // Проверка, существует ли имя Mult2 #ifdef Mult2 double res = Mult2(2.5, 7.8); cout << "2.5 * 7.8 = " << res << endl; #endif // Отменить имя Add2 - директива #undef #undef Add2 // Проверка, существует ли имя Add2 #ifdef Add2 int a = 8, b = 9; int resAdd = Add2(a, b); cout << "a + b = " << resAdd << endl; #else cout << "The Add2 name is undefined." << endl; #endif }
Результат
2.5 * 7.8 = 19.5 The Add2 name is undefined.
⇑
6. Директива #import. Включение сведений из библиотеки типов
С помощью директивы #import можно включать сведения из библиотеки типов (TLB – type library). В данном контексте библиотека типов представляет собой иерархическое хранилище информации о возможностях Active-X сервера, которая хранится как файл с расширением *.tlb или *.olb.
Общая форма объявления директивы #import может быть следующей
#import "filename" [attributes] #import <filename> [attributes]
здесь
- filename – имя файла, содержащее библиотеку типов. Это может быть файл с расширением *.tlb, *.olb, *.dll или *.exe. Это может быть также любой другой формат файла, понятный для функции API LoadTypeLib();
- attributes – атрибуты директивы. Эти атрибуты указывают на то, что компилятор изменяет содержимое заголовка библиотеки типов. Более подробную информацию об атрибутах #import можно найти в документации на сайте learn.microsoft.com.
Пример.
#import "MyTypeLib"
⇑
7. Директива #using. Включение *.dll, *.obj, *.exe, *.net module файлов
Директива #using выполняет импорт метаданных в программу, которая была скомпилирована с параметром /clr. Это означает, что использование директивы возможно только в режиме C++/CLI.
Общая форма использования директивы #using следующая
#using filename [as_friend]
здесь
- filename – имя файла с расширением *.dll, *.exe, *.net module или *.obj.
К примеру, чтобы подключить динамическую библиотеку с именем «MyLibrary.dll» нужно выполнить такой код
#using <MyLibrary.dll>
⇑
8. Директива #pragma. Задание компилятору функций выполнения
Директива #pragma позволяет задавать функции для их выполнения компилятором. Использование тех или иных функций зависит от операционной системы и текущего компьютера, который называют хост-компьютером. Характеристики функций компилятора определяются при установке компилятора C/C++ на компьютер. С помощью директивы #pragma можно предлагать компилятору различные функции для их обработки с обеспечением полной совместимости с языками C или C++.
Директива #pragma имеет множество особенностей использования и несколько реализаций. Общая форма одной из распространенных реализаций следующая
#pragma token_string
здесь
- token_string – так называемая строка токена, которая может быть разных значений. Эта строка представляет собой набор символов, определяющих набор инструкций и их аргументов. К токенам относятся, например, once, alloc_text, component, auto_inline, endregion, loop и т.д.
Подробное рассмотрение особенностей реализации директивы в сочетании с тем или иным токеном не является предметом данной темы.
Пример.
В примере указано указание компилятору включить текущий файл заголовка только один раз в случае, когда будет происходить компиляция файла с исходным кодом.
К примеру, файл заголовка – это файл с именем «MyClass.h», файл с исходным кодом – это файл «MyClass.cpp».
// Файл "MyClass.h" #pragma once
⇑
9. Директива #line. Установить строку на название файла в сообщениях об ошибке
Использование директивы #line дает указание компилятору задать (изменить) номера строки и название файла, которые выводятся в сообщениях об ошибке.
При возникновении ошибки компилятор запоминает номер строки ошибки и название файла соответственно в макросах __LINE__ и __FILE__. После вызова директивы #line значение этих макросов изменяется на заданные в этой директиве.
Директива #line может быть использована в одной из двух возможных общих форм:
#line digit_sequence
или
#line digit_sequence [filename]
где
- digit_sequence – целочисленная константа в диапазоне от 0 до 2147483647 включительно. Эта константа задает номер строки и присваивается макросу __LINE__ после вызова директивы;
- filename – необязательный параметр, являющийся именем файла, который выводится в сообщении об ошибке и записывается в макросе __FILE__. Если filename опущен, то предыдущее имя файла остается без изменений.
Директива #line обычно используется генераторами программ. В этих программах-генераторах на основе выполнения (не выполнения) некоторого утверждения нужно задавать текст сообщения об ошибке и ссылаться на соответствующий исходный файл.
Пример.
#include <iostream> using namespace std; int main() { // Вывести значения макросов __LINE__ и __FILE__ cout << "The value of __LINE__: " << __LINE__ << endl; cout << "The value of __FILE__: " << __FILE__ << endl; // Установить новое значение макроса __LINE__ #line 12 cout << "The value of __LINE__ after changes: " << __LINE__ << endl; cout << "The value of __FILE__ after changes: " << __FILE__ << endl; // Установить новое значение макросов __LINE__ и __FILE__ #line 122 "source2.cpp" cout << "The value of __LINE__ after changes 2: " << __LINE__ << endl; cout << "The value of __FILE__ after changes 2: " << __FILE__ << endl; }
Результат
The value of __LINE__: 7 The value of __FILE__: D:\Programs\C++\Project10\Source.cpp The value of __LINE__ after changes: 12 The value of __FILE__ after changes: D:\Programs\C++\Project10\Source.cpp The value of __LINE__ after changes 2: 122 The value of __FILE__ after changes 2: D:\Programs\C++\Project10\source2.cpp
⇑
Связанные темы
- Макросы. Директива #define. Примеры
- Условная компиляция. Директивы #if, #else, #elif, #endif, #ifdef, #ifndef. Оператор defined
⇑