SlideShare uma empresa Scribd logo
1 de 8
Baixar para ler offline
Как уменьшить вероятность ошибки
на этапе написания кода. Заметка N
                                 N2.
Автор: Андрей Карпов

Дата: 29.03.2011


Аннотация
Это вторая статья о том, как можно избежать ряда ошибок еще на этапе написания кода. В
предыдущей заметке уже упоминался совет избегать множества вычислений в одном выражении.
Однако, этот вопрос требует более пристального внимания. Рассмотрим опасность сложных
условий, и как можно предупредить многие логические ошибки.


Введение




С предыдущей заметкой можно познакомиться здесь. В этот раз примеры ошибок будут взяты из
                                                         т
различных известных проектов, чтобы подчеркнуть их распространенность Ошибки, которые
                                                    распространенность.
будут продемонстрированы были найдены мной с помощью анализатора PVS-Studio за
      продемонстрированы,
относительно большой промежуток времени. Практически про все я писал разработчикам и
поэтому можно надеяться, что они поправлены в новых редакциях кода. Это я пишу во введении,
потому что после статей мне всегда приходят письма с просьбой "напишите про найденные вами
ошибки разработчикам проекта".


1. Не используйте тернарн
                  тернарную операцию '?:' в составных выражениях
Тернарная условная операция записывается в языке Си/Си++ с помощью оператора '?:'. Операция,
                                                    Си++
возвращающая свой второй или третий операнд в зависимости от значения логического
выражения, заданного первым операндом. Пример:

int minValue = A < B ? A : B;

Тернарная операция имеет очень низкий приоритет (см. таблицу). Про это часто забывают и
именно из-за этого тернарная операция крайне опасна.
          за
Рисунок 1 - Операции языка Си/Си++, в порядке снижения приоритета

Обратите внимание, что операция '?:' имеет более низкий приоритет, чем сложение, умножение,
оператора побитового ИЛИ и так далее. Рассмотрим код:

int Z = X + (A == B) ? 1 : 2;

Этот код работает не так, как может показаться на первый взгляд. Программист, скорее всего,
хотел сложить значение X с числом 1 или с числом 2, в зависимости от условия (A == B). Но на
самом деле условием является выражение "X + (A == B)". Фактически, здесь написано:

int Z = (X + (A == B)) ? 1 : 2;

А хотелось:
int Z = X + (A == B ? 1 : 2);

Первая мысль – так ведь надо знать приоритет операций. А программисты их и знают, но уж очень
коварна эта тернарная операция! Ошибки с ней допускают не только новички, но и матёрые
программисты. Её нередко можно встретить даже в самом качественном коде. Вот пара
примеров.




V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a
lower priority than the '*' operator. physics dgminkowskiconv.cpp 1061

dgInt32 CalculateConvexShapeIntersection (...)

{

    ...

    den = dgFloat32 (1.0e-24f) *

             (den > dgFloat32 (0.0f)) ?

                  dgFloat32 (1.0f) : dgFloat32 (-1.0f);

    ...

}




V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a
lower priority than the '-' operator. views custom_frame_view.cc 400

static const int kClientEdgeThickness;

int height() const;

bool ShouldShowClientEdge() const;
void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) {

    ...

    int edge_height = titlebar_bottom->height() -

                             ShouldShowClientEdge() ? kClientEdgeThickness : 0;

    ...

}




V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a
lower priority than the '|' operator. vm vm_file_win.c 393

#define FILE_ATTRIBUTE_NORMAL                    0x00000080

#define FILE_FLAG_NO_BUFFERING                    0x20000000

vm_file* vm_file_fopen(...)

