C # — директивы препроцессора
Содержание:
RemarksRemarks
, как и директивы #else, #elif, #endif, #define и #undef, позволяет включить или исключить код в зависимости от существования одного или нескольких символов., along with the #else, #elif, #endif, #define, and #undef directives, lets you include or exclude code based on the existence of one or more symbols. Это может быть полезно при компиляции кода для отладки или для определенной конфигурации.This can be useful when compiling code for a debug build or when compiling for a specific configuration.
Условные директивы, начинающиеся с директивы , должны явным образом завершаться директивой .A conditional directive beginning with a directive must explicitly be terminated with a directive.
позволяет определить символ, lets you define a symbol. чтобы директива , которой передается этот символ, возвращала значение .By then using the symbol as the expression passed to the directive, the expression evaluates to .
Также символ можно определить с помощью параметра компилятора -define.You can also define a symbol with the -define compiler option. Для отмены определения символа служит директива #undef.You can undefine a symbol with #undef.
Символ, определенный с помощью или , не конфликтует с одноименной переменной.A symbol that you define with or with doesn’t conflict with a variable of the same name. Соответственно, имя переменной не должно передаваться директиве препроцессора, а символ может использоваться только в директиве препроцессора.That is, a variable name should not be passed to a preprocessor directive, and a symbol can only be evaluated by a preprocessor directive.
Символ, создаваемый с помощью , будет определен в пределах того файл, в котором он определен.The scope of a symbol created with is the file in which it was defined.
Система сборки также учитывает символы препроцессора, представляющие целевые платформы в проектах в стиле SDK.The build system is also aware of predefined preprocessor symbols representing different target frameworks in SDK-style projects. Они полезны при создании приложений, предназначенных для нескольких реализаций или версий .NET.They’re useful when creating applications that can target more than one .NET implementation or version.
| Требуемые версии .NET FrameworkTarget Frameworks | СимволыSymbols |
|---|---|
| .NET Framework.NET Framework | , , , , , , , , , , , , , , , , , , , , , , , , , , |
| .NET Standard.NET Standard | , , , , , , , , , , , , , , , , , , |
| .NET Core.NET Core | , , , , , , , , , , , , , , |
Примечание
Для традиционных проектов .NET Framework необходимо вручную настроить символы условной компиляции для различных целевых платформ в Visual Studio с помощью страниц свойств проекта.For traditional .NET Framework projects, you have to manually configure the conditional compilation symbols for the different target frameworks in Visual Studio via the project’s properties pages.
Другие предопределенные символы включают константы DEBUG и TRACE.Other predefined symbols include the DEBUG and TRACE constants. Вы можете переопределить значения для проектов с помощью .You can override the values set for the project using . Например, символ DEBUG автоматически устанавливается в зависимости от свойств конфигурации сборки (в режиме отладки или выпуска).The DEBUG symbol, for example, is automatically set depending on your build configuration properties («Debug» or «Release» mode).
Директивы препроцессораPreprocessor Directives
Директива препроцессора дополняется префиксом с символом «#» и отображается в строке сама по себе.A preprocessor directive is prefixed with the # symbol and appears on a line by itself. Она интерпретируется препроцессором, который запускается перед самим компилятором.It is interpreted by the preprocessor, which runs before the compiler itself.
В следующей таблице перечислены директивы препроцессора, имеющиеся в F#.The following table lists the preprocessor directives that are available in F#.
| ДирективаDirective | ОписаниеDescription |
|---|---|
| символ symbol | Поддерживает условную компиляцию.Supports conditional compilation. Код в разделе после включается, если символ определен.Code in the section after the is included if the symbol is defined. Символ также может быть инвертирован с помощью .The symbol can also be negated with . |
| Поддерживает условную компиляцию.Supports conditional compilation. Помечает раздел кода, который следует включить, если символ, используемый с предыдущей директивой , не определен.Marks a section of code to include if the symbol used with the previous is not defined. | |
| Поддерживает условную компиляцию.Supports conditional compilation. Помечает конец условного раздела кода.Marks the end of a conditional section of code. | |
| штрих int, int,штрих int строкаint, int string,штрих буквальная строка целого числа int verbatim-string | Указывает исходную строку кода и имя файла для отладки.Indicates the original source code line and file name, for debugging. Эта возможность предназначена для средств, создающих исходный код F#.This feature is provided for tools that generate F# source code. |
| варнингкоде warningcode | Отключает предупреждение или предупреждения компилятора.Disables a compiler warning or warnings. Чтобы отключить предупреждение, найдите его номер в выходных данных компилятора и заключите его в кавычки.To disable a warning, find its number from the compiler output and include it in quotation marks. Не указывайте префикс «FS».Omit the «FS» prefix. Чтобы отключить несколько номеров предупреждений в одной строке, заключите каждый номер в кавычки и отделяйте каждую строку пробелом.To disable multiple warning numbers on the same line, include each number in quotation marks, and separate each string by a space. Пример:For example: |
Результат отключения предупреждения применяется ко всему файлу, включая фрагменты файла, предшествующие директиве. |The effect of disabling a warning applies to the entire file, including portions of the file that precede the directive.|
8.3 Включаемые файлы
Директива #include информирует предпроцессор о том, что
содержание файла с заданным именем следует обрабатывать так, как
будто оно присутствует в исходной программе в месте расположения
этой директивы. Новый текст также может содержать директивы
предпроцессора. Предпроцессор выполняет директивы в новом тексте,
а затем продолжает обработку текста исходного файла.
«Спецификация пути» это имя файла, которому может
предшествовать директория. Это должно быть имя существующего
файла. Синтаксис спецификации файла зависит от операционной
системы, в которой компилируется программа.
При поиске файлов предпроцессор использует концепцию
«стандартной» директории. Расположение стандартных директорий для
файлов зависит от реализации и операционной системы. Определение
стандартной директории можно найти в Вашем Руководстве по
компилятору.
Предпроцессор останавливает поиск сразу же после обнаружения
файла с заданным именем. Если задать полную спецификацию файла,
заключенную в двойные цитатные скобки (» «), то предпроцессор
использует ее для поиска и игнорирует стандартную директорию.
Если заключенная в двойные цитатные скобки спецификация
файла является неполной, то предпроцессор сначала ищет директорию
«родительского» файла. Родительский файл это файл, содержащий
директиву #include. Например, если файл fil2 вставляется в файл
fil1, то fil1 будет родительским файлом.
Вставка файлов может быть вложенной. Т.е. директива #include
может появляться в файле, который сам вставляется директивой
#include. Например, в приведенном выше примере файл fil2 может
вызывать файл fil3. В этом случае fil1 все еще будет родительским
для fil2, но «дедушкой» для fil3.
При вложенной вставке файлов поиск директории начинается с
родительского файла, затем проходит по дедушкиным файлам.
Следовательно, поиск начинается в директории, которая содержит
обрабатываемый исходный файл. Если файл не найден, то поиск
продолжается в директориях, заданных в командной строке
компилятора. И, наконец, производится поиск в стандартной
директории.
Если спецификация файла заключена в угловые скобки, то
предпроцессор не проводит поиска в текущем рабочем каталоге.
Поиск файла начинается в директориях, заданный в командной строке
компилятора, а затем в стандартной директории.
Допускается вложение вставки файлов до 10 уровней. При
обработке вложенных #include предпроцессор всегда будет
осуществлять вставку в первоначальный исходный файл.
Пример 1
В данном примере содержимое файла с именем stdio.h будет
вставлено в исходную программу. Угловые скобки заставляют
предпроцессор искать stdio.h в стандартной директории, после
поиска в директориях, указанных в командной строке.
Пример 2
Данный пример добавит в исходный файл содержимое файла
defs.h. Двойные цитатные скобки означают, что предпроцессор
начнет поиск в родительском каталоге.
Директива #define
Символические константы
С директивой препроцессора мы также уже знакомы. С ее помощью объявляются и определяются так называемые символические константы. Например:
#define N 100 #define HELLO "Hello. Answer the next questions, please."
Когда перед компиляцией исходный код будет обработан препроцессором, то все символьные константы (в примере это N и HELLO) в тексте исходного кода на языке C будут заменены на соответствующие им числовые или строковые константы.
Символические константы можно определять в любом месте исходного кода. Однако чтобы переопределить их (изменить значение), необходимо отменить предыдущее определение. Иначе возникнет если не ошибка, то предупреждение. Для удаления символической константы используют директиву :
#include <stdio.h>
#define HELLO "Hello. Answer the next questions, please.\n"
int main () {
printf(HELLO);
#undef HELLO
#define HELLO "Good day. Tell us about you.\n"
printf(HELLO);
}
Если в этом примере убрать строку , то при компиляции в GNU/Linux появляется предупреждение: «HELLO» переопределён.
Символические константы принято писать заглавными буквами. Это только соглашение для удобства чтения кода.
Макросы как усложненные символьные константы
С помощью директивы можно заменять символьными константами не только числовые и строковые константы, но почти любую часть кода:
#include <stdio.h>
#define N 100
#define PN printf("\n")
#define SUM for(i=0; i<N; i++) sum += i
int main () {
int i, sum = ;
SUM;
printf("%d", sum);
PN;
}
Здесь в теле функции PN заменяется препроцессором на , а SUM на цикл . Такие макроопределения (макросы) в первую очередь удобны, когда в программе часто встречается один и тот же код, но выносить его в отдельную функцию нет смысла.
В примере выше PN и SUM являются макросами без аргументов. Однако препроцессор языка программирования C позволяет определять макросы с аргументами:
#include <stdio.h>
#define DIF(a,b) (a) > (b) ? (a)-(b) : (b)-(a)
int main () {
int x = 10, y = 30;
printf("%d\n", DIF(67,90));
printf("%d\n", DIF(876-x,90+y));
}
Вызов макроса в тексте программы приводит к тому, что при обработке программы препроцессором туда подставляется такое выражение . В этом выражении вычисляется разница между двумя числами с помощью условного выражения (см. урок 3). В данном случае скобки не нужны. Однако при таком разворачивании скобки подчеркивают порядок операций
Если бы вместо сложения и вычитания фигурировали операции умножения или деления, то наличие скобок было бы принципиальным.
Обратите внимание, что в имени макроса не должно быть пробелов: . Первый пробел после идентификатора означает конец символической константы и начало выражения для подстановки
Задание
- Напишите программу, содержащую пару макросов: один вычисляет сумму элементов массива, другой выводит элементы массива на экран.
- Напишите программу, содержащую макросы с аргументами, вычисляющие площади различных геометрических фигур (например, квадрата, прямоугольника, окружности).
Основные функции препроцессора
Препроцессором выполняются следующие действия:
- замена соответствующих диграфов и триграфов на эквивалентные символы «» и «»;
- удаление экранированных символов перевода строки;
- замена строчных и блочных комментариев пустыми строками (с удалением окружающих пробелов и символов табуляции);
- вставка (включение) содержимого произвольного файла ();
- макроподстановки ();
- условная компиляция (, , , , );
- вывод сообщений (, ).
Условная компиляция позволяет выбрать код для компиляции в зависимости от:
- модели процессора (платформы);
- разрядности адресов;
- размерности типов;
- наличия/отсутствия поддержки расширений языка;
- наличия/отсутствия библиотек и/или функций;
- особенностей поведения конкретных функций;
- и другого.
Этапы работы препроцессора:
- лексический анализ кода C/C++ (синтаксический анализ не выполняется);
- обработка директив;
- выполнение подстановок:
- диграфов и триграфов;
- комментариев;
- директив;
- лексем, заданных директивами.
Язык препроцессора C/C++ не является полным по Тьюрингу хотя бы потому, что с помощью директив невозможно заставить препроцессор зависнуть. См. рекурсивная функция (теория вычислимости).
Примечания Remarks
Можно использовать директивы #ifdef и #ifndef в любом месте #if . You can use the #ifdef and #ifndef directives anywhere #if can be used. Оператор #ifdef identifier эквивалентен #if 1 , если определен идентификатор . The #ifdef identifier statement is equivalent to #if 1 when identifier has been defined. Это эквивалентно #if 0 тому #undef , что идентификатор не был определен или был неопределен директивой. It’s equivalent to #if 0 when identifier hasn’t been defined, or has been undefined by the #undef directive. Эти директивы проверяют наличие или отсутствие только идентификаторов, определенных с директивой #define , а не идентификаторов, объявленных в исходном коде C или C++. These directives check only for the presence or absence of identifiers defined with #define , not for identifiers declared in the C or C++ source code.
Эти директивы предназначены только для совместимости с предыдущими версиями языка. These directives are provided only for compatibility with previous versions of the language. #if Предпочтительным является заданное константное выражение ( идентификатор ) , используемое с директивой. The defined( identifier ) constant expression used with the #if directive is preferred.
Директива #ifndef проверяет наличие противоположного условия, установленного #ifdef. The #ifndef directive checks for the opposite of the condition checked by #ifdef. Если идентификатор не был определен или его определение было удалено с помощью, условие имеет #undef значение true (отличное от нуля). If the identifier has not been defined, or if its definition has been removed with #undef , the condition is true (nonzero). В противном случае условие не выполняется (false, значение равно 0). Otherwise, the condition is false (0).
Блок, относящийся только к системам Microsoft Microsoft Specific
Идентификатор можно передать из командной строки с помощью параметра /d . The identifier can be passed from the command line using the /D option. С /D можно указать до 30 макросов. Up to 30 macros can be specified with /D .
Директива #ifdef полезна для проверки существования определения, так как определение может быть передано из командной строки. The #ifdef directive is useful for checking whether a definition exists, because a definition can be passed from the command line. Например: For example:
Завершение блока, относящегося только к системам Майкрософт END Microsoft Specific
Препроцессор С/С++ (англ. pre processor , предобработчик) — программа, подготавливающая код программы на языке C/C++ к компиляции.
Основные функции препроцессора [ править | править код ]
Препроцессором выполняются следующие действия:
- замена соответствующих диграфов и триграфов на эквивалентные символы « # » и « »;
- удаление экранированных символов перевода строки;
- замена строчных и блочных комментариев пустыми строками (с удалением окружающих пробелов и символов табуляции);
- вставка (включение) содержимого произвольного файла ( #include );
- макроподстановки ( #define );
- условная компиляция ( #if , #ifdef , #elif , #else , #endif );
- вывод сообщений ( #warning , #error ).
Условная компиляция позволяет выбрать код для компиляции в зависимости от:
- модели процессора (платформы);
- разрядности адресов;
- размерности типов;
- наличия/отсутствия поддержки расширений языка;
- наличия/отсутствия библиотек и/или функций;
- особенностей поведения конкретных функций;
- и другого.
Этапы работы препроцессора:
- лексический анализ кода C/C++ (синтаксический анализ не выполняется);
- обработка директив;
- выполнение подстановок:
- диграфов и триграфов;
- комментариев;
- директив;
- лексем, заданных директивами.
Язык препроцессора C/C++ не является полным по Тьюрингу хотя бы потому, что с помощью директив невозможно заставить препроцессор зависнуть. См. рекурсивная функция (теория вычислимости).
8.2.1 The #if Directive
The directive has the following syntax:
#if constant-expression newline
This directive checks whether the constant-expression is
true (nonzero). The operand must be a constant integer expression
that does not contain any increment (++), decrement (- -),
, pointer (*), address (&), and cast operators.
Identifiers in the constant expression either are or are not macro
names. There are no keywords, enumeration constants, and so on.
The constant expression can also include the
preprocessing operator (see ).
The constant expression in an directive is subject
to text replacement and can contain references to identifiers
defined in previous directives. The replacement
occurs before the expression is evaluated. Each preprocessing token
that remains after all macro replacements have occurred is in the
lexical form of a token.
If an identifier used in the expression is not currently defined,
the compiler treats the identifier as though it were the constant
zero.
Макросы-объекты с текст_замена
Когда препроцессор встречает макросы-объекты с , то любое дальнейшее появление заменяется на . обычно пишется заглавными буквами с символами подчеркивания вместо пробелов.
Рассмотрим следующий фрагмент кода:
#define MY_FAVORITE_NUMBER 9
std::cout << «My favorite number is: » << MY_FAVORITE_NUMBER << std::endl;
|
1 |
#define MY_FAVORITE_NUMBER 9 std::cout<<«My favorite number is: «<<MY_FAVORITE_NUMBER<<std::endl; |
Препроцессор преобразует код выше в:
std::cout << «My favorite number is: » << 9 << std::endl;
| 1 | std::cout<<«My favorite number is: «<<9<<std::endl; |
Результат выполнения:
Мы обсудим это детально, и почему так не стоит делать, — на следующих уроках.
Приватные переменные
В контексте инкапсуляции, все данные в Си могут быть рассмотрены как публичные по умолчанию. Уровень доступа к переменным в структурах () может быть изменен на приватный если изолировать их определение от основной программы. Нужный эффект может быть достигнут при использовании отдельных заголовочных (header, .h) и исходных (source, .c) файлов.
В данном примере, структура была определена в отдельном исходном файле “private_var.c”. Поскольку инициализация структуры в Си требует выделения и освобождения памяти, несколько вспомогательных функций были добавлены.
В соответствующем заголовочном файле «private_var.h», структура была объявлена, но ее содержание осталось скрытым для основной программы.
Таким образом, для “main.c” содержание структуры неизвестно и попытки прочитать или изменить приватные данные вызовут ошибку при компиляции.
ДРУГИЕ ДИРЕКТИВЫ: #undef, #if, #ifdef, #ifndef, #else И #endif
Скрыть рекламу в статье
ДРУГИЕ ДИРЕКТИВЫ: #undef, #if, #ifdef, #ifndef, #else И #endif
Эти директивы обычно используются при программировании больших модулей. Они позволяют приостановить действие более ранних определений и создать файлы, каждый из которых можно компилировать по-разному.
Директива #undef отменяет самое последнее определение поименованного макроопределения.
#define BIG 3
#define HUGE 5
#undef BIG /* BIG теперь не определен */
#define HUGE 10 /* HUGE переопределен как 10 */
#undef HUGE /* HUGE снова равен 5*/
#undef HUGE /* HUGE теперь не определен */
Очевидно (мы надеемся), вы не будете компилировать файл, как в этом примере. Предположим, что у вас есть большой стандартный файл, определенный директивой #include, который вы хотели бы использовать, но для этого некоторые из его определений для одной из функций программы нужно будет временно изменить, Вместо того чтобы иметь дело с этим файлом, вы можете просто включить его, а затем окружить такую выделяющуюся функцию соответствующими директивами #define и #undef.
Или, предположим, что вы работаете с большой системой программ. Вы хотите задать макроопределение, но не уверены, использует ли ваше определение другие определения откуда-нибудь из другого места системы. В этом случае просто отмените ваше макроопределение в том месте, где оно вам больше не нужно, а оригинал этого макроопределения, если он есть, будет по-прежнему оставаться в силе для остальной части системы.
Другие упомянутые нами директивы позволяют выполнять условную компиляцию. Вот пример:
#ifdef MAVIS
#include » horse.h» /* выполнится, если MAVIS определен */
#define STABLES 5
#else
#include «cow.h» /*выполнится, если MAVIS не определен */
#define STABLES 15
#endif
Директива #ifdef сообщает, что если последующий идентификатор (MAVIS) определяется препроцессором, то выполняются все последующие директивы вплоть до первого появления #else или #endif. Когда в программе есть #else, то программа от #else до #endif будет выполняться, если идентификатор не определен.
Такая структура очень напоминает конструкцию if-else языка Си. Основная разница заключается в том, что препроцессор не распознает фигурные скобки {}, отмечающие блок, потому что он использует директивы #else (если есть) и #endif (которая должна быть) для пометки блоков директив.
Эти условные конструкции могут быть вложенными.
Директивы #ifdef и #if можно использовать с #else и #endif таким же образом. Директива #ifndef опрашивает, является ли последующий идентификатор неопределенным; эта директива противоположна #ifdef. Директива #if больше похожа на обычный оператор if языка Си. За ней следует константное выражение, которое считается истинным, если оно не равно нулю:
#if SYS == «IBM»
#include «ibm.h»
#endif
Одна из целей использования «условной компиляции» — сделать программу более мобильной. Изменяя несколько ключевых определений в начале файла, вы можете устанавливать различные значения и включать различные файлы для разных систем.
Эти краткие примеры иллюстрируют удивительную способность языка Си изощренно и строго управлять программами.
Оглавление книги
8.2.7 The defined Operator
Another way to verify that a macro is defined is to use the
unary operator. The
operator has one of the following forms:
defined name
defined (name)
An expression of this form evaluates to 1 if name is
defined and to 0 if it is not.
The operator is especially useful for checking
many macros with just a single use of the
directive. In this way, you can check for macro definitions in
one concise line without having to use many
or directives.
For example, consider the following macro checks:
#ifdef macro1 printf( "Hello!\n" ); #endif #ifndef macro2 printf( "Hello!\n" ); #endif #ifdef macro3 printf( "Hello!\n" ); #endif
Another use of the operator is in a single
directive to perform similar macro checks:
#if defined (macro1) || !defined (macro2) || defined (macro3) printf( "Hello!\n" ); #endif
Note that operators can be combined in
any logical expression using the C logical operators. However,
can only be used in the evaluated expression
of an or preprocessor directive.
Previous PageNext Page
8.5 Управление Line
Если сменить номер строки и имя файла, то компилятор
игнорирует предыдущие значения и продолжит обработку с новыми
значениями. Директива #line обычно используется генераторами
программ для появления сообщений об ошибках в исходном тексте, а
не в генерируемой программе.
Константа в директиве #line может быть любой целой
константой. Имя файла может быть любой комбинацией символов,
заключенной в двойные цитатные скобки. Если имя файла не задано,
то предыдущее имя файла остается без изменения.
Всегда имеется доступ к текущему номеру строки и имени файла
через заранее определенные идентификаторы __LINE__ и __FILE__.
Эти идентификаторы можно использовать в самодокументирующихся
сообщения об ошибках, помещенных в текст программы.
Идентификатор __FILE__ раскрывается в строку, содержимое
которой есть имя файла, заключенное в двойные цитатные скобки.
Пример 1
В данном примере хранимый номер строки меняется на 151, а
имя файла меняется на copy.c .
Пример 2
В данном примере макро ASSERT использует ранее определенные
идентификаторы __LINE__ и __FILE__ для печати сообщения об ошибке
в исходном файле, если значение условия «ложь».
Синтаксис директив
Директивой (командной строкой) препроцессора называется строка в исходном коде, имеющая следующий формат: :
- символ ;
- ноль или более символов пробелов и/или табуляции;
- одно из предопределённых ключевых слов;
- параметры, зависимые от ключевого слова.
Список ключевых слов:
- — создание константы или макроса;
- — удаление константы или макроса;
- — вставка содержимого указанного файла;
- — проверка истинности выражения;
- — проверка существования константы или макроса;
- — проверка несуществования константы или макроса;
- — ветка условной компиляции при ложности выражения ;
- — проверка истинности другого выражения; краткая форма записи для комбинации и ;
- — конец ветки условной компиляции;
- — указание имени файла и номера текущей строки для компилятора;
- — вывод сообщения и остановка компиляции;
- — вывод сообщения без остановки компиляции;
- — указание действия, зависящего от реализации, для препроцессора или компилятора;
- если ключевое слово не указано, директива игнорируется;
- если указано несуществующее ключевое слово, выводится сообщение об ошибке и компиляция прерывается. (В некоторых компиляторах, таких как g++, компиляция продолжается, просто показывая предупреждение.)