Директивы #if, #elsif, #else и #endif
Директивы #if, #elsif, #else и #endif используются для условного включения и исключения фрагментов исходного кода в текст программы.
Директива #if служит для вставки в текст программы фрагмента исходного кода в зависимости от истинностного значения выражения. Значение выражения считается истинным, если оно не равно нулю. Завершение фрагмента исходного кода отмечается директивой #endif. Синтаксис директив #if и #endif имеет следующий вид:
#if <выражение>
<исходный_код>
#endif
Здесь выражение должно быть целочисленным константным выражением, а исход-ный_код — это исходный код на языке программирования С. Этот код вставляется в текст программы, если значение выражения истинно. В противном случае текст про-граммы не изменяется.
Директива #else используется только с директивой #if и служит для вставки в текст программы фрагмента исходного кода, если истинностное значение выражения в директиве #if ложно. Комбинация директив #if и #else имеет следующий вид:
#if <выражение>
<исходный_код_1>
#else
<исходный_код_2>
#endif
Если значение выражения ложно, то в текст программы вставляется исходный_код_2. Например:
#define VERSION 2
#if VERSION > 2
printf("Version = %d\n", 3);
#else
printf("Version = %d\n", 2); // Version = 2
#endif
Директива #elif служит для вставки в текст программы фрагмента исходного кода в за-висимости от истинностного значения выражения, альтернативного выражению директивы #if. Комбинация директив #if и #elif имеет следующий вид:
#if выражение_1
исходный_код_1
#elif выражение_2
исходный_код_2
...
#elif выражение_n
исходный_код_n
#endif
В этом случае в текст программы вставляется тот фрагмент кода, который соответству-ет первому истинному выражению. При этом значения выражений вычисляются последовательно.
Совместно с директивами #if и #elif возможно использование директивы #else. При этом нужно учитывать, что директива #else всегда должна быть одна и находиться перед директивой #endif.
Так как вставка фрагмента кода в исходный текст программы вызывает компиляцию этого кода, то рассмотренные в этом разделе директивы часто называют директивами условной компиляции.
Директивы #ifdef и #ifndef
Директива #ifdef служит для включения фрагмента исходного кода в текст программы при условии, что определено некоторое символическое имя. Например:
#define DEBUG
#ifdef DEBUG
printf("DEBUG is defined.\n"); /* DEBUG is defined */
#else
printf("DEBUG is not defined.\n");
#endif
Директива #ifndef служит для условного включения фрагмента исходного кода в текст программы при условии, что не определено некоторое символическое имя. Эта директива имеет следующий синтаксис:
В заключение этого раздела отметим, что директива #ifdef может заменяться директивой #if defined, а директива #ifndef — директивой #if !defined.
Директива #include
Директива #include предписывает препроцессору поместить в программу текст из файла на место этой директивы. Порядок поиска файла предопределен в компиляторе. Эта директива имеет один из следующих вариантов использования:
#include "имя_файла"
#include <имя_файла>
#include символическое_имя
Здесь имя_файла должно быть символьной строкой, определяющей путь к файлу.
Обычно в первом случае файл с заданным именем ищется сначала в текущем каталоге, а затем в каталогах, заданных в среде разработки.
Во втором случае файл с заданным именем обычно ищется только в каталогах, заданных в среде разработки. Если в кавычках задан полный путь к файлу, то препроцессор ищет файл только по этому пути, игнорируя стандартные каталоги.
В третьем случае препроцессор подставляет в текст программы значение символической переменной, которое должно представлять символьную строку с именем файла, заключенном в кавычки или угловые скобки.
Отметим, что директива #include обрабатывается препроцессором рекурсивно. То есть, если подставленный фрагмент текста содержит другую директиву #include, то она также обрабатывается препроцессором.
Сложные программы могут включать несколько заголовочных файлов. Поэтому возможно многократное включение в текст программы содержимого одного и того же заголовочного файла, что может привести к ошибкам компиляции. Чтобы предотвратить такую ситуацию, обычно в заголовочный файл ставят "часового". Например, заголовочный файл может начинаться с такого текста:
#ifтdef MY_NAME
#define MY_NAME
/* здесь содержимое заголовочного файла */
#endif /* MY_NAME */
В этом случае содержимое заголовочного файла будет вставляться в текст программы только в том случае, если в ней не определено символическое имя MY_NAME.
Предопределенные макрокоманды
Следующие макрокоманды предопределены в препроцессоре и не могут быть изменены программистом:
__DATE__ — строка с датой;
__TIME__ — строка со временем;
__FILE__ — строка с именем исходного файла;
__LINE__ — номер текущей строки;
__STDC__ — константа, которая равна 1, если компилятор поддерживает стандартный язык C, в противном случае — равна 0;
__ STDC_VERSION__ — константа, которая определена, если компилятор поддерживает стандартный язык C99;
Директивы #error
Директива #error предписывает препроцессору прекратить обработку текста программы и выдать сообщение об ошибке. Эта директива имеет следующий синтаксис:
#error <сообщение>
Здесь сообщение представляет собой произвольный текст. Например:
#ifndef VERSION
#error Version is not defined
#endif
Директива #line
Директива #line предписывает препроцессору изменить номер текущей строки и имя компилируемого файла. Эта директива имеет следующий синтаксис:
#line <номер_строки> <имя_файла>
где номер_строки — константа, которая устанавливает новый номер для текущей строки в обрабатываемом исходном файле, а имя_файла задает новое имя файла для компилируемого файла.
Директива #pragma
Директива #pragma предписывает компилятору выполнить некоторое действие. Можно также сказать, что эта директива предназначена для определения других директив препроцессора, которые зависят от реализации и версии компилятора. Директива #pragma имеет следующий синтаксис:
#pragma <директива>
Здесь директива представляет собой предопределенную инструкцию для компилятора, которая является произвольным текстом.
Например, следующие директивы соответственно выключают и включают оптимизацию кода в компиляторе Visual С++ фирмы Microsoft:
#pragma optimize("", off)
#pragma optimize("", on)
Пустая директива
Если строка в программе содержит единственный символ #, то это не вызовет никаких действий препроцессора. В этом случае за символом # следует пустая директива. Например:
#ifdef MAX
#
#else
#define MAX 255
#endif
Оператор #
Оператор # используется в макрокомандах с одним параметром, для преобразования этого параметра в символьную строку языка программирования С. Такая макрокоманда имеет следующий вид:
#define <имя>(<параметр>) <шаблон>
Здесь шаблон — это последовательность лексем, содержащая параметр, которому предшествует символ #. При обработке макрокоманды пробелы, следующие до и после шаблона, удаляются. А при преобразовании шаблона в строку несколько пробелов между лексемами заменяются одним пробелом. Кроме того, при использовании оператора # нужно учитывать следующие ограничения:
- порядок вычисления нескольких операторов # и ## в шаблоне не определен;
- если шаблон содержит лексемы, которые являются строками языка программирования C, и эти лексемы следуют после параметра, то они соединяются с ним как строки.
Например, определим макрокоманду:
#define print_str(x) printf("%s", #x "\n")
Используя эту макрокоманду, вывести на печать строку можно следующим образом:
print_str(This is a string);
Оператор # используется для упрощения вывода на печать строк в формате языка программирования C. Например, при помощи макрокоманды print_str можно вывести на печать следующую строку:
print_str("\This is a string/");
