PVS-Studio corporate blog
Open source
Programming
C++
C
Comments 46
+8
М--даааа… Это что, кто-то #define true false забабахал уходя?!
Как, ну КААК иначе такие дефайны появляются, блин?
+5
Напомнило недавнее из javascript
function if (x) {return true;}

Здесь после if неразрывный пробел  , который будет считаться частью идентификатора, плюс любовь js к автоматической расстановке точек с запятой, что позволяет этому работать.
Конечно, не так злобно, как в примере в статье, зато если хочется нагадить — можно применять избирательно, только по особо сложным местам.
+3
Пример с CryEngine некорректен. Там, очевидно, всё правильно — новый элемент добавляется в конец односвязного списка. Цикл ищет последний элемент, довольно рапространённый паттерн. Предложенное вами «исправление» не имеет смысла.
+1
Только я не предлагал 'исправлений'. :)
Возможно, недостоточно ясно выразился в статье.

В коде CryEngine форматирование поправили, к слову.
+1
Если всё правильно, то форматирование плохое. Отступ намекает, что оператор принадлежит циклу, а если всё правильно, то это не так.
-1
Больше половины ошибок GCC выдаст в режиме -Wall -Wextra.

Ошибку с sprintf, я думаю, он тоже покажет. Да даже если не покажет, то тут уж надо разуть глаза и посмотреть на код в IDE. Макросы подсвечиваются другим цветом, нежели функции. Ну, а если ты сделал одним цветом, то, как обычно, ССЗБ.
+7
Прошу продемонстрировать. Очень часто слышу про "-Wall -Wextra", но никто из авторов не удосуживается попробовать проверить свои предположения про магическую силу gcc/clang :).
0
Хорошо, одна есть. Что касаемо «больше половины ошибок GCC выдаст в режиме ....»? :)
0
Это не моё утверждение. Я просто показал, что, при желании, компилятор кое-что может отловить.

Естественно тюнинг флагов компилятора под проект (чтобы не втыкать -Weverything) — это отдельная задача.
0
На if(); gcc ругается, а вот на for(); нет. Да, не много чего он ловит, но и на том спасибо (у меня была такая ошибка, а еще if(a=b)).
+1
Можно уточнить, а какое количество всего варнингов данный режим компилирования выдаст в сумме для протестированных исходников, чтоб реально понимать, можно ли там человеку будет увидеть конкретно эту ошибку?
-1
Можно не только уточнить, но и самостоятельно проверить. Заодно и сравнить с тем, сколько на указанных исходниках будет сообщений от, скажем, PVS-Studio, Clang Static Analyzer.

Любопытно будет прочитать про результаты такого теста и сравнения.
0
У второго места (ошибка в Unreal Engine) ещё перепутаны местами два последних аргумента в вызове, судя по их названиям. Впрочем, на работу функции это влияния не оказывает.

Объявление и вызов соответственно:
static void CreateProjectSet(… int32 OutCreatedProjects, int32 OutMatchedProjects);
GameProjectAutomationUtils::CreateProjectSet(… OutMatchedProjectsMob, OutCreatedProjectsMob);
UFO landed and left these words here
0
А в примере из FreeBSD (седьмое место) действительно «повезло», что где-то выше объявлена переменная out, и вот эта строка
mlx5_cmd_exec(dev, &din, sizeof(din), &out, sizeof(dout));

компилируется без ошибки?
UFO landed and left these words here
0
Тогда прошу вас выкинуть всю технику из дома имеющую больше 3 кнопок, ведь её мозги тоже написаны на крестах
0

В коде, который на пятом месте, IDE должна была подсветить неиспользуемую переменную. И банальный линтер в pre-commit hook выловил бы такое. Хорошо хоть, что разработчики решили использовать статический анализатор (как я понимаю, постфактум, то есть, на коде из репозитория).

0
for(int i = 0; i < SelectedObjects.Num(); ++i)
{
  UObject* Obj = SelectedObjects[0].Get();
  EdObj = Cast<UEditorSkeletonNotifyObj>(Obj);
  if(EdObj)
  {
    break;
  }
}

А где здесь неиспользуемая переменная?
0

Извиняюсь, протупил: i не используется только в теле цикла, но используется в объявлении.

0
executeForNullThenElse

На первый взгляд какой-то вывих мозга. Но полез в гит… И мозг свихнулся окончательно :) Особенно вот тут:

if (cond_col)
        {
            if (!( executeLeftType<UInt8>(cond_col, block, arguments, result)
                || executeLeftType<UInt16>(cond_col, block, arguments, result)
                || executeLeftType<UInt32>(cond_col, block, arguments, result)
                || executeLeftType<UInt64>(cond_col, block, arguments, result)
                || executeLeftType<Int8>(cond_col, block, arguments, result)
                || executeLeftType<Int16>(cond_col, block, arguments, result)
                || executeLeftType<Int32>(cond_col, block, arguments, result)
                || executeLeftType<Int64>(cond_col, block, arguments, result)
                || executeLeftType<Float32>(cond_col, block, arguments, result)
                || executeLeftType<Float64>(cond_col, block, arguments, result)
                || executeString(cond_col, block, arguments, result)
                || executeGenericArray(cond_col, block, arguments, result)
                || executeTuple(block, arguments, result)))
                throw Exception("Illegal columns " + arg_then.column->getName()
                    + " and " + arg_else.column->getName()
                    + " of second (then) and third (else) arguments of function " + getName(),
                    ErrorCodes::ILLEGAL_COLUMN);
        }
