
- •Стадии препроцессорной обработки
- •Макроопределение без параметров
- •Пример:
- •Пример:
- •Пример:
- •Цепочка подстановок
- •Сравнение макросов и функций
- •Пример:
- •Пример:
- •Пример:
- •Int II, jj, ll; // целые внешние переменные
- •Условная компиляция
- •Пример:
- •Пример:
- •Пример:
- •Вспомогательные директивы
- •Пример:
- •Void main ( ) // строка 2
- •Пример:
Цепочка подстановок
В рассмотренных выше примерах строка замещения представляла собой некоторую символьную константу. В более общем случае строка замещения является выражением. В качестве отдельной лексемы в ней может содержаться макрос, ранее определенный другой директивой #define. Таким образом, препроцессор выполняет цепочку последовательных подстановок.
Пример: Определение диапазона возможных значений любой целой переменной типа int.
#include <limits.h>
#define RANGE ((INT_MAX) - (INT_MIN) +1)
/*RANGE - диапазон значений для int */
int RANGE_T = RANGE/8;
При работе препроцессора сначала выполняется директива #include <limits.h> - в текст программы вставляется текст из файла limits.h (кратко директива #include уже была рассмотрена в лекции 12).
В данном файле определены константы 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 в тексте программы заменилась на соответствующий текст (значения констант могут различаться в зависимости от разрядности ЭВМ).
Далее препроцессор выполняет подстановки в соответствии с двумя первыми директивами #define и программа принимает вид:
#define RANGE ((32767) - (-32768) + 1)
/*RANGE - диапазон Значений для int*/
int RANGE_T = RANGE/8;
В результате выполнения подстановок изменилась строка замещения макроса RANGE в последней директиве #define. После выполнения данной директивы текст программы примет вид:
/*RANGE - диапазон значений для int*/
int RANGE_T = ((32767)-(-32768)+1)/8;
Теперь все директивы #define удалены из текста.
Подстановка строки замещения вместо имени макроса RANGE была выполнена в выражении RANGE/8. Согласно рассмотренным ранее ограничениям не произошло подстановки внутри комментария, а также остался без изменений идентификатор RANGE_T.
Параметризованное макороопределение
Использование макроопределения без параметров имеет существенный недостаток - строка замещения фиксирована. Большие возможности предоставляет макроопределение с параметрами:
#define идентификатор(параметры) строка_замещения
параметры - список идентификаторов, разделенных запятыми. Между именем макроса и скобкой не должно быть пробелов.
Для макровызова используется конструкция вида:
идентификатор (аргументы)
аргументы – список препроцессорных лексем, разделенных запятыми.
Пример:
Макроопределение
#define MAX(x,y) ((x) < (y) ? (x) : (y))
позволяет формировать в программе выражение, определяющее максимальное из двух значений аргументов.
Аргументы макровызова используются при макроподстановке. Например, макровызов
у = MAX(suml, sum2);
он будет заменен на
у = ((suml)>(sum2)?(suml):(sum2));
Отсутствие круглых скобок может привести к неправильному порядку вычисления, поскольку препроцессор не оценивает строку замещения текст с точки зрения синтаксиса.
Пример:
Если определен макрос
#define sqr(x) (x*x)
то макровызов sqr(y+l) будет заменен выражением sqr(у+1*у+1).
Последовательные подстановки, выполняемые в строке замещения, не действуют на параметры макроса, т.е. аргумент макроса не может быть выражением, содержащим переменные, ранее определенные другой директивой #define.
Препроцессорные операции в строке замещения
В строке замещения могут быть использованы операции # и ##.
Операция # помещается перед параметром. При макроподстановке аргумент, замещающий данный параметр, заключался в двойные кавычки.
Пример:
Если определен макрос
#define print(A) printf(#A“=%f”, А)
то макровызов
print (sin (a/2));
будет заменен оператором
printf (“sin(a/2)”“=%f”, sin (a/2));
Последовательность операторов
double а=3.14159;
print (sin (a/2));
выведет на экран текст
sin(a/2)=1.0
Операция ## помещается между двумя лексемами строки замещения, и осуществляет их конкатенацию (соединение).
Пример:
Определены три макроса с одинаковым списком параметров и одинаковыми аргументами. Отличает только строка замещения.
#define zero(a, b, с, d) a (bcd)
#define one(a, b, c, d) a (b с d)
#define two(a, b, c, d) a (b##c##d)
Макровызов: Результат макроподстановки:
zero(sin, x, +, у) sin(bсd)
one(sin, x, +, у) sin(x + у)
two(sin, x, +, y) sin(x+y)
В строке замещения макроса zero( ) последовательность “bсd” воспринимается как отдельный идентификатор. Замена символов b, с, d в составе идентификатора bсd аргуметами, соответствующими параметрам b, с, d не выполняется.
В строке замещения макроса one( ) символы b, с, d воспринимаются как три отдельных параметра, т.к. они отделены друг от друга пробелами. После макроподстановки пробелы сохраняются.
В строке замещения для макроса two( ) между параметрами b и с, с и d использована операция ##. Это позволяет после макроподстановки получить запись аргументов без пробелов между ними.