Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [ 34 ] 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

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

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 = default i ;

В фрагменте:

void f( int i)

#define DEFAULT I 1024 if

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 0x2 0

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



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [ 34 ] 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76