Шаг 76.
Макроподстановка

    На этом шаге мы рассмотрим правила определения и использования макросов.

    Средства макрообработки позволяют определять и обрабатывать макроимена, ориентированные на простую текстовую подстановку, с помощью макроопределений и макровызовов.

    Макроопределение имеет вид:

   #define <макроимя> <строка_лексем>
                    или
   #define <макроимя>(<список_параметров>) <строка_лексем>

где макроимя - идентификатор, строка_лексем - последовательность лексем от макроимени до конца строки (точка с запятой в конце не ставится). Существенно, что между макроименем и открывающей круглой скобкой не должно быть пробелов!

    Список_параметров макроимени - это список идентификаторов, разделенных запятыми. Строка_лексем также может содержать эти параметры.

    Любой макровызов (появление макроимени в качестве отдельной лексемы в тексте программы, расположенном после макроопределения) ведет к замене этого макроимени на указанную строку_лексем. Область действия макроимени - фрагмент программы от точки макроопределения и до конца текста программы.

    Макровызов должен быть отдельной лексемой и состоять из макроимени и заключенного в круглые скобки списка аргументов. При обработке макровызова препроцессор заменяет каждый параметр в строке лексем на соответствующий аргумент макровызова. Количество аргументов в макровызове должно соответствовать количеству параметров макроопределения.

    Для макровызова действует правило, согласно которому после замены макроимени строка вновь просматривается с целью обнаружения в ней макровызовов. Таким образом, допускаются вложенные макровызовы.

    Если в строку замещения входит идентификатор, определенный в другой команде #define, то в строке замещения выполняется следующая замена (цепочка подстановок). Например, программа, содержащая команды:

   #define K 50;
   #define PE cout << "\nКоличество элементов K = " << K
   .   .   .   .   .
      PE;
   .   .   .   .   .
выведет на экран такой текст:
   Количество элементов К = 50

    Обратите внимание, что идентификатор к внутри строки замещения, обрамленной кавычками ("), не заменен на 50.

    Текст внутри строк, символьные константы и комментарии не подлежат замене, т.к. строки и символьные константы являются неделимыми лексемами языка C++. Так что, после макроопределения

   #define YES 1
в операторе
   cout << "YES";
не будет сделано никакой макроподстановки.

    Замены в тексте можно отменять с помощью команды:

   #undef <макроимя>
После выполнения такой директивы макроимя для препроцессора становится неопределенным и его можно определять повторно. Например, не вызовут предупреждающих сообщений директивы:
   #define M 16
   #undef M
   #define M 'C'
   #undef M
   #define M "C"

    Директиву #undef удобно использовать при разработке больших программ, когда они собираются из отдельных "кусков текста", написанных в разное время или разными программистами. В этом случае могут встретиться одинаковые обозначения разных объектов. Чтобы не изменять исходных файлов, включаемый текст можно "обрамлять" подходящими директивами #define - #undef и тем самым устранять возможные ошибки. Приведем пример:

   .   .   .   .   .
     A = 10; //Основной текст.
   .   .   .   .   .
   #define A X
   .   .   .   .   .
     A = 5; //Включенный текст.
   .   .   .   .   .
   #undef A
   .   .   .   .   .
     B = A; //Основной текст.
   .   .   .   .   .
При выполнении программы B примет значение 10, несмотря на наличие оператора присваивания A=5; во включенном тексте.

    Если строка_лексем оказывается слишколм длинной, то ее можно продолжить в следующей строке текста программы. Для этого в конце продолжаемой строки помещается символ "\". В ходе одной из стадий препроцессорной обработки этот символ вместе с последующим символом конца строки будет удален из программы. Например:

   #define STROKA "\n Multum, non multa - \
    многое, но немного!"
   .   .   .   .   .
   cout << STROKA;
   .   .   .   .   .
На экран будет выведено:
   Multum, non multa - многое, но немного!


    На следующем шаге мы приведем несколько примеров использования директив препроцессора.


Предыдущий шаг Содержание Следующий шаг