Макроопределения
Макроопределения, называемые в просторечии макросами, определяются директивой препроцессора #define. Можно выделить три формы макросов #define: простое определение символа, определение символической константы и определение макроса с параметрами.
Простое определение выглядит так:
#define NDEBUG
После такой директивы символ NDEBUG считается определенным. Не предполагается, что он что-то означает; он просто — определен (как пустой). Можно было бы написать:
#define NDEBUG 1
Тогда NDEBUG можно было бы использовать и в качестве символической константы, о которых говорилось в предыдущей главе. Всякое вхождение в текст лексемы NDEBUG препроцессор заменил бы на “I”. Зачем нужны макроопределения, которые ничего не определяют, выяснится при обсуждении условных конструкций препроцессора.
Как вы могли бы догадаться, #define может определять не только константы. Поскольку препроцессор выполняет просто текстовую подстановку, можно сопоставить символу и любую последовательность операторов, как показано ниже:
#define SHUTDOWN \
printf("Error!"); \ return -1
…
if (ErrorCondition()) SHUTDOWN; // "Вызов" макроса.
Обратная дробная черта (\) означает, что макрос продолжается на следующей строчке. В отличие от операторов С директивы препроцессора должны располагаться в одной строке, и поскольку это технически не всегда возможно, приходится явно вводить некоторый признак продолжения.
Определенный ранее макрос можно аннулировать директивой #undef:
#undef NDEBUG
После этого макрос становится неопределенным, и последующие ссылки на него будут приводить к ошибке при компиляции.
Предопределенные макросы
Компилятор C++Builder автоматически определяет некоторые макросы. Их можно разбить на две категории: макросы ANSI и макросы, специфические для C++Builder. Сводки предопределенных макросов даны соответственно в таблицах 4.1 и 4.2.
Таблица 4.1. Предопределенные макросы ANSI
Макрос |
Описание |
||
DATE | Литеральная строка в формате “mmm dd yyyy”, представляющая дату обработки данного файла препроцессором. | ||
FILE | Строка имени текущего файла (в кавычках). | ||
LIME | Целое, представляющее номер строки текущего файла. | ||
STDC | Равно 1, если установлена совместимость компилятора со стандартом ANSI (ключ -А командной строки). В противном случае макрос не определен. | ||
TIME | Строка в формате “hh:mm:ss”, представляющее время препроцессорной обработки файла. |
Значения макросов _file_ и _line_ могут быть изменены директивой #line (см. далее).
Таблица 4.2. Предопределенные макросы C++Builder
Макрос |
Значение |
Описание |
ВСОРТ | 1 | Определен в любом оптимизирующем компиляторе. |
BCPLUSPLUS | 0х0540 | Определен, если компиляция производится в режиме C++. В последующих версиях будет увеличиваться. |
BORLANDC | 0х0540 | Номер версии. |
CDECL | 1 | Определен, если установлено соглашение о вызове cdecl; в противном случае не определен. |
CHARUNSIGNED | 1 | Определен по умолчанию (показывает, что char по умолчанию есть unsigned char). Можно аннулировать ключом -К. |
CONSOLE | Определен при компиляции консольных приложений. | |
CPPUNWIND | 1 | Разрешение разматывания стека; определен по умолчанию. Для аннулирования можно применить ключ -xd-. |
cplusplus | 1 | Определен при компиляции в режиме C++. |
DLL | 1 | Определен, если компилируется динамическая библиотека. |
FLAT | 1 | Определен при компиляции в 32-битной модели памяти. |
MIХ86 | Определен всегда. Значение по умолчанию — 300. (Можно изменить значение на 400 или 500, применив соответственно ключи /4 или /5 в командной строке.) | |
MSDOS | 1 | Целая константа. |
MT | 1 | Определен, если установлена опция -WM. Она означает, что будет присоединяться мультили-нейная (multithread) библиотека. |
PASCAL | 1 | Определен, если установлено соглашение о вызове Pascal. |
TCPLUSPLUS | 0х0540 | Определен, если компиляция производится в режиме C++ (аналогично bcplusplus ). |
TEMPLATES | 1 | Определен для файлов C++ (показывает, что поддерживаются шаблоны). |
TLS | 1 | Thread Local Storage. В C++Builder определен всегда. |
TURBOC | 0х0540 | Номер версии (аналогичен BORLANDC ). |
WCHAR T | 1 | Определен только в программах C++ (показывает, что wear t — внутренне определенный тип. |
WCAR T DEFINED | 1 | То же, что и WCHAR Т. |
Windows | Определен для кода, используемого только в Windows. | |
WIN32 | 1 | Определен для консольных и GUI-приложений. |
Как видите, многие предопределенные макросы C++Builder отражают те или иные установки параметров компиляции, задаваемые в командной строке (при ручном запуске компилятора Ьсс32.ехе). Те же самые установки могут быть выполнены и в интегрированной среде через диалог Project Options, который мы еще будем рассматривать в этой главе.
Макросы с параметрами
Макросы могут выполнять не только простую текстовую подстановку. Возможно определение макросов с параметрами, напоминающих функции языка С, например:
#define PI 3.14159265
#define SQR(x) ( (x) * (x) )
#define AREA(x) (PI * SQR(x))
#define MAX(a, b) (<a)>(b) ? (a): (b))
…
circleArea = AREAfrl + r2);
cMax = MAX(i++, j++);
Третье макроопределение показывает, что макросы могут быть вложенными. После каждого расширения макроса препроцессор снова сканирует полученный текст на предмет того, не содержит ли он идентификаторов, в свою очередь являющихся макросами. Исключением являются случаи, когда расширение содержит собственное имя макроса или является препроцессорной директивой.
Обратите внимание на скобки в показанных 'выше определениях. Можно сформулировать такое правило: каждый параметр и все определение в целом должны заключаться в скобки. Иначе при вхождении макроса в выражение могут появляться ошибки, связанные с различным приоритетом операций. Рассмотрите такой случай:
#define SQR(x) х*х binom = -SQR(a + b) ;
При расширении макроса получится:
binom = -a + b*a + b;
Порядок оценки выражения окажется совсем не тем, что подразумевался.
Преобразование в строку
В макросах может применяться специальная операция преобразования в строку (#). Если в расширении макроса параметру предшествует эта опе-
рация, то выражение-аргумент будет преобразовано в литеральную строку, заключенную в двойные кавычки. Вот один пример:
#define SHOWINT(var)
printf(#var " = %d\n", (int)(var))
int iVariable = 100;
SHOWINT(iVariable) ;
Последняя строчка расширяется в
printf("iVariable"" = %d\n", (int)(iVariable));
и печатает
iVariable = 100
В С примыкающие друг к другу литеральные строки при компиляции соединяются в одну строку.
Конкатенация
Операция конкатенации (##) позволяет составить из нескольких лексем единое слово. Получившийся элемент повторно сканируется для обнаружения возможного идентификатора макроса. Рассмотрите такой код:
#define DEF_INT(n) int iVar ## n
…
DEF_INT(One); // Расширяется в int iVarOne;
DEF_INT(Two); // Расширяется в int iVarTwo; и т.д.