46019 (665326), страница 23
Текст из файла (страница 23)
Обработанный препроцессором раздел может в свою очередь содержать вложенные на любую глубину условные предложения, причем каждой директиве #if должна соответствовать закрывающая директива #endif.
Итоговым результатом вышеописанного сценария является то, что для дальнейшей обработки передается только один раздел (возможно, пустой). Опущенныеразделы служат только для отслеживания возможных вложенных условных конструкций, так что каждая директива #if должна обязательно иметь соответствующую ейзавершающую директиву #endif.
Проверяемые выражения-типа-константы при вычислении должны давать целочисленную константу.
Операция defined
Операция defined дает альтернативный, более гибкий способ проверки того, определены ли комбинации идентификаторов, или нет. Данная операция допустима только в выражениях #if и #elif.
Выражение defined(идентификатор)или defined идентификатор (скобки необязательны) дает 1 (истина), если данное символическое имя было ранее определено (при помощи директивы # defined) иэто определение не было впоследствии отменено (при помощи #undef); в противном случае оно дает 0 (истина). Поэтому директива
#if defined(mysym)
это то же, что
#ifdef mysym
Преимущество заключается в том, что можно использовать defined циклически в сложном выражении, следующем за директивой #if, например
#if defined(mysym) && !defined(yoursym)
Условные директивы #ifdef и #ifndef
Условные директивы #ifdef и #ifndef позволяют проверить, определен лив текущий момент данный идентификатор, то есть была ли обработана предыдущаядиректива #define для данного идентификатора и продолжает ли она действовать в текущий момент.
Строка
#ifdef идентификатор
имеет точно такой же эффект, что и
#if 1
если идентификатор в текущий момент определен, и такой же эффект, что и
#if 0
если идентификатор в текущий момент не определен.
#ifndef, напротив, дает значение "истина", если идентификатор "не определен", поэтому строка
#ifndef идентификатор
имеет точно такой же эффект, что и
#if 0
если идентификатор в текущий момент определен, и такой же эффект, что и
#if 1
если идентификатор в текущий момент не определен.
Синтаксис следует синтаксису директив #if, #elif, #else и #endif, описанному в предыдущем разделе.
Идентификатор, определенный как имеющий пустое (NULL) значение, считается определенным.
Директива управления нумерацией строк #line
Директива #lineслужитдля заданияпрограмме способа нумерации строк, используемой при создании перекрестных ссылок и при выдаче сообщений об ошибках. Если программасостоит из разделов, взятых из других программных файлов, часто бывает полезно обозначить такиеразделы номерами строк, взятыми из соответствующего исходного текста, а не обычными последовательными номерами строк составной программы. Синтаксис директивы следующий:
#line целочисленная-константа
и обозначает, что следующая исходная строка берется начиная с номера строки, заданного целочисленной-константой, из файла, заданного "именем-файла". После того, как имя-файла было однажды задано, все последующие команды #line, относящиеся к данному файлу, может опустить явный аргумент имя-файла. Например,
/* TEMP.C: пример директивы #line */
#include
#line 4 "junk.c"
void main()
(*
printf(" in line %d of %s",__LINE__,__FILE__);
#line 12 "temp.c"
printf("\n");
printf(" in line %d of %s",__LINE__,__FILE__);
#line 8
printf("\n");
printf(" in line %d of %s",__LINE__,__FILE__);
*)
Включение stdio.h означает, что на выходе препроцессора будет нечто большее.
Если запустить TEMP.C через CPP (cpp temp), то на выходе получится файл TEMP.I, который выглядит так:
temp.c 1:
c:\borland\tc\cpp\include\stdio.h 1:
c:\borland\tc\cpp\include\stdio.h 2:
c:\borland\tc\cpp\include\stdio.h 3:
...
c:\borland\tc\cpp\include\stdio.h 212:
c:\borland\tc\cpp\include\stdio.h 213:
temp.c 2:
temp.c 3:
junk.c 4: void main()
junk.c 5: (*
junk.c 6: printf(" in line %d of %s",6,"junk.c");
junk.c 7:
temp.c 12: printf("\n");
temp.c 13: printf(" in line %d of %s",13,"temp.c");
temp.c 14:
temp.c 8: printf("\n");
temp.c 9: printf(" in line %d of %s",9,"temp.c");
temp.c 10: *)
temp.c 11:
Если вызатем компилируете TEMP.C, то получится следующий выход:
in line 6 of junk.c
in line 13 of temp.c
in line 9 of temp.c
Макросы расширяются в аргументах #line, как в директивах #include.
Прежде всего назначение директивы #line заключается в использовании ее в утилитах, имеющих на выходе коды С, а не в кодах, создаваемых человеком "вручную".
Директива #error
Директива #error имеет следующий синтаксис:
#error сообщение-об-ошибке
Директива генерирует сообщение:
Error: имя-файла номер-строки : Error directive: сообщение
Данная директива обычно встраивается в условные конструкции препроцессора, которые отслеживают какие-либо нежелательные условия времени компиляции.Обычноэто условие "ложно". Если условие "истинно", то компилятор может выдать сообщение об ошибке и прекратитьработу. Для этого директива# error помещается в условную ветвь, которая дает для искомого нежелательного условия результат "истина".
Например, вы определили #define MYVAL, принимающую значения 0 или 1. Затем можновключить в исходный код условную директиву, которая будет проверять MYVAL на предмет неверного значения:
#if (MYVAL != 0 && MYVAL != 1)
#error MYVAL must be defined to either 0 or 1
#endif
Директива #pragma
Директива #pragma позволяет использовать специфичные для конкретных реализаций директивы в форме
#pragma имя-директивы
При помощи #pragma Turbo C++позволяет определить любые желаемые директивы, не обращаясь дляэтогок другим, поддерживающим их компиляторам. Если компилятор не поддерживает данное имя-директивы, то он просто игнорируетдирективу #pragma, не выдаваяпри этом никаких сообщений об ошибкахили предупреждений.
Turbo C++ поддерживает следующие директивы #pragma:
- #pragma argsused
- #pragma exit
- #pragma inline
- #pragma option
- #pragma saveregs
- #pragma startup
- #pragma warn
Директива #pragma argsused
Директива #pragma argsused допустима только между определениями функций и действует только на следующую функцию. Она отменяет сообщение уровня предупреждения:
"Parameter name is never used in function имя-функции" ("имя параметра нигде не используется в функции имя-функции")
Директивы #pragma exit и #pagma startup
Данные две директивы позволяют программе задать функцию (функции), которые должны вызываться либо призагрузке программы (перед вызовам main), либо при выходе из программы (непосредственно перед выходом из программы через _exit).
Синтаксис этих директив следующий:
#pragma exit имя-функции
#pragma startup имя-функции
Заданное имя-функции должно относиться к ранееобъявленной функции, не принимающей аргументов и возвращающей значение void; другими словами, эта функция должна быть объявлена как:
void func(void);
Опциональный параметр приоритет должен являться целым числом в диапазоне от 64 до 255. Старшим приоритетом является 0. (Приоритеты от 0 до 63 используются библиотеками С и не должны использоваться пользователем). Функции со старшими приоритетами вызываются первыми при загрузке программы и последними при выходе из нее. Если приоритет не задан, то по умолчанию он равен 100. Например,
#include
void startFunc(void)
(*
printf("Startup function.\n");
*)
#pragma startup startFunc 64
/* приоритет 64 --> вызывается при загрузке первой */
void exitFunc(void)
(*
printf("Wrapping up execution.\n");
*)
#pragma exit exitFunc
/* приоритет по умолчанию равен 100 */
void main(void)
(*
printf(This is main.\n");
*)
Отметим, что имя функции, используемой в #pragma startupили exit, должно быть определено (или объявлено) до того,как встретится соответствующая строка с директивой #pragma.
Директива #pragma inline
Данная директиваэквивалентна опции компилятора командной строки -B или соответствующей опции интегрированной среды Turbo. Она сообщаеткомпилятору, что программа содержит встроенные ассемблерные коды (см. главу 6, "Интерфейс с языком ассемблера"). Синтаксис ее следующий:
#pragma inline
Эту директиву лучше всего помещать вверху файла, поскольку, встретив директиву #pragma inline, компилятор перезапускает себя с опцией -B. Фактически можно опустить и опцию -В, и директиву #pragma inline, и компилятор тем не менее выполнитперезапуск, когда встретит операторы asm. Назначение опции и директивы состоит в том, чтобы сэкономить время компиляции.
Директива #pragma option
Директива #pragma option используетсядля включения опций компилятора командной строки в код вашей программы. Синтаксис этой директивы следующий:
#pragma option [опции...]
Опции могут являться любыми опциями команднойстроки(за исключением перечисленных в следующем параграфе). В одной директиве может находиться любое число опций.
Ниже приводятсяопции,которые не могут находиться в директиве pragma option:
-B (компиляция с использованием ассемблерных кодов)
-c (компиляция без компоновки)
-dxxx (определение макроса)
-Dxxx = ccc (определение макроса с текстом)
-efff (имя .EXE-файла fff)
-lfff (имя включаемой директории)
-Lfff (имя директории с библиотекой)
-lxset (опция компоновщика x)
-M (создание при компоновке .MAP-файла)
-o оверлеи
-Q EMS
-S (создание на выходе .ASM-файла и остановка)
-Uxxx (отмена определения макроса)
-V (virtual)
-Y (оверлеи)
Существует два состояния компилятора. В первом состоянии в директиву pragma option можно включить большее количество опций, нежели во втором. Первое состояние компилятора называется состоянием лексического анализа,а второе - состоянием кодирования.
Использование имени макроса, начинающегося двумя символами подчеркивания (которое может являться именем встроенного макроса), в директивах #if, #ifdef, #ifndef или #elifизменяет состояние компилятора на состояние кодирования.
Появление первой реальной лексемы (первого объявления
С) также изменяет состояние компилятора на кодирование.
Другими словами, можноиспользовать директивы #pragma, # include, #define и некоторые разновидности #if во время состояния лексического анализакомпилятора. Во время этой фазы вы имеете возможность при помощи #pragma option изменять опции командной строки.
В числоопций,которые могут быть заданы в #pragma option только в состоянии лексического анализа компилятора, входят:
-Efff (строка с ассемблерным именем)
-f* (любая опция плавающей точки, кроме -ff)
-l# (значащие символы идентификатора)
-m* (любая опция модели памяти)
-nddd (выходная директория)
-offf (имя выходного файла fff)
-u (использование символов подчеркивания в именах cdecl)
-z* (опция задания любого имени сегмента)
Прочие опции могут изменяться где угодно. Следующие опции оказывают воздействие на компилятор только если они изменяются между объявлениями функций или объектов:
-1 Управление набором команд
-2 Управление набором команд
-a Управление выравниванием. (Отметим, что выравнивание компонентов структурыустанавливается в точке определения структуры, а не далее, при использовании этой структуры объектами.)
-ff Управление быстрыми операциями с плавающей точкой
-G Генерация кода, оптимизированного по быстродействию
-k Стандартное управление стековым фреймом
-N Управление контролем стека
-O Управление оптимизацией
-P Установка по умолчанию соглашений о связях Pascal
-r и -rd Управление регистровыми переменными
-v Управление отладкой по действиям
-y Управление строчной информацией
Следующие опциимогут изменяться в любой момент и оказывают немедленное воздействие на компилятор:
-A Управление ключевыми словами
-C Управление вложенностью комментариев
-d Слияние повторяющихся строк
-gn Остановка компиляции после n предупреждений
-jn Остановка компиляции после n ошибок
-K Тип char устанавливается как unsigned
-wxxx Предупреждение (то же самое, что и #pragma warn)
Любые из имеющих два переключающихся состояния опций (такие как -a или -K) могут быть переведены во включенное или выключенное состояние, как это делается в командной строке. Дополнительно имеется возможность задавать их с точкой (.), что устанавливает такие опции в состояние, задаваемое командной строкой.
Директива #pragma saveregs
Директива #pragma saveregs гарантирует, что при входе в функцию hugeзначения всехрегистров останутся без изменений. Данная директива иногда бывает нужна для интерфейса с кодами на языкеассемблера. Директивадолжнанаходиться непосредственно перед определением функции. Ее действие распространяется только на данную функцию.