Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Посоветуйте интересные проверки для анализа Си/Си++ кода 
:(
    Опции темы
Thunderbolt
Дата 16.5.2012, 09:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



Я один из разработчиков анализатора PVS-Studio. Подробнее про анализатор можно почитать здесь. Мы постоянно реализуем новые диагностические правила. Список того, что ещё можно реализовать, кажется мне бесконечным. Мы постоянно пополняем todo-лист новыми примерами ошибок, которые хорошо-бы научиться диагностировать. Так что с проблемы с нехваткой задач у нас нет. Зато есть проблема, как выбрать наиболее интересные и распространенные типы ошибок. Логично, в первую очередь реализовывать диагностику тех ошибок, которые наиболее часто встречаются в программах. Вопрос в том, как расставить приоритеты разным задачам.

Была предложена идея, создать на сайте раздел, где будут перечислены различные примеры дефектов и пользователи смогут проголосовать за те ошибки, которые на их взгляд они чаще всего допускают. Мне этот подход не нравится по двум веским причинам.

1) Список ошибок будет очень большой. Это значит, что никто не будет просматривать его целиком. Приоритет получат примеры, расположенные в начале списка. Можно конечно сортировать примеры случайным образом, но тогда не понятно, как продолжить просмотр списка, например на следующий день. И вообще всё становится излишне сложно.

2) Программисты недооценивают простые ошибки (см. миф второй). Например, они не любят признавать, что огромную часть ошибок возникает из-за Copy-Paste и опечаток. Мало кто будет голосовать, за подобный пример:

Код
bool isclosebrace (TCHAR c)
{
  return c == _T ('}') ||
         c == _T ('}') || // должно быть ')'
         c == _T (']') ||
         c == _T ('>');



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

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

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

Приведу пару примеров кода, который было бы интересно увидеть.

Код

TCHAR headerM[headerSize] = TEXT("");
...
if (headerM != '\0')


Хотели проверить, что строка пустая, но забыли разменивать указатель. Достаточно распространенная опечатка. Корректный вариант: "if (*headerM != '\0')".

Код

if (memcmp(this, &other, sizeof(Matrix4) == 0)) {


Не там поставлена закрывающаяся скобка. В результате функция memcmp() сравнивает 0 байт.

Код
BOOL ret = TRUE;
if (m_hbitmap)
  BOOL ret = picture.SaveToFile(fptr);


Лишний раз объявили переменную 'ret'. В результате, не будет обработан случай, когда не удастся сохранить файл.

Приведенные примеры не требуют сложного AI и как следствие хорошо диагностируется инструментами статического анализа. Хочется увидеть что-то в таком духе.

Я думаю, многие примеры, которые будут приведены, уже диагностируются PVS-Studio. Но это не страшно, я их отфильтрую. Если хотите, Вы сами можете попробовать, сможет ли PVS-Studio найти тот или иной тип ошибки. Для этого можно воспользоваться демонстрационной версией. Кстати, она полностью функциональна и позволит заодно попробовать  проверить ваши проекты.

---
С уважением, Андрей Карпов
Лучше всего комментарии оставлять здесь, или написать мне на электронную почту: karpov[@]viva64.com

--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
bsa
Дата 16.5.2012, 11:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(Thunderbolt @  16.5.2012,  10:39 Найти цитируемый пост)
Хотели проверить, что строка пустая, но забыли разменивать указатель. Достаточно распространенная опечатка. Корректный вариант: "if (*headerM != '\0')".

Вы уверены, что вариант этот полностью корректный? А в случае режима unicode?

Это сообщение отредактировал(а) bsa - 16.5.2012, 11:40
PM   Вверх
borisbn
Дата 16.5.2012, 13:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



1) Если в коде функции какая-то переменная объявлена, но не используется, то об этом скажет компилятор. Если же такая переменная объявлена в качестве члена класса, и ни в одном методе класса не используется, то компилятор (почему-то) об этом умалчивает.

2) Не уверен, что это вообще можно отследить, но я уже несколько раз натыкался на то, что я тупо забываю, как работает метод resize у вектора: когда мне нужно изменить размер вектора и обнулить его весь, то я часто ошибочно пишу
Код
v.resize( new_size, 0 );

естественно, обнуляется только часть вектора, а не весь new_size.

3) Вот ещё довольно частая ошибка, связанная с STL: при прохождении по контейнеру и удалении некоторых элементов часто делают след.образом
Код
for ( iterator it = v.begin();
       it != v.end();
       ++it ) {              // <--------------
    if ( ... ) {
        v.erase( it );
    }
}

думаю, как надо писать - ни у кого вопросов нет, однако ошибка частая.

