Литмир - Электронная Библиотека
A
A

По техническим причинам в языке С (но не в языке C++) неявно допускается, чтобы константы появлялись из других модулей компиляции.

/* файл x.c: */

const int x;     /* инициализирована в другом месте */

/* файл xx.c: */

const int x = 7; /* настоящее определение */

В языке С++ в разных файлах могут существовать два разных объекта с одним и тем же именем

x
. Вместо использования ключевого слова
const
для представления символьных констант программисты на языке С обычно используют макросы. Рассмотрим пример.

#define MAX 30

void f(int v)

{

  int a1[MAX]; /* OK */

  switch (v) {

  case 1:

    /* ... */

    break;

  case MAX:    /* OK */

    /* ... */

    break;

  }

}

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Имя макроса
MAX
заменяется символами
30
, представляющими собой значение этого макроса; иначе говоря, количество элементов массива
a1
равно
30
, а меткой второго раздела case является число
30
. По общепринятому соглашению имя макроса
MAX
состоит только из прописных букв. Это позволяет минимизировать ошибки, вызываемые макросами.

27.8. Макросы

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Берегитесь макросов: в языке С нет по-настоящему эффективных способов избежать макросов, но их использование имеет серьезные побочные эффекты, поскольку они не подчиняются обычным правилам разрешения области видимости и типов, принятым в языках С и С++. Макросы — это вид текстуальной подстановки. См. также раздел А.17.2.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Как защититься от потенциальных проблем, связанных с макросами, не отказываясь от них навсегда (и не прибегая к альтернативам, предусмотренным в языке С++?

• Присваивайте всем макросам имена, состоящие только из прописных букв:

ALL_CAPS
.

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

• Никогда не давайте макросам короткие или “изящные” имена, такие как

max
или
min
.

• Надейтесь, что остальные программисты следуют этим простым и общеизвестным правилам.

В основном макросы применяются в следующих случаях:

• определение “констант”;

• определение конструкций, напоминающих функции;

• улучшение синтаксиса;

• управление условной компиляцией.

Кроме того, существует большое количество менее известных ситуаций, в которых могут использоваться макросы.

Мы считаем, что макросы используются слишком часто, но в программах на языке С у них нет разумных и полноценных альтернатив. Их даже трудно избежать в программах на языке С++ (особенно, если вам необходимо написать программу, которая должна подходить для очень старых компиляторов или выполняться на платформах с необычными ограничениями).

Мы приносим извинения читателям, считающим, что приемы, которые будут описаны ниже, являются “грязными трюками”, и полагают, что о них лучше не говорить в приличном обществе. Однако мы думаем, что программирование должно учитывать реалии и что эти (очень простые) примеры использования и неправильного использования макросов сэкономят часы страданий для новичков. Незнание макросов не приносит счастья. 

27.8.1. Макросы, похожие на функции

Рассмотрим типичный макрос, напоминающий функцию.

#define MAX(x, y) ((x)>=(y)?(x):(y))

Мы используем прописные буквы в имени

MAX
, чтобы отличить его от многих функций с именем
max
(в разных программах). Очевидно, что этот макрос сильно отличается от функции: у него нет типов аргументов, нет тела, нет инструкции
return
и так далее, и вообще, зачем здесь так много скобок? Проанализируем следующий код:

int aa = MAX(1,2);

double dd = MAX(aa++,2);

char cc = MAX(dd,aa)+2;

Он разворачивается в такой фрагмент программы:

int aa = ((1)>=( 2)?(1):(2));

double dd = ((aa++)>=(2)?( aa++):(2));

char cc = ((dd)>=(aa)?(dd):(aa))+2;

Если бы всех этих скобок не было, то последняя строка выглядела бы следующим образом.

char cc = dd>=aa?dd:aa+2;

Иначе говоря, переменная

cc
могла бы легко получить другое значение, которого вы не ожидали, исходя из определения макроса. Определяя макрос, не забывайте заключить в скобки каждый аргумент, входящий в выражение.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 С другой стороны, не всегда скобки могут спасти нас от второго варианта развертывания. Параметру макроса x было присвоено значение
aa++
, а поскольку переменная x в макросе
MAX
используется дважды, переменная a может инкрементироваться также дважды. Не передавайте макросу аргументы, имеющие побочные эффекты.

Какой-то “гений” определил макрос следующим образом и поместил его в широко используемый заголовочный файл. К сожалению, он также назвал его

max
, а не
MAX
, поэтому когда в стандартном заголовке языка C++ объявляется функция

template<class T> inline T max(T a, T b) { return a<b?b:a; }

имя

max
разворачивается с аргументами
T a
и
T b
, и компилятор видит строку

template<class T> inline T ((T a)>=(T b)?(T a):(T b))

397
{"b":"847443","o":1}