0
Шутка с «не переходите к описанию проблемы», всё же, не очень хороша — кто-нибудь с хорошим знанием синтаксиса С++ может и спятить, как географ, не нашедший на карте Берингова пролива.
-1

И после этого кто-то говорит о том, что Rust не нужен… 8 из 10 таких ошибок в Rust просто не скомпилируются.

0
8 из 10 приведенных участков написаны на диалекте с++, который старше раста
0

То есть в современном C++ компиляция данного кода упадёт с ошибкой? Или вы имеете ввиду "старость надо уважать"?

0
Может и не собраться, да. Но суть скорее в размере кодовой базы которую нельзя просто взять и RIIRнуть
0
сегодня на с++ так уже не пишут, а когда писали — не было раста. Код на современном с++ в свою очередь менее подвержен ошибкам.
UFO landed and left these words here
UFO landed and left these words here
0

Не согласен по поводу CryEngine.


  for(pmd0=m_pMeshUpdate; pmd0->next; pmd0=pmd0->next);
    pmd0->next = pmd;

Очевидно тут ищется конец списка и вставляется новый элемент в конец. В противном случае получится бесконечный цикл, поскольку после первого прохода pmd0 = pmd0->next <=> pmd0 = pmd, затем второй проход в теле цикла pmd0->next = pmd <=> pmd->next = pmd, привет infinite loop.


Так что это баг автоматического форматирования, а не программиста.

0
Наверное, я отстал от моды, но как такое возможно?
sprintf — это макрос, который раскрывается в (!) std::printf!

Вот пишут, что, как и раньше,
int sprintf ( char * str, const char * format,… );

Иначе весь «legacy»-код можно выбросить на помойку. Разве нет?
-1
Разумеется.
Но кто, где и зачем такое написал, мне непонятно?
Или это где-то в том же исходнике было, но автор умолчал?
Что-то в стиле
#define TRUE FALSE

+2
Это define из исходников.
Мне показалось это очевидным, поэтому я не стал писать этот факт прямым текстом.
0
Подозреваю, что в примере с mlx5_core_create_qp() просто опечатка при вызове mlx5_cmd_exec(): если предпоследний аргумент &dout, всё встаёт на свои места (и sizeof(dout)). Иначе зачем dout, вообще, нужен?
Приведённого куска кода явно недостаточно, чтобы сделать правильный вывод…
0
mlx5_core_create_qp()…
т.к. подобная оптимизация не влияет на поведение программы с точки зрения C/C++

Да ну, не может быть. Откуда компилятору знать, что memset не имеет побочных эффектов? Это можно понять только в рантайме. А если memset'у скормить адрес переменной из стека, а размер указать побольше, что бы записалась и следующая? А её уже использовать в коде? Понятно, что это страшный костыль, и того кто так делает к клавиатуре вообще подпускать нельзя, но тем не менее, так сделать можно. И компилятор не имеет права предполагать отстутствие побочных эффектов.
0
Ну behavior, к сожалению, вполне себе defined, если известна целевая архитектура, а компилятору она известна. Иначе не работали бы так любимые всеми атаки на переполнение стекового буфера. Но не суть. Даже если и undefined — это все равно побочный эффект. А раз вызов функции может иметь побочные эффекты — удалять вызов нельзя. Компилятор может определить отсутствие побочных эффектов по синтаксическому дереву функции. Очевидно, что если, например, это const функция, внутри которой нет ничего подозрительного, типа обращения к mutable членам, или вызова внешних функций, то вызов можно вырезать. Но в случае memset, очевидно, присутствуют команды записи данных по адресу. Что бы понять, что такую запись можно удалить, компилятор по сути должен понять, что не важно, была сделана запись, или нет, программа дальше будет работать одинаково. Т.е. надо понимать, что после записи, этот адрес обязательно будет еще раз перезаписан, до попытки повторно из него прочитать. А еще есть такие вещи, как отображаемые в память файлы, порты ввода вывода. Т.е. если есть только код, который в память только пишет, но нет кода, который из неё обратно читает, это еще не значит, что запись можно не делать. Задача определения отсутствия побочных эффектов при наличии операций записи по адресу — очень крутая, в complie time её решить крайне трудно.
0
Это не так работает. Компилятор делает оптимизации исходя из того, что код написан правильно и UB не может возникнуть ни при каких входных данных.
То есть если написан memset локальной переменной, которая потом не читается, а размер приходит извне, то такой memset можно удалить.
0

Пардон, но:


#define sprintf std::printf

это в чьём коде? Без этих данных решить задачу нет возможности. Тем более, что в Gcc актуальных версий такого определения в cstdio нет, там сделано так:


#undef sprintf
namespace std
{
  using ::sprintf;
}
0
видимо, этот дефайн был добавлен для отладки и его забыли убрать
0

Просто если лезть в статью, то как быть с пожеланием:


Ни за что не переходите к описанию проблемы, пока не найдёте ошибку в приведённом фрагменте кода.

;-)

+2
Правильное пожелание, для демонстрации того, что ошибка может быть в другом месте, а конкретное место будет казаться правильным на 100%…
Они ж демонстрируют возможности анализатора.
Only those users with full accounts are able to leave comments. , please.