﻿# Почему студентам нужен анализатор кода CppCat

CppCat – это простой статический анализатор кода для поиска ошибок в программах на языке Си/Си\+\+\. Мы начали выдавать бесплатные академические лицензии всем желающим \(студентам, преподавателям и так далее\)\. Для большей популяризации CppCat среди студентов я решил написать эту заметку об ошибках, которые можно найти в лабораторных работах, встречающихся на сайте Pastebin\.com\.


> К сожалению, мы больше не развиваем и не поддерживаем проект CppCat\\\. Вы можете почитать \[здесь\]\(https://pvs\-studio\.ru/ru/blog/posts/0320/\) о причинах\\\.

![0295_Why_Students_Need_the_CppCat_Code_Analyzer_ru/image1.png](https://import.viva64.com/docx/blog/0295_Why_Students_Need_the_CppCat_Code_Analyzer_ru/image1.png)

## Чуть\-чуть про CppCat

[CppCat](https://pvs-studio.ru/ru/cppcat/) – статический анализатор кода, интегрирующийся в среду Visual Studio и позволяющий найти множество опечаток и прочих ошибок ещё на этапе кодирования\. Анализатор умеет запускаться автоматически после компиляции и проверять только что написанный код\. Анализатор поддерживает C, C\+\+, C\+\+/CLI, C\+\+/CX\.

О том, как получить бесплатную лицензию на CppCat, описано в статье: [Бесплатный CppCat для студентов](https://pvs-studio.ru/ru/blog/posts/0290/)\. Хочу добавить, что мы даём лицензию не только студентам, но и аспирантам, преподавателям и так далее\.

Многих расстраивает, что CppCat не интегрируется в бесплатную среду Visual Studio Express\. К сожалению, мы не можем ничего с этим поделать\. Express\-версии Visual Studio не поддерживают модули расширений \(plugins\)\. Однако, это не беда\. Хочется напомнить, что студенты имеют доступ к [Microsoft DreamSpark](http://mif.vspu.ru/15438) и могут получить доступ к Visual Studio Professional\.

## Завлечение студентов

В начале хотел написать что\-то в духе:

Даже студент может получить пользу от статического анализа\. Зачем долго и мучительно искать ошибку в своей программе, если о ней может подсказать CppCat? Таким образом можно не только быстрее найти ошибку, но ещё узнать подробности о том, как делать не надо\. Документация к CppCat подробно объясняет каждую диагностику и приводит различные примеры ошибок, подсказывает, как их исправлять\.

Потом решил, что как\-то это натянуто\. Ну какие у студентов серьезные ошибки? Можно и поотлаживать цикл из 10 итераций\. Это будет даже полезно\. Поэтому я переформулирую рекомендацию использовать CppCat следующим образом:

Работодатель будет ценить не только ваше умение программировать и писать хитрые алгоритмы\. Не менее важно уметь пользоваться базовым инструментарием\.

Грош цена алгоритму, если он будет потерян из\-за того, что вы не знаете, что такое система контроля версий и максимум, что предпринимали – делали копию исходных кодов на флешку\.

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

Я не берусь составлять список наиболее важного инструментария\. Однако важно знать хотя бы одну систему контроля версий, зачем может быть нужен WinMerge, что такое профайлер, как сделать дистрибутив и так далее\.

Одной из технологий, которую хорошо знать и можно упомянуть в резюме, является статический анализ кода\. CppCat отлично подходит для знакомства с методологией статического анализа\. Это будет хорошим плюсом к вашим знаниям и намекнёт работодателю, что вы что\-то слышали о качестве кода\.

## А теперь то, ради чего мы здесь собрались

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

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

У Вас может возникнуть вопрос: откуда я взял лабораторные работы? Отвечаю\.

Есть сайт [Pastebin\.com](https://pastebin.com/), где разработчики могут удобно обмениваться фрагментами кода\. Студенты активно используют этот сайт\. Почти весь код, имеющий тег C\+\+, представляет собой какую\-то лабораторную работу или её часть\.

Мы написали программу, которая следит за сайтом Pastebin\.com и выкачивает оттуда свежие файлы, помеченные как "код C\+\+"\. Насобирав более двух тысяч файлов, я сделал из них проект в Visual Studio и проверил его\. Больше половины, конечно, проверить не удалось\. Во многих файлах только фрагменты кода, вписан текст, не являющийся комментарием, не хватает каких\-то библиотек и так далее\. Однако я и не ставил целью проверить как можно больше кода, опубликованного на Pastebin\.com\. То, что проверилось, мне вполне хватит для этой статьи\. Сбор файлов продолжается и, возможно, я потом напишу ещё что\-то на эту тему\.

## Типовые ошибки студентов, изучающих Си\+\+

Я просмотрел не так уж много ошибок в лабораторных работах\. Так что пока могу выделить только 3 паттерна\.

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

P\.S\. Многие из опубликованных примеров были с ограничением по времени и уже недоступны\. Поэтому ссылки на удалённые страницы давать не буду\.

### Паттерн 1\. Третье место по популярности\. Путаница в однотипных условиях

Многие задачи по программированию подразумевают проверку многих условий\. И, реализуя их, легко запутаться или опечататься\. Показательный пример:

```cpp
int main()
{
  int n,a,b,c;
  cin >> n;
  for(int i=0;i<n;i++)
  {
    cin >> a >> b >> c;
    if((a % 2==0 && b % 2 ==0 && c % 2!=0)||
       (a % 2==0 && b % 2!=0 && c % 2==0)||
       (a % 2!=0 && b % 2==0 && c % 2==0)||
       (a % 2!=0 && b % 2 !=0 && c % 2==0)||
       (a % 2==0 && b % 2!=0 && c % 2!=0)||   // <=
       (a % 2==0 && b % 2!=0 && c % 2!=0))    // <=
    {
      cout << "1";
    }
    else
      cout << "2";
  }
  cout << endl;
  return 0;
}
```

Предупреждение CppCat: V501 There are identical sub\-expressions '\(a % 2 \=\= 0 && b % 2 \!\= 0 && c % 2 \!\= 0\)' to the left and to the right of the '\|\|' operator\. jtzrihcg\.cpp 14

Надо было как\-то хитро проверить значения трёх введенных переменных\. Скорее всего, код копировался и не везде был правильно поправлен\. В результате предпоследняя и последняя строка в условии совпадают\.

Ещё пример:

```cpp
int main() {
  ....
  } else if(gesucht < geraten) {
    puts("Ein bisschen zu klein");
  } else if (gesucht < geraten) {
    puts("Ein bisschen zu gross");
  }
  ....
}
```

V517 The use of 'if \(A\) \{\.\.\.\} else if \(A\) \{\.\.\.\}' pattern was detected\. There is a probability of logical error presence\. Check lines: 41, 43\. wrgkuuzr\.cpp 40

Два раза выполняется проверка \(gesucht < geraten\), но при этом должны выводиться разные строки\.

Кстати, в обоих примерах ошибка находится в последней строке\. Опять нам встретился "[эффект последней строки](https://pvs-studio.ru/ru/blog/posts/cpp/0260/)"\.

### Паттерн 2\. Второе место по популярности\. Выход за границу массива на 1 элемент

То, что элементы массивов нумеруются в языке C\+\+ от нуля, является большим затруднением при его изучении\. Т\.е\. вроде понять это просто, но вот научиться не выходить за границу массива очень сложно\. Если нужен 10 элемент массива, то так и хочется написать A\[10\]\. Пример:

```cpp
int main()
{
  ....
  int rodnecs[10];
  ....
  VelPol1 = rodnecs[1] + rodnecs[3] + rodnecs[5] +
            rodnecs[8] + rodnecs[10];
  ....
}
```

Предупреждение CppCat: V557 Array overrun is possible\. The '10' index is pointing beyond array bound\. 0z3x9b3i\.cpp 38

Ещё:

```cpp
void main()
{
  ....
  double pop[3][3];
  ....
  for (int i = 0; i<3; i++)
  {
    calc_y[i] = F(pop[i][1], pop[i][2], pop[i][3], x[i]);
  }
  ....
}
```

Предупреждение CppCat: V557 Array overrun is possible\. The '3' index is pointing beyond array bound\. 1uj9v9xs\.cpp 48

Много неправильных сравнений в условиях циклов:

```cpp
int main()
{
  int i,pinakas[20],temp,temp2,max,min,sum=0;
  for (i=1;i<=20;i++)
  {
    pinakas[i]=rand();
  ......
}
```

Предупреждение CppCat: V557 Array overrun is possible\. The value of 'i' index could reach 20\. 287ep6c0\.cpp 20

Очень много:

```cpp
int main()
{
  const int arraySize = 10;
  int a[arraySize];
  int key,index,to_do = arraySize - 1;
  bool did_swap = true;

  srand(time(NULL));
  for (int i = 0; i <= arraySize; i++)
  {
    //generating random number between 1 - 100
    a[i] = rand() % 100 + 1;
  }
  ....
}
```

Предупреждение CppCat: V557 Array overrun is possible\. The value of 'i' index could reach 10\. wgk1lx3u\.cpp 18

Остальные ошибки аналогичны приведённым выше, так что закончим\.

### Паттерн 3\. Первое место по популярности\. Неинициализированные переменные

О\! Я, кажется, понял, почему, кого не спроси, одной из наиболее частых и опасных ошибок в программировании на Си/Си\+\+ люди называют "неинициализированные переменные"\. Но, при этом, анализируя [проекты](https://pvs-studio.ru/ru/blog/inspections/) с помощью PVS\-Studio, я редко встречаю эту ошибку\.

Почему? Видимо, при изучении языка все очень сильно набивают себе шишки на этом\. Таким образом, программисты потом почти не допускают такие ошибки\. Но воспоминания остались\. И на вопрос, чего надо бояться, часто ответят "неинициализированные переменные"\.

Есть совсем простое:

```cpp
int main()
{
  ....
  int n,k=0, liczba=n, i=1;
  ....
}
```

Предупреждение CppCat: V614 Uninitialized variable 'n' used\. 1hvefw6r\.cpp 92

Можно неправильно работать со списком:

```cpp
void erase(List * Lista){
  List* pom;
  pom->next = Lista->next;
  Lista->next= pom;
  delete pom;
}
```

Предупреждение CppCat: V614 Uninitialized pointer 'pom' used\. 6gpsgjuy\.cpp 54

Можно сделать цикл со случайным количеством итераций:

```cpp
void main()
{
  int i,n;
  imie* ime[20];
  string nazwa;
  string kobieta="Kobiece imina: ";
  wpr_dane();
  for (i = 1; i < n; i++)
  {
    ....
}
```

Предупреждение CppCat: V614 Uninitialized variable 'n' used\. 8kns8hyn\.cpp 63

Можно вначале использовать, а потом вводить значение переменной:

```cpp
int main() {
  int n1;
  int n2;
  std::vector<int> vec1(n1);
  std::vector<int> vec2(n2);
  std::cin >> n1;
  for (int i = 0; i < n1; i++) {
    std::cin >> vec1[i];
  }
  std::cin >> n2;
  for (int j = 0; j < n2; j++) {
    std::cin >> vec2[j];
  }
  ....
}
```

Предупреждения CppCat: 

* V614 Uninitialized variable 'n1' used\. 9r9zdkp6\.cpp 25
* V614 Uninitialized variable 'n2' used\. 9r9zdkp6\.cpp 26

Далее приводить примеры, думаю, смысла нет\. Но, поверьте, студенты отстреливают себе ноги неинициализированными переменными разнообразнейшими способами\.

## Прочие ошибки

Конечно, в лабораторных я увидел много других разнообразнейших ошибок\. Таких же больших групп ошибок, как описано выше, я выделить не могу\. Хотя я могу назвать ещё несколько заметных групп: неправильное вычисление размера массивов, точка с запятой, досрочное прерывание цикла, неправильная работа с массивами, WTF\.

### Неправильное вычисление размера массивов

Многим начинающим тяжело даётся понимание, что в Си/Си\+\+ указатель и массив – это разные сущности\. В результате нередко попадается код подобный этому:

```cpp
int arrayLen(int p[])
{
   return(sizeof(p)/sizeof(*p));
}
```

Предупреждение CppCat: V511 The sizeof\(\) operator returns size of the pointer, and not of the array, in 'sizeof \(p\)' expression\. seprcjvw\.cpp 147

Правда, функция arrayLen\(\) нигде не используется\. Видимо, из\-за того, что не работает\. :\)

Ещё один пример:

```cpp
bool compare_mas(int * mas, int * mas2){
  //вычисляем кол-во элементов первого массива
  const auto mas_size = sizeof(mas) / sizeof(mas[0]);

  //вычисляем кол-во элементов второго массива
  const auto mas2_size = sizeof(mas2) / sizeof(mas2[0]);
  ....
}
```

Предупреждения CppCat:

* V514 Dividing sizeof a pointer 'sizeof \(mas\)' by another value\. There is a probability of logical error presence\. 0mxbjwbg\.cpp 2
* V514 Dividing sizeof a pointer 'sizeof \(mas2\)' by another value\. There is a probability of logical error presence\. 0mxbjwbg\.cpp 3

### Не там поставлена точка с запятой ';'

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

Типовой пример:

```cpp
vector sum(vector m[],int N){
vector sum,tmp;
    for (int i=0;i<N;i++);
    {
    tmp.a=m[i].a;
    tmp.b=m[i].b;
    tmp.c=m[i].c;
    sum.a+=tmp.a;
    sum.b+=tmp.b;
    sum.c+=tmp.c;
    }
    return sum.a,sum.b,sum.c;
}
```

Предупреждение CppCat: V529 Odd semicolon ';' after 'for' operator\. knadcqde\.cpp 122

### Досрочное прерывание цикла

Есть ряд примеров, где цикл случайно прерывается раньше времени:

```cpp
int main()
{
  ....
  for (long long j = sled.size()-1; j > i; j --)
  {
    sled[j] = '0';
    des = 1;
    break;
  }
  ....
}
```

Предупреждение CppCat: V612 An unconditional 'break' within a loop\. XHPquVXs\.cpp 31

### Неправильная работа с массивами

В нескольких лабораторных встретилась работа с массивами в стиле Pascal\. То есть используется запятая, что хоть и компилируется, но работает, конечно, неправильно:

```cpp
void build_maze(){
  // tablica przechowujaca informacje o odwiedzonych polach
  bool ** tablica = new bool *[n];
  ....
  if (tablica[aktualny.x - 1, aktualny.y] == false){
  ....
}
```

Предупреждение CppCat: V520 The comma operator ',' in array index expression '\[aktualny\.x \- 1, aktualny\.y\]'\. qqxjufye\.cpp 125

Или забывают, что память под возвращаемые массивы надо выделять специальным образом:

```cpp
int *mul3(int *a)
{
  int mem = 0;
  int b[1001];
  for (int i = 100; i >= 0; i--)
  {
    int x = a[i] * 3 + mem;
    mem = x / 10;
    b[i] = x % 10;
  }
  return b;
}
```

Предупреждение CppCat: V558 Function returns the pointer to temporary local object: b\. hqvgtwvr\.cpp 89

### WTF

Есть фрагменты кода, которые я, кроме как WTF, назвать не могу\. Возможно, кто\-то попросил одногруппника объяснить, где в программе ошибка\. Хотя, скорее, эта лабораторная как раз на изучение переполнения массивов\. К сожалению, я не знаю, что написано в комментарии\.

 Приведу целиком один такой пример:

```cpp
#include <iostream>
using namespace std;
int main()
{
    int a[10];
    for(int i=0; i<50; i++)
        cout << a[i] << endl;
    //ovoj loop ili kje krashne ili kje ti nedefinirani vrednost
    //(ne mora da bidat 0)
    //ako namesto 50 stavis 500000, skoro sigurno kje krashne
    int b[10];
    for(int i=0; i<50; i++)
    {
        b[i] = i;
        cout << b[i] << endl;
    }
    //ovoj loop nekogas kje raboti, nekogas ne. problemot so
    //out-of-bounds index errori e sto nekogas rabotat kako
    //sto treba, pa greskata tesko se naogja
}
```

## Что ещё не вошло статью

Много что не вошло\! Например, встречается неправильное использование функций printf\(\)\. Но это настолько банально, что даже писать про это не хочется\.

Впрочем, встречались и достаточно экзотические разновидности ошибок:

```cpp
void zmienne1()
{
  ....
  int a,b,c,d;
  cin >> a >> b >> c >> d;
  if(a == b == c == d)
  ....
}
```

Предупреждение CppCat: V709 Suspicious comparison found: 'a \=\= b \=\= c'\. Remember that 'a \=\= b \=\= c' is not e qual to 'a \=\= b && b \=\= c'\. b5lt64hj\.cpp 284

Тоже из редкого \(если, конечно, не смотреть на предупреждения компилятора\):

```cpp
const long AVG_PSYCHO = 0.8;
const long AVG_GRAD = 1.2;
```

Предупреждения CppCat:

* V674 The '0\.8' literal of the 'double' type is assigned to a variable of the 'long' type\. Consider inspecting the '\= 0\.8' expression\. 2k2bmnpz\.cpp 21
* V674 The '1\.2' literal of the 'double' type is assigned to a variable of the 'long' type\. Consider inspecting the '\= 1\.2' expression\. 2k2bmnpz\.cpp 22

Тем не менее, надо заканчивать\. Надеюсь, я развлек читателей и соблазнил кого\-то попробовать CppCat\.

## Почему мы не планируем делать какой\-либо online\-анализатор

Я предвижу вопрос: "Почему бы вам не сделать какую\-то систему для online\-проверки кода?"\. Например, есть какая\-то форма, куда можно вставить код и нажать кнопку "проверить"\. Или, раз вы мониторите сайт pastebin\.com, так почему бы не выкладывать куда\-то результаты проверки?

Я уверен, что делать этого нам не нужно\. Для этого есть три причины, поэтому прошу не начинать дискуссию на эту тему\.

Причины:

1. Это не нужно ни нам, ни пользователю\. Нам это добавит работы\. А пользователь не получит ничего нового\. Он может просто скачать и установить PVS\-Studio или CppCat и провести все эксперименты, которые пожелает\. Демонстрационной версии будет более чем достаточно\. Часто формы "вставь и проверь код" делают те, у кого просто так нельзя скачать пробную версию\. У нас можно\. Более того, она не имеет каких\-либо функциональных ограничений\. Ещё кто\-то может сказать, что у него нет Windows, а он хотел что\-то попробовать\. Но раз у него нет Windows, то он всё равно не наш пользователь\.
1. Такая система сильно искажает оценку возможностей статического анализатора\. Статья на эту тему: Мифы о статическом анализе\. [Миф пятый – можно составить маленькую программу, чтобы оценить инструмент](https://pvs-studio.ru/ru/blog/posts/0119/)\. Мы хотим, чтобы люди испытывали анализатор на своих реальных проектах, а не на синтетических примерах\.
1. Как уже я сказал, синтетические примеры мы проверять не хотим\. А проверить проект целиком сложно с инфраструктурной точки зрения\. Подробнее про это написано в [интервью](https://pvs-studio.ru/ru/blog/posts/0231/)\. Кратко: придётся делать сложную систему, куда нужно закачивать исходники, библиотеки, настраивать параметры сборки и так далее\. Иначе полноценный анализ невозможен\. Получается, что проще скачать анализатор, установить и проверить\.

## Заключение

Уважаемые студенты и преподаватели, будем рады видеть вас среди наших пользователей\. Желаю студентам стать квалифицированными специалистами и склонить в дальнейшем свой коллектив на приобретение PVS\-Studio для командной работы\.

## Дополнительные ссылки:

1. [PVS\-Studio для Visual C\+\+](https://pvs-studio.ru/ru/blog/posts/0222/)\.
1. [Альтернатива PVS\-Studio за $250](https://pvs-studio.ru/ru/blog/posts/0228/)\.
1. [Сравнение возможностей статических анализаторов кода PVS\-Studio и CppCat](https://pvs-studio.ru/ru/blog/posts/0258/)\.
1. [Бесплатный CppCat для студентов](https://pvs-studio.ru/ru/blog/posts/0290/)\.