
- •Глава 3. Производные типы данных. Массивы.
- •3.3. Многомерные массивы.
- •3.4. Базовые алгоритмы обработки двумерных массивов.
- •Глава 4. Функции.
- •4.1. Стандартное определение функции.
- •4.2. Локальные переменные.
- •4.3. Размещение тела функции. Прототип функции.
- •4.4. Глобальные переменные.
- •4.5. Классы памяти.
- •5.1. Директива # define.
- •5.2. Использование макроопределений с аргументами.
- •5.3. Директива # include. Включение файла.
- •5.4. Условная компиляция.
5.1. Директива # define.
Директива # define может появиться в любом месте исходного файла, даваемое ею определение имеет силу от места появления до конца файла. Директива часто используется для определения символических констант, однако она имеет более широкое применение ( в предыдущих параграфах приводились примеры определения констант с помощью директивы # define). Рассмотрим пример.
Пример .
# define TWO 2 /* определена числовая константа, равная 2*/
# define FOUR TWO*TWO
# define MSG “ это директива препроцессора ”
# define PRIN_1 cout<< “a=” << a
# define CONST1 “ a равно ”
# include <iostream.h>
/* Рассмотрим программу, использующую перечисленные директивы*/
void main ( ) Результат выполнения
{ int a=TWO;
cout << MSG; это директивы препроцессора
PRIN_1; а=2
a=FOUR;
cout << CONST1<< a; а равно 4
}
Рассмотрим формат директивы # define. Каждая строка состоит из трех частей:
1 часть- наименование директивы,
2 часть- макроопределение,
3 часть- << строка замещения >>.
# define MSG “это директива препроцессора ”
директива макроопределение срока замещения
# define TWO 2
директива макроопределение строка замещения
# define PRIN_1 cout<< “a=” << a макроопределение строка замещения
Процесс прохождения от макроопределения до заключительной строки замещения называется «макрорасширением».
Следует помнить, что препроцессор не выполняет никаких действий, он делает предложенные подстановки, а все действия выполняются только при работе программы.
Рассмотрим, например, две директивы
# define TWO 2;
# define FOUR TWO*TWO;
Во время выполнения программы оператор
int a= TWO превращается в оператор int a= 2;оператор a= FOUR заменяется на оператор a= TWO*TWO и затем в оператор а=2*2. Фактическое умножение произошло при работе программы.
Рассмотрим директиву # define PRIN_1 cout<< “a=” << a.
Оператор PRIN_1 превращается в строку cout<< “a=” << a и при выполнении программы на экран выводится текст и значение переменной а. Необходимо заметить, что PRIN_1 напечатает только переменную, названную а.
5.2. Использование макроопределений с аргументами.
Макроопределение с аргументами очень похоже на функцию, но это не одно и тоже, хотя, вообще говоря, макроопределение с аргументами можно назвать макрофункцией.
Рассмотрим конкретный пример. Определим макрофункцию, вычисляющую квадрат некоторого числа.
# define SQR (x) x*x
# define PRIN (x) cout << “ x в квадрате равен ” << x
# include <iostream.h>
void main ( )
{ int x=4;
int y;
y=SQR (x); // y=x*x
PRIN (y); // cout <<“x в квадрате равен”<<y
PRIN (SQR(x)); //cout <<“x в квадрате равен”<<x*x
// Рассмотрим некоторые специфические ситуации. Результат вычисления
х=4;
PRIN (SQR(x+2)) замещение строк после компиляции 14
cout <<“x в квадрате равен”<< x+2*х+2
PRIN (100/SQR(2)) cout <<”x в квадрате равен”<<100/2*2 100
PRIN (SQR(++x) cout <<”x в квадрате равен”<<++x*++x 30
Рассмотрим каждый случай отдельно и определим причину получения «неожиданных» результатов. Для того, чтобы полученные результаты объяснить, необходимо вспомнить, что препроцессор не выполняет никаких вычислений, он только замещает строку.
Случай 1.
PRIN (SQR(x+2)); так как после компиляции получено выражение х+2*х+2, то расставив порядок действий можно понять, почему на экран выводится число 14. При х=4 получаем 4+2*4+2=14. Этот пример очень характерен, он показывает существенное отличие функции от макрофункции.
При вызове функции передается значение аргумента в программу, макровызовов передает строку аргументов в программу на первом шаге компиляции.
Для получения правильного ответа макроопределение должно быть записано следующим образом:
# define SQR (x) (x)*(x)
Случай 2.
PRIN (100/SQR(2)); Строка замещается следующей строкой cout <<”x в квадрате равен”<<100/2*2. Вычисления ведутся слева на право: 100/2=50; 50*2=100. В таком случае макроопределение необходимо записать так: # define SQR (x) (x*x).
Для получения правильного ответа для этих двух примеров, необходимо записать:
# define SQR (x) ((x)*(x)).
Случай 3.
SQR (++x) превращается в строку ++х*++х и при х=4, получаем 5*6. Операции инкремента и декремента не рекомендуется использовать при записи аргумента в макрофункции.
Отметим следующее преимущество макроопределений: при их использовании не нужно беспокоиться о типах переменных, так как макроопределения имеют дело с символьными строками, а не с фактическими значениями, но использование макроопределения приводит к увеличению объема памяти, так как макроопределение создает строчный код. При использовании макроопределений желательно соблюдать следующие правила:
в макроопределении нет пробелов, но они могут появиться в замещающей строке, препроцессор полагает, что макроопределение заканчивается на первом пробеле, поэтому все, что стоит после пробела, остается в замещающей строке;
— использование круглых скобок для каждого аргумента и всего определения, это является гарантией того, что аргументы будут сгруппированы нужным образом;
—для имен макрофункций следует использовать прописные буквы.