4) Сам я уже заставил себя избавиться от такой "ошибки", но часто встречаю у других: сравнение переменной с плавающей точкой с нулём
Код
double d = ...;
if ( d == 0 ) { ...

В принципе, иногда так можно делать, но иногда это может стать причиной ошибки (или зависания или т.п.)

Если что-нибудь ещё вспомню - напишу.



--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
borisbn
Дата 16.5.2012, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Вот ещё частая ошибка копи-паста
Цитата
for ( int i = 0; i < m; i++ ) {
    for ( int j = 0; j < n; i++ ) {

хорошо ещё, если j нигде не изменяется - программа повиснет и это можно будет быстро найти...
или же этот участок кода вызывается раз в год...


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Thunderbolt
Дата 16.5.2012, 13:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



Цитата(bsa @ 16.5.2012,  11:39)
Вы уверены, что вариант этот полностью корректный? А в случае режима unicode?

Скажем так, "сойдет". Если по хорошему, то как-то так: if (*headerM != _T('\0')).
Это не выдуманный пример, я взятый из кода. Поэтому я обычно делаю минимальные исправления. Потому, что всё равно могу не угадать идею автора. И лезть с украшательствами тут уже глупо будет выглядеть. smile

--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
Thunderbolt
Дата 16.5.2012, 14:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



Спасибо за примеры. Записал их для обдумывания.

Цитата

1) Если в коде функции какая-то переменная объявлена, но не используется, то об этом скажет компилятор. Если же такая переменная объявлена в качестве члена класса, и ни в одном методе класса не используется, то компилятор (почему-то) об этом умалчивает.


Желание понятно. Но боюсь, подобная диагностика может оказаться неудачной. Помимо технических трудностей (межмодульный анализ) можно сходу назвать 3 больших источника ложных срабатываний.
1) Нередко встречаются зарезервированные поля, которые нигде не используется.
2) Это библиотека. Член может нигде не использоваться, но пригодится в другом приложении.
3) Это поле, используемое для специфического выравнивания данных.
И интуиция мне подсказывает, что этот список можно продолжать. Проблема лишних переменных распространена и извечна. И раз компиляторы здесь молчат, это не спроста.


Цитата

Код
double d = ...;
if ( d == 0 ) { ...

В принципе, иногда так можно делать, но иногда это может стать причиной ошибки (или зависания или т.п.)


Да, данная диагностика уже присутствует в PVS-Studio.

Добавлено через 3 минуты и 21 секунду
Данный код диагностируется с помощью V533: It is likely that a wrong variable is being incremented inside the 'for' operator. Consider reviewing 'i'.
--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
borisbn
Дата 16.5.2012, 14:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



Цитата(Thunderbolt @  16.5.2012,  09:39 Найти цитируемый пост)

Код
BOOL ret = TRUE;
if (m_hbitmap)
  BOOL ret = picture.SaveToFile(fptr);

Лишний раз объявили переменную 'ret'.

навеяло...
А если "первая" переменная объявлена как член класса, будет ли сообщение об "ошибке" ?
Код
class A {
    int var;
    void foo() {
        int var = ...;
    }
};


а по поводу неиспользуемого члена класса, я, конечно же, имел в виду private-член... Но если Вы считаете, что такая проверка нецелесообразна - я Вам верю  smile 


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
bsa
Дата 16.5.2012, 14:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(borisbn @  16.5.2012,  15:17 Найти цитируемый пост)
А если "первая" переменная объявлена как член класса, будет ли сообщение об "ошибке" ?
в данном случае очень вероятно, что это не ошибка.
PM   Вверх
Thunderbolt
Дата 16.5.2012, 14:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


DevRel
*


Профиль
Группа: Участник
Сообщений: 122
Регистрация: 7.11.2007
Где: Тула

Репутация: 10
Всего: 16



Цитата

А если "первая" переменная объявлена как член класса, будет ли сообщение об "ошибке" ?

К сожалению нет. Слишком много ложных срабатываний. А если 99% какого-то сообщения ложные, то лучше их не далать. Или их не будут смотреть или выключат.

Цитата

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

Я подумаю. Но вот уже ещё вспоминаются, что многие используют, "неиспользуемые" переменные приблизитеьно так:

struct Point {
  int x, y, z;
};

memcpy(&(p1->x), &(p1->x), 3*sizeof(int));

Это суровая прадва жизни...
--------------------
Карпов Андрей, DevRel в PVS-Studio.
PM MAIL WWW   Вверх
volatile
Дата 17.5.2012, 00:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



1.Часто забываю выставить константность
Это конечно не грубая ошибка, но тем не менее, у меня очень  часто.
Не знаю можно ли это отследить, и насколько это корретно будет.

int func (char * p)
  { return strlen (p); }
Здесь, например, по указателю не производится записи, и неплохо бы порекомендовать сделать его константным.
int func (char const * p)

А если бы еще константность методов рекомедовать можно было, то вообще хорошо.


2. Еще довольно часто бывает, добавляю новый член в класс, а в конструкторе, забываю добавить его инициализацию.
Но это я думаю отследить вообще не возможно...
Ну или такой алгоритм: Если инициализируются все члены, а какой-то один забыт, то можно выдать предупреждение.



PM MAIL   Вверх
bsa
Дата 17.5.2012, 12:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

Репутация: 63
Всего: 196



Цитата(volatile @  17.5.2012,  01:21 Найти цитируемый пост)
Еще довольно часто бывает, добавляю новый член в класс, а в конструкторе, забываю добавить его инициализацию.
Кстати, очень частая и досадная ошибка...

PM   Вверх
borisbn
Дата 17.5.2012, 12:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



volatilebsa, вроде ж с приходом 11-го можно так
Код
class X {
    int var = 42;
};

хотя..... т.к. в студии этого нет и не предвидится, а автор создаёт свой проект только под неё, то - да, согласен. полезно.


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0853 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.