Цепочка подстановок
Если в строке замещения команды #define в качестве отдельной лексемы встречается препроцессорный идентификатор, ранее определенный другой директивой #define, то выполняется цепочка последовательных подстановок. В качестве примера рассмотрим, как можно определить диапазон (RANGE) возможных значений любой целой переменной типа int в следующей программе:
Пример 10.2 #include < limits.h> #define RANGE ((INT_MAX) – < INT_MIN)+1) /* RANGE - диапазон значений для int */ …………. int RANGE_T = RANGE/8; ………….
Препроцессор последовательно просматривает текст и, обнаружив директиву #include < limits.h>, вставляет текст из файла limits.h. Там определены константы INT_MAX и INT_MIN (предельные максимальное и минимальное значения целых величин). Программа принимает следующий вид: #define INT_MAX 32767 #define INT_MIN -32768 #define RANGE ((INT_MAX)-(INT_MIN)+1). /* RANGE - диапазон значений для int*/ int RANGE_T = RANGE/8;
Обратим внимание на то, что директива #include исчезла из программы, но ее заменил соответствующий текст. Обнаружив в тексте (добытом из файла limits.h) директивы define..., препроцессор выполняет соответствующие подстановки, и программа принимает следующий вид:
#define RANGE ((32767)-(-32768)+1) /* RANGE - диапазон значений для int*/ int RANGE_T = RANGE/8; Подстановки изменили строку замещения препроцессорного идентификатора RANGE в директиве #define, размещенной ниже, чем текст, включенный из файла limits.h. Последовательно анализируя текст программы, препроцессор встречает препроцессорный идентификатор RANGE и выполняет подстановку. Программа приобретает следующий вид:
/* RANGE - диапазон значений для int*/ int RANGE_T - ((32767) - (-32768)+1)/8;
Теперь все директивы #define удалены из текста. Получен текст, пригодный для компиляции, т.е. создана «единица трансляции». Подстановка строки замещения вместо идентификатора RANGE выполнена в выражении RANGE/8, однако внутри комментария идентификатор RANGE остался без изменений и не изменился идентификатор RANGE_Т. Этот пример иллюстрирует выполнение «цепочки» подстановок и ограничения на замены: замены не выполняются внутри комментариев, внутри строковых констант, внутри символьных констант и внутри идентификаторов (не может измениться часть идентификатора). Например, RANGE_T остался без изменений. Если строка замещения оказывается слишком длинной, то ее можно продолжить в следующей строке текста. Для этого в конце продолжаемой строки помещается символ ‘\’. В ходе одной из стадий препроцессорной обработки этот символ вместе с последующим символом конца строки будет удален из текста программы. Пример 10.3 #define STRING " \n Game Over! – Игра закончена! " printf(STRING); На экран будет выведено: Game Over! – Игра закончена!
С помощью команды #define удобно выполнять настройку программы. Например, если в программе требуется работать с массивами, то их размеры можно явно определять на этапе препроцессорной обработки:
Пример 10.4 Исходный текст Результат препроцессорной обработки #define К 40 void main() void main () { { int M[K][K]; int M[40][40]; float A[2*K+1], float A[2*40+l], float B[K+3][K-3] float В[40+3][40-3]; …….. ……..
При таком описании очень легко изменять предельные размеры сразу всех массивов, изменив только одну константу (строку замещения) в директиве #define. Предусмотренные директивой #define препроцессорные. замены не выполняются внутри строк, символьных констант и комментариев, т.е. не распространяются на тексты, ограниченные кавычками (“), апострофами (‘) и разделителями (/*,. */). В то же время строка замещения может содержать перечисленные ограничители, например, при замене препроцессорного идентификатора STRING. Если в программе нужно часто печатать или выводить на экран дисплея значение какой-либо переменной и, кроме того, снабжать эту печать одним и тем же пояснительным текстом, то удобно ввести сокращенное обозначение оператора печати, например:
Пример 10.5 #define PK printf(“\n Номер элемента=%d.”, N);
После этой директивы использование в программе оператора РК; будет эквивалентно (по результату) оператору из строки замещения. Например, последовательность операторов int N = 4; РК; приведет к выводу такого текста: Номер элемента=4.
Если в строку замещения входит идентификатор, определенный в другой команде #define, то в строке замещения выполняется следующая замена (цепочка подстановок). Например, программа, содержащая команды: Пример 10.6 #define К 50 #define PE printf (“\п Число элементов K=%d”, K); ……. РЕ; выведет на экран такой текст: Число элементов К=50 Идентификатор К внутри строки замещения, обрамленной кавычками, не заменяется на 50.
Строку замещения, связанную с конкретным препроцессорным идентификатором, можно сменить, приписав уже определенному идентификатору новое значение другой командой #define:
Пример 10.7 #define M 16 /* Идентификатор М определен как 16 */ #define M ‘С’ /* М определен как символьная константа ‘С’ */ #define M “С” /* М определен как символьная строка, в которой есть два элемента ‘С’ и ‘\0’ */
Однако при таких сменах значений препроцессорного идентификатора некоторые компиляторы C++ выдают предупреждающее сообщение на каждую следующую директиву #define:
Warning..: Redefinition of ‘M’ is not identical Замены в тексте можно отменять с помощью команды #undef идентификатор После выполнения такой директивы этот препроцессорный идентификатор становится неопределенным, и его можно определять повторно. Например, не вызовут предупреждающих сообщений директивы: #define M 16 #undef M #define M ’С’ #undef M #define M ”С”
Директиву #undef удобно использовать при разработке больших программ, когда они собираются из отдельных «кусков текста», написанных в разное время или разными программистами. В этом случае могут встретиться одинаковые обозначения разных объектов. Чтобы не изменять исходных файлов, включаемый текст можно «обрамлять» подходящими директивами #define, #undef и тем самым устранять возможные ошибки.
Пример 10.8 ……. А = 10; / Основной текст*/ ……. #define A X ……. А = 5; / Включенный текст */ ……….. #undef A ……….. В = А; / Основной текст */ ……….
При выполнении программы переменная В примет значение 10, несмотря на наличие оператора присваивания А = 5 во включенном тексте.
|