{

    ...

    mds[3] = FILE_ATTRIBUTE_NORMAL |

                (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING;

    ...

}

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

Избежать подобных ошибок можно, если не стремиться поместить несколько операций в одну
строку кода. А если и помещать, то не жадничать поставить дополнительные скобки. Про скобки
мы поговорим ниже. Сейчас попробуем просто предотвратить потенциальные ошибки при
использовании '?:'.

Конечно оператор '?:' является синтаксическим сахаром и его практически всегда можно заменить
на if. К редким исключениям относится такие задачи, как инициализация ссылки:

MyObject &ref = X ? A : B;
Конечно, и с этим проблем нет, но создание ссылки на A или B без оператора '?:' будет гораздо
многословней:

MyObject *tmpPtr;

If (X)

  tmpPtr = &A;

else

  tmpPtr = &B;

MyObject &ref = *tmpPtr;

Итак, отказываться от оператора '?:' не рационально. Но и ошибиться с ним просто. Поэтому лично
я выработал для себя следующее правило. Результат '?:' должен сразу куда-то помещаться и не
сочетаться с другими действиями. То есть слева от условия оператора '?:' должна стоять операция
присваивания. Вернемся к первоначальному примеру:

int Z = X + (A == B) ? 1 : 2;

Я предлагаю писать так:

int Z = X;

Z += A == B ? 1 : 2;

В случае примера, относящегося к IPP Samples, я бы написал так:

mds[3] = FILE_ATTRIBUTE_NORMAL;

mds[3] |= (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING;

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


2. Не стесняйтесь использовать скобки
Почему-то так повелось, что использование лишних скобок при программировании на Си/Си++
считается неким постыдным действием. Возможно, это идёт от того, что вопрос про приоритет
операций очень любят задавать на собеседовании. И у человека подсознательно откладывается
стремление всегда максимально использовать механизм приоритетов. Ведь если он поставит
лишние скобки, то вдруг кто-то подумает что он новичок, а не истинный джедай.

Я даже встречал на просторах интернета обсуждение, где человек категорично утверждал, что
использование лишних скобок - это плохой тон. А если человек не уверен как будет вычислено
выражение, то ему надо учиться, а не программы писать. К сожалению, не смог найти эту
дискуссию, но я не разделяю подобные точки зрения. Знать приоритеты, конечно, надо, но если в
выражении используются разнородные операции, то лучше подстраховывать себя скобками. Это
не только защитит от потенциальных ошибок, но и сделает код более легко читаемым для других
разработчиков.

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




V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or
intended to use the '&&' operator. game g_client.c 1534

#define SVF_CASTAI 0x00000010

char *ClientConnect(...) {

    ...

    if ( !ent->r.svFlags & SVF_CASTAI ) {

    ...

}




V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or
intended to use the '&&' operator. dosbox sdlmain.cpp 519

static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags,

                                                                Bit32u bpp) {

    ...

    if (!sdl.blit.surface || (!sdl.blit.surface->flags&SDL_HWSURFACE)) {

    ...
}



Ну и еще один пример из Chromium:

V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or
intended to use the '&&' operator. base platform_file_win.cc 216

#define FILE_ATTRIBUTE_DIRECTORY 0x00000010

bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {

    ...

    info->is_directory =

     file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0;

    ...

}

Выражения просты. Разработчики молодцы. А ошибки всё равно есть. Так что использование
скобок в скользких местах лишним не будет.

Наверное, стоит поступать так. Если операции просты и привычны, то дополнительные скобки не
нужны. Примеры:

if (A == B && X != Y)

if (A - B < Foo() * 2)

А вот если используются более редкие операторы (~, ^, &, |, <<, >>, ?:), то явные скобки не
повредят. Это и упростит чтение кода и защит от потенциальной ошибки. Примеры:

If ( ! (A & B))

x = A | B | (z < 1 ? 2 : 3);

Не стесняться использовать скобки при использовании редких операций поможет и для
рассмотренного ранее оператора "?:". Как именно лучше поступить с "?:" - дело вкуса. Мне более
нравится вариант с упрощением.


Заключение
Пишите простой и понятный код. Разбивая длинные и сложные выражения на несколько строк, вы
получаете более длинный код. Но такой код бывает намного проще понять и прочитать. В нем
меньше вероятность допустить ошибку. Не бойтесь завести лишнюю переменную. Компилятор
отлично всё оптимизирует.

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

Mais conteúdo relacionado

Destaque (13)

12 estados unidos
12 estados unidos12 estados unidos
12 estados unidos
 
The Connected Customer 2012 ANZIIF Claims Convention - August 2012
The Connected Customer   2012 ANZIIF Claims Convention - August 2012The Connected Customer   2012 ANZIIF Claims Convention - August 2012
The Connected Customer 2012 ANZIIF Claims Convention - August 2012
 
7iSF-2 rup
7iSF-2   rup7iSF-2   rup
7iSF-2 rup
 
Urazbaev
UrazbaevUrazbaev
Urazbaev
 
Ics patrones-20130820
Ics patrones-20130820Ics patrones-20130820
Ics patrones-20130820
 
Build vs. Buy: The Hidden Costs of Licensing
Build vs. Buy: The Hidden Costs of Licensing Build vs. Buy: The Hidden Costs of Licensing
Build vs. Buy: The Hidden Costs of Licensing
 
Tipologias Multimedia
Tipologias MultimediaTipologias Multimedia
Tipologias Multimedia
 
Brenda pineda
Brenda pinedaBrenda pineda
Brenda pineda
 
Experiencias de aprendizajes con
Experiencias de aprendizajes conExperiencias de aprendizajes con
Experiencias de aprendizajes con
 
KVP Corporate presentation-2012
KVP Corporate presentation-2012KVP Corporate presentation-2012
KVP Corporate presentation-2012
 
Cantábrico Traducciones
Cantábrico TraduccionesCantábrico Traducciones
Cantábrico Traducciones
 
IFRS Training Flyer
IFRS Training FlyerIFRS Training Flyer
IFRS Training Flyer
 
Fx. esenciales
Fx. esencialesFx. esenciales
Fx. esenciales
 

Mais de Tatyanazaxarova

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияTatyanazaxarova
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программTatyanazaxarova
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокTatyanazaxarova
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиTatyanazaxarova
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурTatyanazaxarova
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхTatyanazaxarova
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияTatyanazaxarova
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиTatyanazaxarova
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаTatyanazaxarova
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхTatyanazaxarova
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейTatyanazaxarova
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаTatyanazaxarova
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаTatyanazaxarova
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовTatyanazaxarova
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаTatyanazaxarova
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокTatyanazaxarova
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокTatyanazaxarova
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеTatyanazaxarova
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияTatyanazaxarova
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииTatyanazaxarova
 

Mais de Tatyanazaxarova (20)

Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
 
Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
 
Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном коде
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
 

Как уменьшить вероятность ошибки на этапе написания кода. Заметка N2.

  • 1. Как уменьшить вероятность ошибки на этапе написания кода. Заметка N N2. Автор: Андрей Карпов Дата: 29.03.2011 Аннотация Это вторая статья о том, как можно избежать ряда ошибок еще на этапе написания кода. В предыдущей заметке уже упоминался совет избегать множества вычислений в одном выражении. Однако, этот вопрос требует более пристального внимания. Рассмотрим опасность сложных условий, и как можно предупредить многие логические ошибки. Введение С предыдущей заметкой можно познакомиться здесь. В этот раз примеры ошибок будут взяты из т различных известных проектов, чтобы подчеркнуть их распространенность Ошибки, которые распространенность. будут продемонстрированы были найдены мной с помощью анализатора PVS-Studio за продемонстрированы, относительно большой промежуток времени. Практически про все я писал разработчикам и поэтому можно надеяться, что они поправлены в новых редакциях кода. Это я пишу во введении, потому что после статей мне всегда приходят письма с просьбой "напишите про найденные вами ошибки разработчикам проекта". 1. Не используйте тернарн тернарную операцию '?:' в составных выражениях Тернарная условная операция записывается в языке Си/Си++ с помощью оператора '?:'. Операция, Си++ возвращающая свой второй или третий операнд в зависимости от значения логического выражения, заданного первым операндом. Пример: int minValue = A < B ? A : B; Тернарная операция имеет очень низкий приоритет (см. таблицу). Про это часто забывают и именно из-за этого тернарная операция крайне опасна. за
  • 2. Рисунок 1 - Операции языка Си/Си++, в порядке снижения приоритета Обратите внимание, что операция '?:' имеет более низкий приоритет, чем сложение, умножение, оператора побитового ИЛИ и так далее. Рассмотрим код: int Z = X + (A == B) ? 1 : 2; Этот код работает не так, как может показаться на первый взгляд. Программист, скорее всего, хотел сложить значение X с числом 1 или с числом 2, в зависимости от условия (A == B). Но на самом деле условием является выражение "X + (A == B)". Фактически, здесь написано: int Z = (X + (A == B)) ? 1 : 2; А хотелось:
  • 3. int Z = X + (A == B ? 1 : 2); Первая мысль – так ведь надо знать приоритет операций. А программисты их и знают, но уж очень коварна эта тернарная операция! Ошибки с ней допускают не только новички, но и матёрые программисты. Её нередко можно встретить даже в самом качественном коде. Вот пара примеров. V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '*' operator. physics dgminkowskiconv.cpp 1061 dgInt32 CalculateConvexShapeIntersection (...) { ... den = dgFloat32 (1.0e-24f) * (den > dgFloat32 (0.0f)) ? dgFloat32 (1.0f) : dgFloat32 (-1.0f); ... } V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '-' operator. views custom_frame_view.cc 400 static const int kClientEdgeThickness; int height() const; bool ShouldShowClientEdge() const;
  • 4. void CustomFrameView::PaintMaximizedFrameBorder(gfx::Canvas* canvas) { ... int edge_height = titlebar_bottom->height() - ShouldShowClientEdge() ? kClientEdgeThickness : 0; ... } V502 Perhaps the '?:' operator works in a different way than it was expected. The '?:' operator has a lower priority than the '|' operator. vm vm_file_win.c 393 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_FLAG_NO_BUFFERING 0x20000000 vm_file* vm_file_fopen(...) { ... mds[3] = FILE_ATTRIBUTE_NORMAL | (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING; ... } Как видите, ошибки данного типа заслуживают внимания. И я не случайно выделил их описание в отдельный пункт. Они весьма и весьма распространены. Можно привести другие примеры, но все они однотипны. Избежать подобных ошибок можно, если не стремиться поместить несколько операций в одну строку кода. А если и помещать, то не жадничать поставить дополнительные скобки. Про скобки мы поговорим ниже. Сейчас попробуем просто предотвратить потенциальные ошибки при использовании '?:'. Конечно оператор '?:' является синтаксическим сахаром и его практически всегда можно заменить на if. К редким исключениям относится такие задачи, как инициализация ссылки: MyObject &ref = X ? A : B;
  • 5. Конечно, и с этим проблем нет, но создание ссылки на A или B без оператора '?:' будет гораздо многословней: MyObject *tmpPtr; If (X) tmpPtr = &A; else tmpPtr = &B; MyObject &ref = *tmpPtr; Итак, отказываться от оператора '?:' не рационально. Но и ошибиться с ним просто. Поэтому лично я выработал для себя следующее правило. Результат '?:' должен сразу куда-то помещаться и не сочетаться с другими действиями. То есть слева от условия оператора '?:' должна стоять операция присваивания. Вернемся к первоначальному примеру: int Z = X + (A == B) ? 1 : 2; Я предлагаю писать так: int Z = X; Z += A == B ? 1 : 2; В случае примера, относящегося к IPP Samples, я бы написал так: mds[3] = FILE_ATTRIBUTE_NORMAL; mds[3] |= (islog == 0) ? 0 : FILE_FLAG_NO_BUFFERING; С рекомендацией можно не согласиться. Не буду её отстаивать. Например, мне не очень нравится, что вместо одной строки мы получаем две или более. Неплохой альтернативой может быть обязательное взятие оператора '?:' в скобки. Я ставлю основной задачей показать паттерны ошибок. Выбор паттерна защиты от ошибок зависит от предпочтений программиста. 2. Не стесняйтесь использовать скобки Почему-то так повелось, что использование лишних скобок при программировании на Си/Си++ считается неким постыдным действием. Возможно, это идёт от того, что вопрос про приоритет операций очень любят задавать на собеседовании. И у человека подсознательно откладывается стремление всегда максимально использовать механизм приоритетов. Ведь если он поставит лишние скобки, то вдруг кто-то подумает что он новичок, а не истинный джедай. Я даже встречал на просторах интернета обсуждение, где человек категорично утверждал, что использование лишних скобок - это плохой тон. А если человек не уверен как будет вычислено выражение, то ему надо учиться, а не программы писать. К сожалению, не смог найти эту дискуссию, но я не разделяю подобные точки зрения. Знать приоритеты, конечно, надо, но если в выражении используются разнородные операции, то лучше подстраховывать себя скобками. Это
  • 6. не только защитит от потенциальных ошибок, но и сделает код более легко читаемым для других разработчиков. Ошибки с путаницей приоритетов также возникают не только у новичков, но и у профессионалов. При этом выражение не обязательно должно быть сильно запутанным и длинным. Ошибку можно допустить и в относительно простых выражениях. Рассмотрим некоторые примеры. V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. game g_client.c 1534 #define SVF_CASTAI 0x00000010 char *ClientConnect(...) { ... if ( !ent->r.svFlags & SVF_CASTAI ) { ... } V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. dosbox sdlmain.cpp 519 static SDL_Surface * GFX_SetupSurfaceScaled(Bit32u sdl_flags, Bit32u bpp) { ... if (!sdl.blit.surface || (!sdl.blit.surface->flags&SDL_HWSURFACE)) { ...
  • 7. } Ну и еще один пример из Chromium: V564 The '&' operator is applied to bool type value. You've probably forgotten to include parentheses or intended to use the '&&' operator. base platform_file_win.cc 216 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { ... info->is_directory = file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0; ... } Выражения просты. Разработчики молодцы. А ошибки всё равно есть. Так что использование скобок в скользких местах лишним не будет. Наверное, стоит поступать так. Если операции просты и привычны, то дополнительные скобки не нужны. Примеры: if (A == B && X != Y) if (A - B < Foo() * 2) А вот если используются более редкие операторы (~, ^, &, |, <<, >>, ?:), то явные скобки не повредят. Это и упростит чтение кода и защит от потенциальной ошибки. Примеры: If ( ! (A & B)) x = A | B | (z < 1 ? 2 : 3); Не стесняться использовать скобки при использовании редких операций поможет и для рассмотренного ранее оператора "?:". Как именно лучше поступить с "?:" - дело вкуса. Мне более нравится вариант с упрощением. Заключение Пишите простой и понятный код. Разбивая длинные и сложные выражения на несколько строк, вы получаете более длинный код. Но такой код бывает намного проще понять и прочитать. В нем меньше вероятность допустить ошибку. Не бойтесь завести лишнюю переменную. Компилятор отлично всё оптимизирует. Не жадничайте на скобках в выражениях, где используются редкие операторы или где смешиваются битовые и логические операции.
  • 8. Программист, который в будущем будет читать ваш код со скобками, только скажет спасибо за это.