Важно!
Важно помнить, что в C++ предусмотрен еще один способ определения констант, который заключается в использовании спецификатора const. Однако многие программисты "пришли" в C++ из С-среды, где для этих целей обычно использовалась директива #define. Поэтому вам еще часто придется с ней сталкиваться в С++-коде.Макроопределения, действующие как функции
Директива #define
имеет еще одно назначение: макроимя может использоваться с аргументами. При каждом вхождении макроимени связанные с ним аргументы заменяются реальными аргументами, указанными в коде программы. Такие макроопределения действуют подобно функциям. Рассмотрим пример.
/* Использование "функциональных" макроопределений. */
#include
using namespace std;
#define MIN(a, b) (((a)<(b)) ? a : b)
int main()
{
int x, y;
x = 10;
y = 20;
cout << "Минимум равен: " << MIN(x, у);
return 0;
}
При компиляции этой программы выражение, определенное идентификатором MIN (а, b)
, будет заменено, но x и y будут рассматриваться как операнды. Это значит, что cout-инструкция после компиляции будет выглядеть так.
cout << "Минимум равен: " << (((х)<(у)) ? х : у);
По сути, такое макроопределение представляет собой способ определить функцию, которая вместо вызова позволяет раскрыть свой код в строке.
Макроопределения, действующие как функции,
— это макроопределения, которые принимают аргументы.Кажущиеся избыточными круглые скобки, в которые заключено макроопределение MIN
, необходимы, чтобы гарантировать правильное восприятие компилятором заменяемого выражения. На самом деле дополнительные круглые скобки должны применяться практически ко всем макроопределениям, действующим подобно функциям. Нужно всегда очень внимательно относиться к определению таких макросов; в противном случае возможно получение неожиданных результатов. Рассмотрим, например, эту короткую программу, которая использует макрос для определения четности значения.
// Эта программа дает неверный ответ.
#include
using namespace std;
#define EVEN(a) a%2==0 ? 1 : 0
int main()
{
if(EVEN(9+1)) cout << "четное число";
else cout << "нечетное число ";
return 0;
}
Эта программа не будет работать корректно, поскольку не обеспечена правильная подстановка значений. При компиляции выражение EVEN(9+1)
будет заменено следующим образом.
9+1%2==0 ? 1 : 0
Напомню, что оператор "%"
имеет более высокий приоритет, чем оператор "+". Это значит, что сначала выполнится операция деления по модулю (%) для числа 1, а затем ее результат будет сложен с числом 9, что (конечно же) не равно 0. Чтобы исправить ошибку, достаточно заключить в круглые скобки аргумент a в макроопределении EVEN, как показано в следующей (исправленной) версии той же программы.
// Эта программа работает корректно.
#include
using namespace std;
#define EVEN(a) (a)%2==0 ? 1 : 0
int main()
{
if(EVEN(9+1)) cout << "четное число";
else cout << "нечетное число";
return 0;
}
Теперь сумма 9+1
вычисляется до выполнения операции деления по модулю. В общем случае лучше всегда заключать параметры макроопределения в круглые скобки, чтобы избежать непредвиденных результатов, подобных описанному выше.Использование макроопределений вместо настоящих функций имеет одно существенное достоинство: поскольку код макроопределения расширяется в строке, и нет никаких затрат системных ресурсов на вызов функции, скорость работы вашей программы будет выше по сравнению с применением обычной функции. Но повышение скорости является платой за увеличение размера программы (из-за дублирования кода функции).