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

1 ... 35 36 37 [ 38 ] 39 40 41 ... 77


#define

CONDITION C 0x03

#define

SET CONDITION A(p)

((p)->status

CONDITION A)

#define

SET CONDITION B(p)

((p)->status

CONDITION B)

#define

SET CONDITION C(p)

((p)->status

CONDITION C)

#define

CLEAR CONDITION A(p)

((p)->status

&=

= -CONDITION A)

#define

CLEAR CONDITION B(p)

((p)->status

&=

= -CONDITION B)

#define

CLEAR CONDITION C(p)

((p)->status

&=

= -CONDITION C)

#define

IN CONDITION A(p)

((p)->status

&

CONDITION A)

#define

IN CONDITION B(p)

((p)->status

&

CONDITION B)

#define

IN CONDITION C(p)

((p)->status

&

CONDITION C)

#define

POSSIBILITIES(x)

((x) & 0x0030)

#define

POSSIBILITY A

0x0000

#define

POSSIBILITY B

0x0010

#define

POSSIBILITY C

0x0020

#define

POSSIBILITY D

0x0030

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

if ( struct.status &= -CONDITION A )

по меньшей следующему:

мере, с трудом читается. Еще хуже нечто, подобное

struct.status = POSSIBILITY A; if ( POSSIBILITIES( struct.status)

...

== POSSIBILITY A )

Лучшее решение использует битовые поля; они не требуют дополнительного места и заведомо эффективно реализуются на большинстве машин. (Некоторые люди утверждают, что второй пример лучше, чем битовое поле, потому что здесь нет неявного сдвига, но многие машины поддерживают команду проверки бита, которая устраняет какую-либо потребность в сдвиге, который в случае своего использования вызывает очень незначительные накладные расходы. Необходимость в устранении ненужной путаницы обычно перевешивает подобные соображения о снижении эффективности).

enum { possibility a, possibility b, possibility b, possibility d };

struct fred



unsigned in condition a : 1;

unsigned in condition b : 1;

unsigned in condition c : 1;

unsigned possibilities : 2;

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

struct fred flintstone; flintstone.in condition a = 1; if ( flintstone.in condition a )

...

flintstone.possibilities = possibility b;

if ( flintstone.possibilities == possibility a )

...

Единственным очевидным исключением из этого правила является взаимодействие с архитектурами со страничной организацией памяти; битовые поля не гарантируют какого-то упорядочивания в типе int, из которого выделяются биты.

85.3. Не используйте флагов завершения

Флаг завершения типа готов едва ли нужен в Си или Си++. Его использование просто добавляет одну лишнюю переменную в процедуру. Не делайте так:

BOOL готов = FALSE; while ( !готов )

if ( некоторое условие() ) готов = 1;

Поступайте следующим образом:

while ( 1 )

if ( некоторое условие() ) break;

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



Единственным исключением из этого правила является выход из вложенных циклов в Си++, где оператор goto может привести к пропуску программой вызова конструктора или деструктора. Эта проблема была рассмотрена в правиле 54.

85.4. Рассчитывайте, что ваш читатель знает Си

Не делайте чего-то подобного этому:

#define SHIFT LEFT(x, bits) ((x) << (bits))

Программисты на Си знают, что << означает сдвиг влево . Аналогично, не делайте таких вещей:

x++; инкрементировать x

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

85.5. Не делайте вид, что Си поддерживает булевый тип (#define TRUE)

Нижеследующее может скорее вызвать проблемы, чем нет:

#define TRUE 1 #define FALSE 0

Любая отличная от нуля величина в Си означает истину, поэтому в следующем фрагменте f() может вернуть совершенно законное значение истина , которое не совпало с 1 , и проверка даст отрицательный результат:

if( f() == TRUE ) Вызов не выполняется, если f() возвращает значение истина , отличное от 1.

...

Следующий вариант надежен, но довольно неудобен. Я не думаю, что можно рекомендовать что-либо из подобной практики:

#define FALSE 0 if( f() != FALSE ) ...

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



1 ... 35 36 37 [ 38 ] 39 40 41 ... 77

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