- •Стадии препроцессорной обработки
- •Макроопределение без параметров
- •Пример:
- •Пример:
- •Пример:
- •Цепочка подстановок
- •Сравнение макросов и функций
- •Пример:
- •Пример:
- •Пример:
- •Int II, jj, ll; // целые внешние переменные
- •Условная компиляция
- •Пример:
- •Пример:
- •Пример:
- •Вспомогательные директивы
- •Пример:
- •Void main ( ) // строка 2
- •Пример:
Сравнение макросов и функций
Макросы можно сравнить с функциями. В определении функции содержаться операторы, которые должны выполниться при вызове функции. В строке замещения макроса содержится текст, который должен быть подставлен в текст программы при вызове макроса.
Общие черты макросов и функций:
Начинают выполняться в момент вызова,
Принимают параметры и возвращают значение,
Вызов выполняется по имени с указанием аргументов,
При вызове аргументы ставятся на место параметров.
Отличия макросов от функций:
Определение функции присутствует в программе один раз. Тексты, формируемые макросом, вставляются в программу в каждую точку макровызова.
Для функции тип аргументов и тип возвращаемого значения фиксированы. Макрос пригоден для обработки аргументов любого типа, который допустим в выражениях строки замещения. Тип получаемого значения зависит только от типов аргументов и выражений строки замещения. Например, рассмотренный макросы MAX( ) работает для аргументов любых целых и вещественных типов.
Фактические параметры функций могут быть выражениями, которые вычисляются в процессе выполнения программы. Аргументы макровызова не могут быть выражением, содержащим переменные, ранее определенные другой директивой #define.
Использование директивы #define
С помощью директивы #define можно вводить собственные обозначения базовых или производных типов.
Пример:
Директива
#define REAL long double
вводит имя REAL для типа long double. Далее в тексте программы можно определять объекты типа long double, используя данное имя:
REAL x, array [6];
Такой прием удобно использовать для присвоения более коротких имен сложным типам данных.
Директиву #define также удобно использовать для сокращенного обозначения оператора печати, в случае, когда в программе требуется часто выводить на экран значение переменной с одним и тем же пояснительным текстом.
Пример:
После записи директивы
#define PN printf ("\n Номер элемента=%d", N);
Последовательность операторов
int N=4;
PN;
выведет на экран текст
Номер элемента=4
Еще одной из областей эффективного применения макросов является адресации элементов многомерных массивов.
Доступ к элементам многомерных массивов в С++ имеет две особенности, которые создают неудобства при работе с ними:
при обращении к элементу массива нужно указывать все его индексы,
нумерация элементов массивов начинается с нуля.
Применение макросов для организации доступа к элементам массива позволяет обойти трудности, вызванные указанными выше особенностями, хотя это достигается за счет нетрадиционных обозначений элементов - индексы в макросах, представляющих элементы массивов, заключаются в круглые скобки.
Пример:
#define N 4 // число строк матрицы
#define M 5 // число столбцов матрицы
#define A(i,j) x[M*(i-l) + (j-1)]
#include <stdio.h>
void main ( )
{
/* Определение одномерного массива */
double x[N*M];
int i, j, k;
for (k=0; k < N*M; k++) x[k]=k;
/* Перебор строк */
for (i=1; i<=N; i++) {
printf ("\n Строка %d:", i);
for (j=1; j<=M; j++) printf(" %6.1f", A(i, j)); // перебор элементов строк
}
}
Результат выполнения программы:
Строка 1: 0.0 1.0 2.0 3.0 4.0
Строка 2: 5.0 6.0 7.0 8.0 9.0
Строка 3: 10.0 11.0 12.0 13.0 14.0
Строка 4: 15.0 16.0 17.0 18.0 19.0
В программе создается виртуальный многомерный массив, размерностью N×М (N – число строк, M - число столбцов). Размерность массива задается на этапе препроцессорной обработки с помощью директивы #define. Для имитации многомерного массива (создания виртуального массива) в программе используется макроопределения и одномерный (реальный) массив x[] размерностью N*M. Таким образом, элементы виртуального многомерного массив будут размешаться в одномерном массиве построчно. Значения элементам массива присваиваются в цикле с параметром k.
На рис. 2.7 приведена схема одномерного массива х[ ] для моделирования виртуального двумерного массива с помощью макоопределений.
Рис. 2.7 – Имитация многомерного массива с помощью одномерного массива и макроопределения.
Для доступа к элементам массива используются макровызовы A(i, j). Индекс i соответствует номеру строки многомерного массива и изменяется от 1 до N, а индекс j – номеру столбца и изменяется во внутреннем цикле от 1 до М. A(i,j) является достаточно естественным обозначениями элементов матрицы, причем нумерация столбцов и строк начинается с 1.
За счет применения макросов выполняются замены параметризованных обозначений A(i, j) на x[5*(i-l)+(j-l)]. Далее действия выполняются над элементами одномерного массива х[ ]. Но т.к. данные преобразования выполняются на этапе препроцессорной обработки можно считать, что осуществляется работа с традиционными для многомерных массивов обозначениями.
Использованный в программе оператор
printf (“% 6.1f”, A (i, j));
после макроподстановок будет иметь вид:
printf (“% 6.1f”, x[5*(i-l)+(j-l)]);
Например
A (1,1) соответствует x[0] - вычислено как x[5(1-1)+(1-1)]
A (1,2) соответствует x[1] – вычислено x[5(1-1)+(2-1)]
A (2,1) соответствует x[5] – вычислено как x[5(2-1)+(1-1)]
A (3,4) соответствует x[13] – вычислено как x[5(3-1)+(1-1)]
Макросы унаследованы из языка С, при написании программ на C++ их следует избегать. Вместо макросов без параметров предпочтительнее использовать const или enum, а вместо параметризованных макросов — встроенные функции или шаблоны.
Директива #undef отменяет действие директивы #define.
Директива #undef имеет следующий формат:
#undef идентификатор
После выполнения директивы идентификатор, ранее определенный директивой #define. для препроцессора становится неопределенным, и его можно определять повторно.