Честный обзор PVS-Studio обычным программистом


PVS-Studio — программа, которая ищет в исходных кодах проектов на C++, C# ошибки, которые компилятор не видит, но программист в этих местах, скорее всего, накосячил.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image1.png

Примечание. Статья впервые была опубликована на русском языке в блоге blog.harrix.org. Статья и её перевод размещаются на нашем сайте с согласия автора.

Введение

На меня вышли люди из PVS-Studio с вопросом о сотрудничестве. О данной программе наслышан много со страниц Хабрахабра, но никогда этим продуктом не пользовался. И я предложил такой вариант: они мне дают лицензию на продукт, и я проверяю свои программы и пишу обзор по ней: о том как пользовался, как проверял коды и так далее. Они согласились.

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

Второй момент. Еще несколько лет назад был приверженцем функционального программирования, не любил ООП, не использовал пространства имен, напридумывал много велосипедов и так далее. Сейчас тот период вспоминаю как страшный сон и многие свои программы активно переписываю, но проверять там пока мало что есть. Поэтому для проверки возьму те проекты (все есть на GitHub), из того периода. Хоть там и функциональщина правит, но я тщательно подходил к написанию программ, тестированию, документации — я думаю, что серьезных ошибок там быть не должно.

Итак, вперед.

Установка

С установкой проблем не возникло. На главной странице есть большая кнопка 'Скачать', которая ведет на страницу с видной ссылкой на скачивание.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image2.png

Установка абсолютно стандартная, там даже ничего выбирать особо нельзя. Но я в своих статьях всегда стараюсь приводить даже самые понятные шаги. Так что далее скрины:

Установка PVS-Studio

Шаг 1.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image4.png

Шаг 2.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image5.png

Шаг 3.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image6.png

Шаг 4.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image7.png

Шаг 5.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image8.png

Шаг 6.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image9.png

Шаг 7.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image10.png

О том, как ничего не получилось

Сразу говорю, что никакую документацию вначале не читал. Установил программу. Что дальше? В 'Пуске' появились следующие пункты:

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image11.png

Интуиция подсказывает, что нужный пункт совпадает с названием программы. Щелкаем. И тут меня обломали. Выскочило вот такое сообщение:

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image12.png

Если честно, то я сильно напрягся: я-то работаю в основном в Qt, а Visual Studio, скорее, держу как обучающую программу для студентов.

Ладно. Может быть мне поможет другой пункт в меню Standalone?

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image13.png

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

Итак, вначале я попытался открыть какой-нибудь свой файл (меня напряг факт, что можно выбрать только один файл, а не несколько).

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image15.png

Открыл. А дальше что? Никаких больших или ярких кнопок нет.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image16.png

В главном меню нашел только один пункт, который похож на то, что мне нужно:

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image18.png

Нажав на него, появляется окно.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image19.png

Вот тут я сглупил. Я не стал читать этот текст, а сразу стал тыкать кнопки. В Select меня попросили выбрать какие-то *.suppress файлы. Явно не то, что нужно. Глаз зацепился за слово Compiler. Значит, запускаем Start Monitoring.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image20.png

Лично я подумал, что программа ищет компиляторы на компе, так что процесс может быть долгим. И процесс реально оказался долгим (я ждал несколько часов). Но меня радовало, что что-то он начал находить:

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image21.png

Уже потом я выяснил, что дело в том, что я во время мониторинга спокойно работал со своими проектами и их компилировал.

