Просмотр промежуточного файла
Почти каждый компилятор имеет ключ, который можно устанавливать или в интегрированной среде разработки, или в командной строке. С помощью этого ключа можно сообщить компилятору о том, что вы хотите сохранить промежуточный файл. Если вас действительно интересует содержимое этого файла, обратитесь к руководству по использованию компилятора, чтобы узнать, какие ключи можно для него устанавливать.
Использование директивы #define
Команда #define определяет строку подстановки. Строка
#define BIG 512
означает, что вы предписываете препроцессору заменять лексему BIG строкой 512 в любом месте программы. Эта запись не является командой языка C++. Строка 512 вставляются в исходную программу везде, где встречается лексема BIG. Лексема — это строка символов, которую можно применить там, где может использоваться любая строка, константа или какой-нибудь другой набор символов. Таким образом, при записи строк
#define BIG 512 int myArray[BIG];
промежуточный файл, создаваемый препроцессором, будет иметь такой вид:
int myArray[512];
Обратите внимание, что в коде исчезла команда #define. Из промежуточного файла все директивы препроцессора удаляются, поэтому они отсутствуют в конечном варианте кода источника.
Использование директивы #define для создания констант
Один вариант использования директивы #define - это создание констант. Однако этим не стоит злоупотреблять, поскольку директива #define просто выполняет замену строки и не осуществляет никакого контроля за соответствием типов. Как пояснялось на занятии, посвященном константам, гораздо безопаснее вместо директивы #define использовать ключевое слово const.
Использование директивы #define для тестирования
Второй способ использования директивы #define состоит в простом объявлении того, что данная лексема определена в программе. Например, можно записать следующее:
#define BIG
В программе можно проверить, была ли определена лексема BIG, и предпринять соответствующие меры. Для подобной проверки используются такие команды препроцессора, как #ifdef (если определена) и #ifndef (если не определена). За обеими
командами должна следовать команда #endif, которую необходимо установить до завершения блока (до следующей закрывающей фигурной скобки).
Директива #ifdef принимает значение, равное истине, если тестируемая лексема уже была определена. Поэтому можем записать следующее:
#ifdef DEBUG
cout << "Строка DEBUG определена"; #endif
Когда препроцессор читает директиву #ifdef, он проверяет построенную им самим таблицу, чтобы узнать, была ли уже определена в программе лексема DEBUG. Если да, то #ifdef возвращает значение true, и все, что находится до следующей директивы #else или #endif, записывается в промежуточный файл для компиляции. Если эта директива возвращает значение false, то ни одна строка кода, находящаяся между директивами #ifdef DEBUG и #endif, не будет записана в промежуточный файл, т.е. вы получите такой вариант промежуточного файла, как будто этих строк никогда и не было в исходном коде.
Обратите внимание, что директива #ifndef является логической противоположностью директивы #ifdef. Директива #ifndef возвращает true в том случае, если до этой точки в программе заданная лексема не была определена.
Комманда препроцессора #else
Как вы правильно предположили, директиву #else можно вставить между #ifdef (или #ifndef) и завершающей директивой #endif. Использование этих директив показано в листинге 21.1.
Листинг 21.1. Использование директивы #define
1: #define DemoVersion
2: #define NT_VERSION 5
3: #include <iostream.h>
4:
5:
6: int main()
7: {
8:
9: cout << "Checking on the definitions of DemoVersion, NT_VERSION _and WINDOWS_VERSION...\n";
10:
11: #ifdef DemoVersion
12: cout << "DemoVersion defined.\n";
13: #else
14: cout << "DemoVersion not defined.\n";
15: #endif
16:
17: #ifndef NT_VERSION
18: cout << "NT_VERSION not defined!\n";
19: #else
20: cout << "NT_VERSION defined as: " << NT_VERSION << endl;
21: #endif
22:
23: #ifdef WINDOWS_VERSION
24: cout << "WINDOWS_VERSION definod!\n";
25: #else
26: cout << "WINDOWS_VERSION was nol: do1inod.\n";
27: #endif
28:
29: cout << "Done.\n";
30: return 0;
31: }
Результат:
hecking on the definitions of DemoVersion, NT_VERSION_and
WINDOWS_VERSION...
DemoVersion defined.
NT_VERSION defined as: 5
WINDOWS_VERSION was not defined.
Done.
Анализ: В строках 1 и 2 определяются лексемы DemoVersion и NT_VERSION, причем лексеме NT_VERSION назначается литерал 5. В строке 11 проверяется определение лексемы DemoVersion, а поскольку она определена (хотя и без значения), то результат тестирования принимает истинное значение и строка 12 выводит соответствующее сообщение.
В строке 17 определенность лексемы NT_VERSION проверяется с помощью директивы #ifndef. Поскольку данная лексема определена, возвращается значение false и выполнение программы продолжается со строки 20. Именно здесь слово NT_VERSION заменяется символом 5, т.е. компилятор воспринимает эту строку кода в следующем виде:
cout << " NT_VERSION defined as: " << 5 << endl:
Обратите внимание, что первое слово в сообщении NT_VERSION не замещается строкой 5, поскольку является частью текстовой строки, заключенной в кавычки. Но лексема NT_VERSION между операторами вывода замешается; таким образом, компилятор видит вместо нее символ 5, точно так же, как если бы вы ввели этот символ в выражение вывода.
Наконец, в строке 23 программа проверяет определенность лексемы WIND0WS_VERSI0N. Поскольку эта лексема в программе не определена, возвращается значение false и строкой 26 выводится соответствующее сообщение.