Почему студентам нужен анализатор кода CppCat
CppCat — это простой статический анализатор кода для поиска ошибок в программах на языке Си/Си++. Мы начали выдавать бесплатные академические лицензии всем желающим (студентам, преподавателям и так далее). Для большей популяризации CppCat среди студентов я решил написать эту заметку об ошибках, которые можно найти в лабораторных работах, встречающихся на сайте Pastebin.com.Чуть-чуть про CppCatCppCat — статический анализатор кода, интегрирующийся в среду Visual Studio и позволяющий найти множество опечаток и прочих ошибок ещё на этапе кодирования. Анализатор умеет запускаться автоматически после компиляции и проверять только что написанный код. Анализатор поддерживает C, C++, C++/CLI, C++/CX.О том, как получить бесплатную лицензию на CppCat, описано в статье: Бесплатный CppCat для студентов. Хочу добавить, что мы даём лицензию не только студентам, но и аспирантам, преподавателям и так далее.
Многих расстраивает, что CppCat не интегрируется в бесплатную среду Visual Studio Express. К сожалению, мы не можем ничего с этим поделать. Express-версии Visual Studio не поддерживают модули расширений (plugins). Однако, это не беда. Хочется напомнить, что студенты имеют доступ к Microsoft DreamSpark и могут получить доступ к Visual Studio Professional.
Завлечение студентов В начале хотел написать что-то в духе: Даже студент может получить пользу от статического анализа. Зачем долго и мучительно искать ошибку в своей программе, если о ней может подсказать CppCat? Таким образом можно не только быстрее найти ошибку, но ещё узнать подробности о том, как делать не надо. Документация к CppCat подробно объясняет каждую диагностику и приводит различные примеры ошибок, подсказывает, как их исправлять.
Потом решил, что как-то это натянуто. Ну какие у студентов серьезные ошибки? Можно и поотлаживать цикл из 10 итераций. Это будет даже полезно. Поэтому я переформулирую рекомендацию использовать CppCat следующим образом:
Работодатель будет ценить не только ваше умение программировать и писать хитрые алгоритмы. Не менее важно уметь пользоваться базовым инструментарием.
Грош цена алгоритму, если он будет потерян из-за того, что вы не знаете, что такое система контроля версий и максимум, что предпринимали — делали копию исходных кодов на флешку.
Поэтому важно изучать не только языки и среды разработки, но и вспомогательный инструментарий.
Я не берусь составлять список наиболее важного инструментария. Однако важно знать хотя бы одну систему контроля версий, зачем может быть нужен WinMerge, что такое профайлер, как сделать дистрибутив и так далее.
Одной из технологий, которую хорошо знать и можно упомянуть в резюме, является статический анализ кода. CppCat отлично подходит для знакомства с методологией статического анализа. Это будет хорошим плюсом к вашим знаниям и намекнёт работодателю, что вы что-то слышали о качестве кода.
А теперь то, ради чего мы здесь собрались Скучная часть закончилась. Я думаю, вы догадываетесь, что сейчас мы будем искать ошибки в лабораторных работах.Говорить о том, что код часто плох, в этот раз я не буду. И так понятно, что код лабораторных содержит в себе тьму разнообразнейших ошибок. Поэтому я поставил задачу не найти как можно больше ошибок. Это неинтересно. Я попробовал выделить какие-то паттерны ошибок, которые проявляются у студентов чаще всего. Правда, пока я смог четко заметить преобладание ошибок только трех типов. Но про это чуть ниже.
У Вас может вопрос: откуда я взял лабораторные работы? Отвечаю.
Есть сайт Pastebin.com, где разработчики могут удобно обмениваться фрагментами кода. Студенты активно используют этот сайт. Почти весь код, имеющий тег C++, представляет собой какую-то лабораторную работу или её часть.
Мы написали программу, которая следит за сайтом Pastebin.com и выкачивает оттуда свежие файлы, помеченные как «код C++». Насобирав более двух тысяч файлов, я сделал из них проект в Visual Studio и проверил его. Больше половины, конечно, проверить не удалось. Во многих файлах только фрагменты кода, вписан текст, не являющийся комментарием, не хватает каких-то библиотек так далее. Однако я и не ставил целью проверить как можно больше кода, опубликованного на Pastebin.com. То, что проверилось, мне вполне хватит для этой статьи. Сбор файлов продолжается и, возможно, я потом напишу ещё что-то на эту тему.
Типовые ошибки студентов, изучающих Си++ Я просмотрел не так уж много ошибок в лабораторных работах. Так что пока могу выделить только 3 паттерна.Приводить все подряд примеры ошибок я не буду. Они однотипны и неинтересны. Ограничусь только несколькими примерами. Но, поверьте, если я говорю, что такая ошибка распространена, это действительно так.
P.S. Многие из опубликованных примеров были с ограничением по времени и уже недоступны. Поэтому ссылки на удалённые страницы давать не буду.
Паттерн 1. Третье место по популярности. Путаница в однотипных условиях
Многие задачи по программированию подразумевают проверку многих условий. И, реализуя их, легко запутаться или опечататься. Показательный пример:
int main ()
{
int n, a, b, c;
cin >> n;
for (int i=0; i
Ещё пример:
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), но при этом должны выводиться разные строки.
Кстати, в обоих примерах ошибка находится в последней строке. Опять нам встретился «эффект последней строки».
Паттерн 2. Второе место по популярности. Выход за границу массива на 1 элемент То, что элементы массивов нумеруются в языке C++ от нуля, является большим затруднением при его изучении. Т.е. вроде понять это просто, но вот научиться не выходить за границу массива очень сложно. Если нужен 10 элемент массива, то так и хочется написать A[10]. Пример: 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. 0z3×9b3i.cpp 38Ещё:
void main () { … //evaulation double calc_y[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Много неправильных сравнений в условиях циклов:
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Очень много:
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. Первое место по популярности. Неинициализированные переменные О! Я, кажется, понял, почему, кого не спроси, одной из наиболее частых и опасных ошибок в программировании на Си/Си++ люди называют «неинициализированные переменные». Но, при этом, анализируя проекты с помощью PVS-Studio, я редко встречаю эту ошибку.Почему? Видимо, при изучении языка все очень сильно набивают себе шишки на этом. Таким образом, программисты потом почти не допускают такие ошибки. Но воспоминания остались. И на вопрос, чего надо бояться, часто ответят «неинициализированные переменные».
Есть совсем простое:
int main () { … int n, k=0, liczba=n, i=1; … } Предупреждение CppCat: V614 Uninitialized variable 'n' used. 1hvefw6r.cpp 92Можно неправильно работать со списком:
void erase (List * Lista){ List* pom; pom→next = Lista→next; Lista→next= pom; delete pom; } Предупреждение CppCat: V614 Uninitialized pointer 'pom' used. 6gpsgjuy.cpp 54Можно сделать цикл со случайным количеством итераций:
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Можно вначале использовать, а потом вводить значение переменной:
int main () {
int n1;
int n2;
std: vector
Ещё один пример:
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 Не там поставлена точка с запятой ';' Этих ошибки встречаются не так часто, как я ожидал. Они есть, но распространенной ошибкой в лабораторных работах я её назвать не могу.Типовой пример:
vector sum (vector m[], int N){
vector sum, tmp;
for (int i=0; i
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 89WTF Есть фрагменты кода, которые я, кроме как WTF, назвать не могу. Возможно, кто-то попросил одногруппника объяснить, где в программе ошибка. Хотя, скорее, эта лабораторная как раз на изучение переполнения массивов. К сожалению, я не знаю, что написано в комментарии.Приведу целиком один такой пример:
#include
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Тоже из редкого (если, конечно, не смотреть на предупреждения компилятора):
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, так почему бы не выкладывать куда-то результаты проверки? Я уверен, что делать этого нам не нужно. Для этого есть три причины, поэтому прошу не начинать дискуссию на эту тему.
Причины:
Это не нужно ни нам, ни пользователю. Нам это добавит работы. А пользователь не получит ничего нового. Он может просто скачать и установить PVS-Studio или CppCat и провести все эксперименты, которые пожелает. Демонстрационной версии будет более чем достаточно. Часто формы «вставь и проверь код» делают те, у кого просто так нельзя скачать пробную версию. У нас можно. Более того, она не имеет каких-либо функциональных ограничений. Ещё кто-то может сказать, что у него нет Windows, а он хотел что-то попробовать. Но раз у него нет Windows, то он всё равно не наш пользователь. Такая система сильно искажает оценку возможностей статического анализатора. Статья на эту тему: Мифы о статическом анализе. Миф пятый — можно составить маленькую программу, чтобы оценить инструмент. Мы хотим, чтобы люди испытывали анализатор на своих реальных проектах, а не на синтетических примерах. Как уже я сказал, синтетические примеры мы проверять не хотим. А проверить проект целиком сложно с инфраструктурной точки зрения. Подробнее про это написано в интервью. Кратко: придётся делать сложную систему, куда нужно закачивать исходники, библиотеки, настраивать параметры сборки и так далее. Иначе полноценный анализ невозможен. Получается, что проще скачать анализатор, установить и проверить. Заключение Уважаемые студенты и преподаватели, будем рады видеть вас среди наших пользователей. Желаю студентам стать квалифицированными специалистами и склонить в дальнейшем свой коллектив на приобретение PVS-Studio для командной работы.Дополнительные ссылки: PVS-Studio для Visual C++. Альтернатива PVS-Studio за $250. Сравнение возможностей статических анализаторов кода PVS-Studio и CppCat. Бесплатный CppCat для студентов.