Директивы препроцессора в Си
Препроцессор — это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.
Основные директивы препроцессора
#include — вставляет текст из указанного файла
#define — задаёт макроопределение (макрос) или символическую константу
#undef — отменяет предыдущее определение
#if — осуществляет условную компиляцию при истинности константного выражения
#ifdef — осуществляет условную компиляцию при определённости символической константы
#ifndef — осуществляет условную компиляцию при неопределённости символической константы
#else — ветка условной компиляции при ложности выражения
#elif — ветка условной компиляции, образуемая слиянием else и if
#endif — конец ветки условной компиляции
#line — препроцессор изменяет номер текущей строки и имя компилируемого файла
#error — выдача диагностического сообщения
#pragma — действие, зависящее от конкретной реализации компилятора.
Директива #include
Директива #include позволяет включать в текст программы указанный файл. Если заголовочный файл содержит описание библиотечных функций и находится в папке компилятора, он заключается в угловые скобки <> .
Если файл находится в текущем каталоге проекта, он указывается в кавычках «» . Для файла, находящегося в другом каталоге необходимо в кавычках указать полный путь.
C++. Препроцессор. Общие сведения. Директивы препроцессора. Обзор
В языке программирования 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 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 color: #008000;"> // 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 color: #333300;">⇑3. Директива #error. Отображение сообщения об ошибке
Использование директивы #error заставляет компилятор приостановить компиляцию. Эта директива используется для отладки программы.
Общая форма использования директивы #error имеет вид
#error error_message
- error_message – текст сообщения об ошибке.
Пример.
В примере компиляция программы будет выполняться до директивы #error . Далее компиляция программы остановится и выдаст сообщение об ошибке
#error: Compilation error
Таким образом *.exe-файл создан не будет.
#include 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» ) поиск файла производится в каталоге с текущей программой (рабочем каталоге). Если файл не найден, используется второй способ поиска файла (с помощью ограничителей <> ).
При втором способе (ограничителе <> ) поиск файла осуществляется в каталоге, указанном компилятором. Как правило, это каталог со всеми заголовочными файлами под названием INCLUDE .
При подключении файлов собственно разработанные библиотеки файлов включаются в двойные кавычки «» , а файлы стандартной библиотеки включаются в ограничители <> .
При проектировании программы, размещаемой в одном файле, хорошей практикой считается разбивка программного кода на два файла:
- файл с расширением *.h . Здесь содержатся объявления (только объявления) функций, которые предоставляются в общий доступ (доступны для клиента);
- файл с расширением *.cpp . Здесь содержатся реализации всех разработанных функций. Сюда могут входить не только функции, предоставленные для общего доступа, но и любые другие дополнительные внутренние функции проекта.
Пример.
// Подключение стандартных библиотек time.h, iostream #include #include // Подключение пространства имен using namespace std; // Подключение пользовательского файла my_file.h #include "my_file.h" void main()
5. Директива #undef
Директива #undef используется для удаления имени макроса, определенного директивой #define . После использования директивы, имя удаляемого макроса становится неопределенным.
Общая форма директивы #undef следующая
#undef macros_name
- macros_name – имя макроса, которое нужно удалить (сделать неопределенным).
Пример.
#include 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 color: #0000ff;">#endif // Отменить имя Add2 - директива #undef #undef Add2 // Проверка, существует ли имя Add2 #ifdef Add2 int a = 8, b = 9; int resAdd = Add2(a, b); cout "a + b color: #0000ff;">#else cout "The Add2 name is undefined." #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 [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
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 using namespace std; int main() < // Вывести значения макросов __LINE__ и __FILE__ cout "The value of __LINE__: " << __LINE__ << endl; cout "The value of __FILE__: " // Установить новое значение макроса __LINE__ #line 12 cout "The value of __LINE__ after changes: " << __LINE__ << endl; cout "The value of __FILE__ after changes: " // Установить новое значение макросов __LINE__ и __FILE__ #line 122 "source2.cpp" cout "The value of __LINE__ after changes 2: " << __LINE__ << endl; cout "The value of __FILE__ after changes 2: "
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
Что такое директива в c
Директива #define определяет идентификатор и последовательность символов, которые будут подставляться вместо идентификатора каждый раз, когда он встретится в исходном файле. Формальное определение директивы:
#define идентификатор последовательность_символов
Используем директиву #define:
#include #define N 23 int main(void) < int x = N; printf("Number: %d", x); // Number: 23 return 0; >
Здесь определен один идентификатор N. В программе, где встречается этот идентификатор, он будет заменяться на число 23. Например, строка
int x = N;
после обработки препроцессором будет иметь следующий код
int x =23;
Более сложный пример
#include #define BEGIN < #define END >#define N 23 int main(void) BEGIN int x = N; printf("Number: %d", x); // Number: 23 return 0; END
Здесь определены три идентификатора BEGIN, END, N. В итоге все вхождения последовательности символов "BEGIN" будут заменяться на открывающую фигурную скобку, а "END" - на закрывающую, а символ "N" на число 23.
Таким образом, после обработки препроцессора функция main приобретет следующий вид:
int main(void)
#define также может определять более сложные выражения. Например:
#include #define ADD(a,b) (a+b) int main(void) < int n1 = 10; int n2 = 5; printf("%d + %d = %d", n1, n2, ADD(n1, n2)); // 10 + 5 = 15 >
В данном случае выражение ADD(a,b) будет заменяться операцией сложения двух чисел (a + b)
Особенно удобно использовать директиву #define для определения размеров массивов:
#include #define N 4 int main(void) < int numbers[N] = ; for(int i=0; i return 0; >
В данном случае если мы захотим глобально поменять размер массива, то достаточно изменить значение N в директиве define.
Следует учитывать, что директива препроцессор не заменяет последовательности символов в двойных и одинарных кавычках и в комментариях:
#include #define N 4 int main(void) < char symbol = 'N'; printf("%c \n", symbol); // N printf("N"); //N return 0; >
Причем если идентификатор должен представлять одно слово, то его последовательность символов может состоять из нескольких слов или символов, разделенных пробелами:
#define REAL long double
Директива #undef
В процессе работы мы можем многократно определять новое значение для одного идентификатора:
#define N 23 #define N 32 #define N 55
Но некоторые компиляторы, в частности, gcc, могут выдавать предупреждения при повторном определении идентификатора, и чтобы выйти из этой ситуации, мы можем использовать директиву #undef для отмены действия макроса. Эта директива имеет следующее определение:
#undef идентификатор
#include #define STRING "Good morning \n" int main(void)
Директива (программирование)
В программировании термин «директива» (указание) по использованию похож на термин «команда», так как также используется для описания некоторых конструкций языка программирования (то есть указаний компилятору или ассемблеру особенностей обработки при компиляции).
Язык ассемблера
В языке ассемблера директивы указывают общую информацию, такую как целевая среда, указание границ между секциями и так далее. Например, директива «ALIGN», которая вставляет в текущую секцию необходимое количество байт для выравнивания строк, часто упоминаемая как «директива», противоречит тому факту, что она совпадает с частями конструкций в генерируемом коде.
Препроцессор Си
В языки программирования Си и C++ встроена поддержка препроцессора. Строки в исходном коде, которые должны быть обработаны препроцессором в виде #define и #include , называются препроцессорными директивами.
Синтаксические конструкции, похожие на препроцессорные директивы языка Си, такие как #region в языке C#, также часто называются «директивами», хотя в указанных случаях стадии обработки препроцессором может и не быть.
В других языках высокого уровня
В языке Ada директивы компилятора называются прагмами (сокращение от «pragmatic information» («полезная информация»)).
В Паскале директивы называются указательными комментариями из-за того, что по синтаксису похожи на комментарии. В Паскале указательный комментарий — это комментарий, у которого первым символом указан знак доллара; например, аналогом директивы #include "file" языка Си будет указательный комментарий .
В Perl ключевое слово « use » («применять») можно использовать как «прагму», например use strict; или use utf8; . Такая конструкция иногда может упоминаться как «директива». Тем не менее, в некоторых источниках[1] используется термин «директивы Perl» для обозначения конструкций типа hashbang.
Дополнительные материалы
- пример использования директив в языке Ассемблера
- описание использования директив в языке Фортран
- описание использования директив в языке C--
- Язык программирования Си
- Концепции языков программирования
- Препроцессоры
Wikimedia Foundation . 2010 .