Comments 46
Не совсем понятно почему упор сделан именно на ошибки с C/C++. Это достаточно распространенный синтаксис и встречается во многих языках.
+2
Ага, сам постоянно туплю, когда одно из условий с отрицанием (когда оба, тогда проще — надо составить условие без отрицаний, а потом || на && и наоборот заменить). Правда == && == у себя в ошибках не встречал.
0
Видимо потому что PVS-studio чаще всего C\C++ проекты проверяет открытые. А так да, проблема с операторами думаю для всех языков актуальна.
+1
По поводу тернарного оператора — уже давно вошло в привычку брать его в скобки. И условие в свои отдельные скобки. ИМХО — это должно войти в привычку у многих. Иначе от граблей из-за различных приоритетов долго будут страдать все…
И еще — когда идут сложные условия ветвления (больше 1), рекомендуется хоть как-то составить таблицу истинности — никто не застрахован от ошибок, а так за счет пары лишних минут можно прилично уменьшить вероятность создания бага.
И еще — когда идут сложные условия ветвления (больше 1), рекомендуется хоть как-то составить таблицу истинности — никто не застрахован от ошибок, а так за счет пары лишних минут можно прилично уменьшить вероятность создания бага.
+5
UFO just landed and posted this here
if( (pSymDef->GetType() != SbxEMPTY) ||
(pSymDef->GetType() != SbxNULL) )
Наличие скобок в таких примерах не сильно помогает не допустить ошибку. С приоритетом операций в этом примере тоже всё понятно. Я хотел сделать акцент именно на необычные комбинации трёх операторов. Нужно просто запомнить, что такие ситуации бывают.
+6
Жаль, что не было акцента на двух правильных вариантах. Ведь если условное выражение не совпадает с одним из этих вариантов, то значит оно неверно составлено.
0
Вы правильно сказали, что если условное выражение совпадает с одним из этих вариантов, то, скорее всего, оно составлено неверно.
А вот о правильных вариантах почти невозможно рассуждать, потому что у каждого будет своя логика в программе.
Например, в условии ниже есть ошибка — оно всегда истинно:
Сделав замену оператора || на && мы получим такое условие:
которое будет истинно для любых значений err, не равных code1 и code2. В этом синтетическом примере отсутствует исходная логическая ошибка, но мы не можем утверждать, что это будет правильной правкой в каждом конкретном проекте. А условия, которые не зависят от части логического подвыражения, непонятно даже как исправить на синтетическом примере, не то, что в реальном коде.
А вот о правильных вариантах почти невозможно рассуждать, потому что у каждого будет своя логика в программе.
Например, в условии ниже есть ошибка — оно всегда истинно:
if ( err != code1 || err != code2)
{
....
}
Сделав замену оператора || на && мы получим такое условие:
if ( err != code1 && err != code2)
{
....
}
которое будет истинно для любых значений err, не равных code1 и code2. В этом синтетическом примере отсутствует исходная логическая ошибка, но мы не можем утверждать, что это будет правильной правкой в каждом конкретном проекте. А условия, которые не зависят от части логического подвыражения, непонятно даже как исправить на синтетическом примере, не то, что в реальном коде.
0
Я несколько о другом говорил. Не знаю, как у всех, но части людей нельзя говорить про то, как делать нельзя, но не говорить про то, как делать можно (хотя оно логически вытекает из запрета). Простой пример: мать посылает сына в магазин за хлебом и говорит: «Купи любой, только батон нарезной не покупай». И пока он собирается и одевается, постоянно напоминает — «не покупай нарезной». Приходит сын в магазин и вспоминает — «Какой хлеб купить надо было? А! Мама постоянно про батон нарезной говорила, куплю его.»
Я не рассуждаю о том, что два оставшихся варианта всегда правильны. Но статья нам доказывает, что перечисленные четыре — всегда избыточны в количестве условий. Поэтому, если наше условное выражение не подходит под != && != или == || ==, то оно заведомо содержит в себе лишние условия.
Кому как, но мне проще запомнить два верных в большинстве случаев выражения, чем запоминать четыре, которые содержат лишнее условие.
Я не рассуждаю о том, что два оставшихся варианта всегда правильны. Но статья нам доказывает, что перечисленные четыре — всегда избыточны в количестве условий. Поэтому, если наше условное выражение не подходит под != && != или == || ==, то оно заведомо содержит в себе лишние условия.
Кому как, но мне проще запомнить два верных в большинстве случаев выражения, чем запоминать четыре, которые содержат лишнее условие.
0
UFO just landed and posted this here
UFO just landed and posted this here
Не понял пассажа про ошибки и ВУЗ, поясните, пожалуйста.
+5
UFO just landed and posted this here
Эмм, а что это за поток сознания? Где кому какие ошибки в вузе внушают\развивают?
+5
UFO just landed and posted this here
Осталось узнать, как оно работает при таком коде.
0
Defensive programming. То, что не отловится одной проверкой заметит другая… скорее всего.
С одной стороны даёт возможность создавать современных монстров на сотни миллионов строк кода, а с другой — гарантирует что дыры в безопасности будут «лататься» вечно.
Чтобы дыр не было нужно писать fail-fast/crash-only software и/или использовать GIGO (тогда проверок в системе остаётся мало и каждую из них можно хорошо обдумать), но это замедляет разработку кода, потому так никто не делает…
С одной стороны даёт возможность создавать современных монстров на сотни миллионов строк кода, а с другой — гарантирует что дыры в безопасности будут «лататься» вечно.
Чтобы дыр не было нужно писать fail-fast/crash-only software и/или использовать GIGO (тогда проверок в системе остаётся мало и каждую из них можно хорошо обдумать), но это замедляет разработку кода, потому так никто не делает…
+1
Надо бы ещё добавить проверку флагов. Тоже большой источник ошибок.
0
«Подстраховать себя от таких ошибок можно путём проверки своего кода с помощью построения таблиц истинности.»
Есть еще один метод — выучить, наконец, законы де Моргана.
Есть еще один метод — выучить, наконец, законы де Моргана.
+8
#include <iostream>
enum ERROR{
code1 = 0,
code2 = 1,
code3 = 2
};
int main(int argc, char const *argv[])
{
ERROR err = code3;
if ( err == code1 || err == code2)
{
std::cout<< "err equal code1 or code2: "<<err<<std::endl;
} else {
std::cout<< "err is: "<<err<<std::endl;
}
return 0;
}
g++ log.cpp
./a.out
err is: 2
Логическое условие выполняется, как и задумано. Т.е. выполняется ветвь кода, когда ERROR err = code3
+1
Это вы к чему написали? В вашем случае ошибки нет.
+3
Т.е. в таких ситуациях писать switch безопаснее. Но длиннее…
Ещё можно было напомнить, что, когда компилятор не вычисляет правую часть выражения, при определённых условиях в || и &&
Ещё можно было напомнить, что, когда компилятор не вычисляет правую часть выражения, при определённых условиях в || и &&
-2
В случае, если заменить условие
if ( err != code1 || err != code2)
Компилятор вычислив левую часть выражения и получив true — правую (после оператора || )вычислять уже не будет, и вывод программы будет ошибочным
if ( err != code1 || err != code2)
Компилятор вычислив левую часть выражения и получив true — правую (после оператора || )вычислять уже не будет, и вывод программы будет ошибочным
#include <iostream>
enum ERROR{
code1 = 0,
code2 = 1,
code3 = 2
};
int main(int argc, char const *argv[])
{
ERROR err = code3;
if ( err != code1 || err != code2)
{
std::cout<< "err equal code1 or code2: "<<err<<std::endl;
} else {
std::cout<< "err is: "<<err<<std::endl;
}
return 0;
}
g++ log.cpp
./a.out
err equal code1 or code2: 2
0
В предыдущем комментарии забыл поправить иходник.
Тут видя false в первой половине, компилятор вычисляет вторую и она оказывается true — при || итог всего условия true, хотя err == code1
Тут видя false в первой половине, компилятор вычисляет вторую и она оказывается true — при || итог всего условия true, хотя err == code1
#include <iostream>
enum ERROR{
code1 = 0,
code2 = 1,
code3 = 2
};
int main(int argc, char const *argv[])
{
ERROR err = code1;
if ( err != code1 || err != code2)
{
std::cout<< "err isn't equal code1 or code2: "<<err<<std::endl;
} else {
std::cout<< "err is: "<<err<<std::endl;
}
return 0;
}
g++ log.cpp
./a.out
err isn't equal code1 or code2: 0
-1
А еще наверное частая ошибка когда без скобочек используют маски и сравнения вроде if ( a & 3 == b & 3), нет?
0
А эта проверка только для простых типов проводится, или для всех? А то с C++-ной возможностью перегрузить операторы можно какое угодно поведение получить, и описанные случаи окажутся вполне корректными. Например, если делается интерпретатор/интероп для некоего слаботипизированного языка программирования, где "" == 0 && "" == false – true. Или eDSL с компактным синтаксисом, вроде boost::spirit, где старые операторы наделяют другой семантикой из-за невозможности добавлять новые.
+1
Анализатор ищет неправильные конструкции, какие были приведены в примерах. Но как и во всех диагностиках, для правил V547 и V590 реализован ряд своих исключений, которые позволяют избавить диагностику от ложных срабатываний. Я сейчас не вспомню все исключения, тем более для двух правил. Скорее всего, благодаря им я не встречал ложные срабатывания в описанных вами ситуациях, зато встречал много ошибок на простом коде.
0
Что также может быть интересно — для пользовательских операторов не работает short-circuit-поведение.
+1
Если речь идет не о элементарных типах, а о классах, оператор сравнения для которых может быть перегружен, то конструкция
if (en_RenderType==RT_BRUSH &&
en_RenderType==RT_FIELDBRUSH)
Может быть не ложной.
if (en_RenderType==RT_BRUSH &&
en_RenderType==RT_FIELDBRUSH)
Может быть не ложной.
0
Есть много способов выстрелить себе в ногу. Есть целые языки, устроенные так, чтобы люди, их использующие страдали до тех пор, пока не наберутся опыта и не перейдут на что-то менее ужасное. Но… Зачем? Равенство должно быть отношением эквиватентности, незачем из него делать невесть что.
+3
Лучше сразу пристрелите автора, чтоб не мучился и не мучил других.
+2
UFO just landed and posted this here
понятно, что в
if (err == ERR_OK || err != ERR_PENDING)…
можно спокойно убрать кусок с ERR_OK, но КМК иногда это может быть «документацией», что обрабатываем, например, «хороший» и «плохие» сценарии, а «злой», в смысле асинхронный, будет, например, где-то ещё.
Что в общем не должно мешать оторвать руки человеку, придумавшему такой API. 8-)
if (err == ERR_OK || err != ERR_PENDING)…
можно спокойно убрать кусок с ERR_OK, но КМК иногда это может быть «документацией», что обрабатываем, например, «хороший» и «плохие» сценарии, а «злой», в смысле асинхронный, будет, например, где-то ещё.
Что в общем не должно мешать оторвать руки человеку, придумавшему такой API. 8-)
0
3 и 4 это ошибки. 1 и 2 нет.
!= || !=
== || !=
Оба случая — может достаточно наличия/отсутствия одного из двух признаков. Не согласен совершенно с автором статьи.
!= || !=
== || !=
Оба случая — может достаточно наличия/отсутствия одного из двух признаков. Не согласен совершенно с автором статьи.
0
Sign up to leave a comment.
Логические выражения в C/C++. Как ошибаются профессионалы