Анимация
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

a(); \

b(); \

} else

но я думаю, что комбинация do с while (0) незначительно лучше.

Так как вы можете объявить переменную после любой открытой фигурной скобки, то у вас появляется возможность использования предшествующего метода для определения макроса, имеющего по существу свои собственные локальные переменные. Рассмотрим следующий макрос, осуществляющий обмен значениями между двумя целочисленными переменными:

#define swap int(x,y) \ do \

int x##y; \

x##y = x; \

x = y; \

y = x##y \

while (0)

Сочетание ## является оператором конкатенации в стандарте ANSI Си. Я использую его здесь для обеспечения того, чтобы имя временной переменной не конфликтовало с любым из имен исходных переменных. При данном вызове:

swap(laurel, hardy);

препроцессор вначале подставляет аргументы обычным порядком (заменяя x на laurel, а y на hardy), давая в результате следующее имя временной переменной:

int laurel##hardy;

Затем препроцессор удаляет знаки решетки, давая int laurelhardy;

Дополнительная польза от возможности замены макросов функциями заключается в отладке. Иногда вы хотите, чтобы что-то было подобно макросу по эффективности, но вам нужно при отладке установить в нем точку прерывания. Используйте для этого в Си++ встроенные функции, а в Си используйте следующие:

#define AT LEFT(this)((this)->left child is thread ? NULL\

:(this)->left)

#ifdef DEBUG

static tnode *at left(tnode *this) { return AT LEFT(this); }



#else

# define at left( this) AT LEFT(this) #endif

Я закончу это правило упоминанием о еще двух причудливых конструкциях, которые иногда полезны в макросе, прежде всего потому, что они помогают макросу расширяться в один оператор, чтобы избежать проблем с фигурными скобками, рассмотренных ранее. Положим, вы хотите, чтобы макрос по возможности расширялся в единственное выражение. Оператор последовательного вычисления достигает этого в ущерб читаемости, и наряду с ним я никогда не использую формы, показанные в таблице 1 , по той же причине - их слишком трудно читать. (Коли на то пошло, я также не использую их в макросах, если я могу достичь желаемого каким-то другим способом).

Таблица 1. Макросы, эквивалентные условным операторам

Этот код:

Делает то же самое, что и:

(a&&f() )

if(a)

f();

(bf() )

if(!b)

f();

( z ? f() : g())

if(z)

f();

else

g();

Первые два выражения опираются на тот факт, что вычисления в выражении с использованием операций && и гарантированно осуществляются слева направо и прекращаются сразу, как только устанавливается истина или ложь. Возьмем для примера выражение a&& f(). Если a ложно, то тогда не важно, что возвращает f(), так как все выражение ложно, если любой из его операндов значит ложь. Следовательно, компилятор никогда не вызовет f() , если a ложно, но он должен вызвать f() , если a истинно. То же самое применимо и к b, но здесь f() вызывается, если b, напротив, ложно.

81.1. Операция ?: не то же самое, что и оператор

if/else

Последняя строка в таблице 1 относится к другому спорному вопросу. Условная операция - это простой оператор. Она используется лишь в выражении и передает значение. Условная операция является не очень привычной заменой для оператора if/else, но не менее, чем операции && или приемлемы для замены простого if. Хотя большинство людей



и не принимают во внимание замену: if(z)

i=j;

else

i=k;

z&&(i=j);

z(i = k);

Мне довелось случайно увидеть подобное этому, но с использованием условной операции:

z?(i = j):(i = k);

Все предыдущие фрагменты в равной мере способны сбить с толку. Следующий код показывает, как надлежащим образом использовать условную операцию, и ее результат яснее (т.е. лучше), чем у равноценного оператора if/else:

i = z?j:k;

81.2. Помещайте тело макроса и его аргументы в круглые скобки

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

#define TWO K 1024 + 1024

что при использовании в:

10 * TWO K

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

10* 1024 + 1024

вычисляемого как:

(10 * 1024) + 1024

Решаем эту задачу при помощи круглых скобок:

#define TWO K (1024 + 1024)

Вот сходная задача в следующем фрагменте кода:

#define SQUARE(x) (x * x)

Определено:

SQUARE(y + 1);



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