Программирование >>  Решение нетривиальных задач 

1 ... 32 33 34 [ 35 ] 36 37 38 ... 77


что расширяется макросом до:

y + 1 * y + 1

и вычисляется как:

y + (1 * y) + 1

И вновь круглые скобки приходят на помощь. Следующее определение:

#define SQUARE(x) ((x) * (x))

расширяется до:

((y + 1) * (y + 1))

82. enum и const лучше, чем макрос

Директива #define должна быть вашим последним средством при определении значения константы. Рассмотрим следующую рассмотренную ранее распространенную ошибку:

#define TWO K 1024 + 1024 x = TWO K * 10

что в результате вычисления дает 11264 (1024+(1024*10)) вместо требуемых 20480. Определение перечисления типа:

enum { two k = 1024 + 1024 };

или константы типа:

const int Two k = 1024 + 1024;

не вызывает трудностей, связанных с макросом. И круглые скобки не требуются.

Перечисление enum на несколько очков превосходит константу: во-первых, определение const int в языке Си на самом деле выделяет память под тип int и инициализирует ее. Вы не можете модифицировать эту область памяти, но память при этом занята. Следовательно, определение константы в Си нельзя поместить в заголовочном файле; вы нужно будет воспользоваться модификатором extern как для какой-нибудь глобальной переменной. (В Си++ все это несущественно, так как там память выделяется лишь тогда, когда вы определяете адрес константы или передаете его по ссылке. Определения констант в Си++ могут - а на деле часто и должны - помещаться в заголовочном файле).

Перечисление отличается тем, что память для него никогда не выделяется. Подобно макросу, оно может вычисляться во время компиляции. Следовательно, при использовании вами перечисления не



* С этим утверждением автора, так и следующим за ним примером инкрементирования аргумента макроса нельзя согласиться. - Ред.

происходит потери производительности.

Второй проблемой является порча области глобальных имен. Область действия перечисления легко ограничивается. Например, в следующем фрагменте кода перечисление default i действует лишь внутри функции f() :

void f( int i )

enum { default i = 1024 };

if ( !i )

i = default i ;

В фрагменте:

void f( int i )

#define DEFAULT I 1024

if ( !i )

i = DEFAULT I ;

макрос DEFAULT I виден всем функциям, чьи определения следуют после определения этого макроса. Если DEFAULT I определяется в заголовочном файле, то он будет виден в нескольких файлах - даже если он не используется кодом в этих файлах. Та же самая проблема касается также константы, определенной на глобальном уровне.

Перечислитель enum особенно полезен в Си++, потому что он может быть ограничен областью действия класса и инициализироваться в самом определении класса вместо конструктора. Эти вопросы рассматриваются далее в той части книги, что посвящена правилам Си++.

Наконец, перечислитель может быть использован в качестве аргумента оператора case и размера при объявлении массива. Ни в одной из указанных ситуаций константа использоваться не может*.

83. Аргумент параметризированного макроса не должен появляться в правой части более одного раза

Макрос SQUARE() даже в своем модифицированном виде представил выше серьезную проблему. Дано:

#define SQUARE(x) ((x)*(x))



Выражение SQUARE(++x) дважды инкрементирует x. После чего макрос в этом случае дает неверный результат. Если x вначале содержит 2, то SQUARE(++x) вычисляется как 3*4. Такое поведение есть пример побочного эффекта макроса - ситуации, когда макрос ведет себя неожиданно.

SQUARE(++x) также показывает пример ситуации, в которой использование макроса просто слишком рискованно для оправдания сложностей сопровождения. Встроенная функция Си++ или шаблон, расширяемый до встроенной функции, являются более удачными решениями. Даже в Си простую функцию с неудачными аргументами легче сопровождать, чем эквивалентный макрос:

double square( double x )

return x * x;

Но, тем не менее, у меня есть серьезное сомнение в том, что использование функции для скрытия простого умножения является стоящим делом.

83.1. Никогда не используйте макросы для символьных констант

Например:

#define SPACE

имеет смысл, если только вы намерены использовать вместо пробела другой символ (как если бы вы испытывали, например, программу для замены символов табуляции). Никогда не делайте так:

#define SPACE 0x20

Действительное значение символьной константы для пробела ( ) изменяется компилятором так, чтобы оно соответствовало операционной среде, для которой ведется компиляция. Для среды, поддерживающей ASCII, это значение будет 0x2 0, а для EBDCDIC - уже нечто другое. Не думайте, что у какого-то символа свое постоянное значение.



1 ... 32 33 34 [ 35 ] 36 37 38 ... 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика