Анимация
JavaScript
|
Главная Библионтека что расширяется макросом до: 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 |