После пары часов я решил, что хватит мне найденных компиляторов, и решил остановить поиск. Но, к сожалению, программа мне ничего не выдала. Что в таком случае делать? Блин, придется читать документацию (

Вот не на самом видном месте оказался нужный мне кусок.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image22.png
https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image24.png
https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image26.png

Но вот после прочтения статьи я наконец-то понял, что мне нужно делать.

О том, как всё получилось

Настоящий принцип работы программы, а не предполагаемый.

Запускаю мониторинг в PVS-Studio, а потом запускаю свой компилятор со своим проектом. После компиляции останавливаю мониторинг и программа через некоторое время выдает мне ошибки в проекте.

Покажу на примере тестового приложения Qt 5.7 под MinGW, которое будет использовать мою библиотеку Harrix MathLibrary.

Открываю раздел анализа.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image18.png

Запускаю мониторинг запусков компиляторов.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image28.png

Работа мониторинга может происходить в фоновом режиме.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image29.png

Запускаю компиляцию проекта:

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image30.png

PVS-Studio нашла запуск нашего компилятора.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image32.png

Останавливаем мониторинг.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image34.png

И PVS-Studio нашла кучу замечаний. Блин. А я-то надеялся ((

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image35.png

Двойной щелчок по ошибке, и программа открывает нам файл исходного кода с ошибкой.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image37.png

Когда наконец понимаешь принцип работы программы, то в дальнейшем всё становится просто. Однако, для новичка это не совсем интуитивно.

Ну а теперь посмотрим, а что за ошибки найдены. Может это и не ошибки вовсе?

Замечание. При запуске компилятора перестраивайте весь проект. Вот только что я расстроился из-за того, что мне нашли 71 замечание. Я очистил проект и полностью перестроил проект. Теперь мне нашли более 1900 замечаний.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image39.png

Вот тут мне уже хочется материться.

Разбор ошибок

Мы рассмотрели путь моего восхождения по использованию программы, поняли, как ею пользоваться. Теперь посмотрим на результат работы программы.

Ошибки, найденные в самом Qt, меня особо не интересуют — это пусть останется на совести соответствующих разработчиков.

https://import.viva64.com/docx/blog/0435_Honest_PVS_Studio_Review_ru/image40.png

Посмотрим где я накосячил.

Подавляющее большинство из более 1900 замечаний — это замечания V550:

V550. An odd precise comparison. It's probably better to use a comparison with defined precision: fabs(A - B) < Epsilon or fabs(A - B) > Epsilon

И в большинстве случаев я склонен с этим согласиться. Например, в данном примере (F[i]==F[i+1]) может возникнуть проблема:

//для одинаковых элементов ранги
//делаем одинаковыми как среднее арифметическое
for (i=0;i<VHML_N-1;i++)
{
if (F[i]==F[i+1])
  {
  j=i+1;
  while ((F[i]==F[j])&&(j<VHML_N)) j++;
  Sn=HML_SumOfArithmeticalProgression(i+1,1,j-i);
  Sn/=double(j-i);
  for (k=0;k<VHML_N;k++)
   if (Fitness[k]==F[i]) VHML_ResultVector[k]=Sn;
  i=j-1;
  }
}

И тем более в страшном коде модели маятника Максвелла крайние положения маятника лучше так не проверять:

//если маятник находится в крайних точках,
if (((x==R)&&(v<0))||((x==l)&&(v>0))) v=-v*(1.-k);

А в следующем коде вылетело следующее предупреждение.

//Найдем среднее арифметические двух выборок
xn=HML_Mean(x,VHML_N);
yn=HML_Mean(x,VHML_N);

V656 Variables 'xn', 'yn' are initialized through the call to the same function. It's probably an error or un-optimized code. Consider inspecting the 'HML_Mean(x, VHML_N)' expression. Check lines: 3712, 3713. harrixmathlibrary.h 3713

Очень досадная ошибка. Судя по всему, скопировал и не всё поменял.

Далее идет еще одна глупая ошибка.

int VHML_Result=0;
    if (VHML_N1==VHML_N2)
        for (int i=0;i<VHML_N1;i++)
            if (a[i]!=b[i]) VHML_Result=-1;
            else
                VHML_Result=-1;

V523 The 'then' statement is equivalent to the 'else' statement. harrixmathlibrary.h 695

Данная функция всегда будет выдавать, что решение есть. Я так и не понял, что мною двигало, чтобы я в конце работы функции перечеркнул все вычисления переменной solutionis.

double HML_LineTwoPoint(double x, double x1, double y1,
                        double x2, double y2, int *solutionis)
{
/*
Функция представляет собой уравнение прямой по двум точкам.
Возвращается значение y для x.
Входные параметры:
 x - значение точки для которой считаем значение прямой;
 x1 - абцисса первой точки;
 y1 - ордината первой точки;
 x2 - абцисса второй точки;
 y2 - ордината второй точки;
 solutionis - сюда возвращается результат решения задачи:
  0 - решения нет;
  1 - решение есть;
  2 - любое число является решением (прямая параллельна оси Oy).
Возвращаемое значение:
 Значение y прямой для данного x.
*/
double y=0;
 
if ((x1==x2)&&(y1==y2))
{
  //это одна и та же точка, так что выдадим любое решение
  y=y1;
  *solutionis=2;
}
else
{
  if (y1==y2)
  {
    // это прямая параллельна оси Ox
    y=y1;
    *solutionis=1;
  }
  else
  {
    if (x1==x2)
    {
      //это прямая параллельная оси Oy
      if (x==x1)
      {
        y=y1;
        *solutionis=2;
      }
      else
      {
        y=0;
        *solutionis=0;
      }
    }
    else
    {
      y=(x-x1)*(y2-y1)/(x2-x1)+y1;
    }
  }
}
 
*solutionis=1;
return y;
}

V519 The '* solutionis' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 1788, 1821. harrixmathlibrary.cpp 1821

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

if (VHML_N>0) VHML_Result=0;
 
...
 
//Посчитаем значение целевой функции вещественного вектора
VHML_Result=VHML_TempFunction(VHML_TempDouble3,RealLength);
 
return VHML_Result;

V519 The 'VHML_Result' variable is assigned values twice successively. Perhaps this is a mistake. Check lines: 385, 395. harrixmathlibrary.cpp 395

PVS-Studio также нашла одинаковые функции в моем коде (тогда я еще не любил std). Кроме этих, он нашел еще несколько одинаковых функций. А это крайне полезно, если проект большой и в нем много функций. Не всегда вспомнишь: а такая функция была или нет.

template <class T> void HML_Swap(T &a, T &b)
{
/*
Функция меняет местами значения двух чисел.
Входные параметры:
a - первое число;
b - второе число.
Возвращаемое значение:
Отсутствует.
*/
T x;
x = b;
b = a;
a = x;
}
 
template <class T> void HML_NumberInterchange(T &a, T &b)
{
/*
Функция меняет местами значения двух чисел.
Входные параметры:
a - первое число;
b - второе число.
Возвращаемое значение:
Отсутствует.
*/
T x;
x = b;
b = a;
a = x;
}

V524 It is odd that the body of 'HML_Swap' function is fully equivalent to the body of 'HML_NumberInterchange' function. harrixmathlibrary.h 2349

А тут классическая ошибка отсутствия приведения типов.

double HML_TestFunction_HyperEllipsoid(double *x, int VHML_N)
{
/*
Функция многих переменных: Гипер-эллипсоид.
Тестовая функция вещественной оптимизации.
Входные параметры:
x - указатель на исходный массив;
VHML_N - размер массива x.
Возвращаемое значение:
Значение тестовой функции в точке x.
*/
double VHML_Result=0;
 
for (int i=0;i<VHML_N;i++)
VHML_Result += (i+1)*(i+1)*x[i]*x[i];
 
return VHML_Result;
}

V636 The '(i + 1) * (i + 1)' expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid overflow. An example: double A = (double)(X) * Y;. harrixmathlibrary.cpp 10509

А вот тут программа выдала ошибочное предупреждение, так как HML_ProportionalSelectionV2 возвращает случайное значение.

NumberOfParent1=HML_ProportionalSelectionV2(....);
NumberOfParent2=HML_ProportionalSelectionV2(....);

V656 Variables 'NumberOfParent1', 'NumberOfParent2' are initialized through the call to the same function. It's probably an error or un-optimized code. Check lines: 1106, 1107. harrixmathlibrary.cpp 1107

В библиотеке Harrix QtLibrary было также найдено несколько замечаний.

Например, там была функция разбиения строки на слоги. И там хорошая подсказка вылезла, что условия неплохо было бы объединить.

//"Х-"
if ((i>=1)&&(i!=N-1))
{
  if ((HQt_GetTypeCharRus(S.at(i-1))==3) &&
     (HQt_GetTypeCharRus(S.at(i))!=0)    &&
     (HQt_GetTypeCharRus(S.at(i+1))!=0))
    cut=true;
}
 
//"Г-Г"
if ((i>=1)&&(i!=N-1))
{
  if ((HQt_GetTypeCharRus(S.at(i-1))==1) &&
     (HQt_GetTypeCharRus(S.at(i))==1)    &&
     (HQt_GetTypeCharRus(S.at(i+1))!=0))
    cut=true;
}

V581 The conditional expressions of the 'if' operators situated alongside each other are identical. Check lines: 1140, 1147. harrixqtlibrary.cpp 1147

А в цикле из нижеследующего куска кода булевская переменная in всегда будет равна true.

 int VHQt_Result = -1;
    bool in=false;
    int i=0;
 
    while ((i<StringList.count())&&(in!=true))
    {
        if (StringList.at(i)==String)
            VHQt_Result=i;
        i++;
    }
   return VHQt_Result;

V560 A part of conditional expression is always true: (in != true). harrixqtlibrary.cpp 2342

Встречаются случаи, когда в заполнении модели элементами встречаются повторы:

item = new QStandardItem(QString("HML_RealGeneticAlgorith...."));
model->appendRow(item);
 
item = new QStandardItem(QString("HML_RealGeneticAlgorith...."));
model->appendRow(item);

V760 Two identical blocks of text were found. The second block begins from line 86. mainwindow.cpp 83

Вердикт

Минусы:

  • Программа не интуитивно понятна. Сходу понять, как с ней работать, сложно. Если бы я просто зашел к ним на сайт, скачал пробную версию и решил бы посмотреть, что это за зверь, то я, скорее всего, не разобравшись, удалил бы программу.
  • 'Старомодный' дизайн приложения.
  • Подсветка синтаксиса похожа на таковую в Notepad++ (и это хорошо). Но, например, я привык, что в Notepad++ при выделении слова выделяются такие же слова, а при выделении открывающейся скобки подсвечивается закрывающаяся.

Плюсы:

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

Общий итог: данная программа относится к разряду must have. Очень удобный инструмент по контролю своего кода.

P.S. А я надеялся, что ошибок не будет (

P.S.S. 1900 с лишим замечаний!



Найдите ошибки в своем C, C++, C# и Java коде

Предлагаем попробовать проверить код вашего проекта с помощью анализатора кода PVS-Studio. Одна найденная в нём ошибка скажет вам о пользе методологии статического анализа кода больше, чем десяток статей.

goto PVS-Studio;



Найденные ошибки

Проверено проектов
412
Собрано ошибок
14 132

А ты совершаешь ошибки в коде?

Проверь с помощью
PVS-Studio

Статический анализ
кода для C, C++, C#
и Java

goto PVS-Studio;
Этот сайт использует куки и другие технологии, чтобы предоставить вам более персонализированный опыт. Продолжая просмотр страниц нашего веб-сайта, вы принимаете условия использования этих файлов. Если вы не хотите, чтобы ваши данные обрабатывались, пожалуйста, покиньте данный сайт. Подробнее →
Принять