Доклад о редких нестандартных расширениях языка С++, про которые никто не знает, но которые надо поддерживать в анализаторе кода.
О магии Visual C++ с файлом stdafx.h, когда проект компилируется, хотя не должен. О том как зародился viva64 (предшественник PVS-Studio) для поиска 64-битных проблем. Как и почему исчез анализ кода, который одно время существовал в компиляторе Intel C++.
2. О докладчике
• Карпов Андрей Николаевич, 1981
• Присутствует на Habrahabr под именем
Andrey2008 - habrahabr.ru/users/andrey2008/
• Технический директор ООО «СиПроВер»
• MVP в категории Visual C++
• Intel Black Belt Software Developer
• Один из основателей проекта PVS-Studio
(статический анализатор кода для языков
C/C++/C#).
www.viva64.com
3. Содержание
• Пункт N1
• Пункт N2
• Пункт N3
• Я буду рассказывать о том,
о чем не могу писать в
статьях.
А ведь накопилось. И о странном.
www.viva64.com
4. На экономический вопрос «Почему программное обеспечение такое
дорогое?» столь же экономическим ответом был бы такой: «Потому
что его пытаются получить при помощи дешевого труда». А почему
пытаются? Да потому, что присущие ему трудности повсеместно
сильно недооцениваются.
Эдсгер Дейкстра
Как так получилось, что требуются
вспомогательные инструменты?
www.viva64.com
5. Чувствуется пренебрежение к сложности
При сдвиге >> знакового типа
int левые биты заполняются
копией знакового бита.
На самом деле, это
implementation-defined.
www.viva64.com
6. Ещё пример из той же книги по электронике
Файлы file1.c, file2.c, file3.c
можно подключить с
помощью директивы #include
#include "file1.h"
#include "file2.h"
#include "file3.h"
#include "file1.c"
#include "file2.c"
#include "file3.c"
www.viva64.com
7. Ещё пример из той же книги по электронике
Пространство, занимаемое
структурой в памяти,
равняется сумме размеров
всех элементов.
www.viva64.com
8. Что же делать? Да здравствуют костыли!
• Мы все молодцы. Но нас заставляют делать быстро и дёшево.
• Компромисс: вспомогательные инструменты.
• Например, отладчики, статические анализаторы кода.
• Это не хорошо и не плохо. Так устроен мир.
9. Начало. Viva64
• Актуальность: нет
инструмента для поиска 64-
битных ошибок
• Казалось, надо приложить
немного усилий и будет
много денег
• Суровая реальность
www.viva64.com
10. Стартап
• Ожидание
• Я буду генерировать
интересные креативные
идеи и воплощать их в
жизнь
• Реальность
• Сгорела проводка
• Важный сотрудник
заболел
• 10 $
• 1 копейка
www.viva64.com
12. Как начать?
• "Я готов отчитаться за каждый
доллар, но не спрашивайте меня,
как я заработал первый
миллион." — Генри Форд.
• Чем только не приходится
заниматься…
• Любая подработка
• Гранты
• Аутсорсинг
www.viva64.com
17. О сравнении анализаторов
•Я не могу написать, что пришлось отбирать
проекты
• Visual Studio падал
• Cppcheck зависал
•На самом деле, всё сводится к «шарику»
www.viva64.com
18. Всегда есть кому дорого и про Хобби
Не знаю, в каком мире вы живете, но, мне кажется, что многие компании
склонны переоценивать важность своего софта.
На 9000 рублей можно кормить двух человек месяц, проехать 3000
километров, пролететь четверть земли. Купить более 20 книг, сделать
операцию на глазу.
www.viva64.com
19. Ну тупые (c) Задорнов
char m_buffer[BUF_SIZE];
memset( m_buffer, 0, sizeof(*m_buffer) );
В этой строке очищается столько байт, сколько хранится в
первом элементе массива.
Про это некрасиво писать, но программисты часто
не видят ошибки. Это грустно, так как мешает
продавать инструмент.
www.viva64.com
20. ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) {
memcpy( mat, src, sizeof( src ) );
}
Ну тупые (c) Задорнов
V511. The sizeof() operator returns size of the pointer, and not of the array, in 'sizeof(src)' expression.
Except it doesn't. The sizeof() operator returns the size of the object, and
src is not a pointer - it is a float[3][3]. sizeof() correctly returns 36 on my
machine.
Позже: Oop - yup - I essentially tested with with "char A[100];" rather
than "void Foo(char B[100])"
www.viva64.com
21. SHFILEOPSTRUCT Data;
....
Data.pTo = L"";
Описание тоже никто не читает
V540 Member 'pTo' should point to string terminated by two 0 characters.
В C++ sizeof(*L"") == sizeof(wchar_t) == 2,
так что ошибки тут нет.
www.viva64.com
22. Тяжела и неказиста жизнь простого
продвиженца
• Помочь опубликовать я не могу, т.к. не открываю вложения
• - Так ведь в статье были ссылки.
- Я не перехожу по обернутым ссылкам. (речь про bit.ly)
• Analyzer
• https
www.viva64.com
23. Как раз там, где нужен статический анализ,
его продать невозможно
• Требуется не устранить максимальное количество ошибок, а
пройти сертификацию.
• Заземлённые указатели
www.viva64.com
24. Байки разработчика с одной из АЭС
• «Некоторые персонажи в персонале
станций могут дать фору Гомеру Симпсону»
• «Зачем нам git? Вот смотри, у меня всё в
тетрадке записано».
• Ещё пример из жизни: в конструкторе
копирования у класса происходит открытие
файла конфигурации и полный его парсинг.
www.viva64.com
25. Он иногда присылал мне примечательные
фрагменты
• В следующие две строки программист умудрился уместить
как минимум 3 WTF:
inline virtual Foo& operator=(const Foo& x) {
if (&x); return *this;
}
www.viva64.com
27. Ещё впечатления
• Рядом с этим кодом был комментарий, в котором
программист объяснял, что в новом стандарте C++ bool будет
то же самое, что и int. Год написания комментария не
известен.
#if !defined(BOOL_DEFINED)
typedef int bool;
#endif
www.viva64.com
29. Я за Си
Любой дурак может написать код, понятный компьютеру. Хорошие
программисты пишут код, понятный людям.
Мартин Фаулер
www.viva64.com
30. Я за Си (с точки зрения разработчика PVS-Studio)
31. Свой класс строки - это норма
• Рано или поздно, в любом
состоявшемся проекте появляется свой
класс строки.
• Я ждал, появится ли он в PVS-Studio.
• Он появился, и это было обосновано.
• Это нормально. Не стесняйтесь это
делать.
www.viva64.com
32. Самое сложное в статическом анализе: не
ругаться
• Испытания C++: 105 открытых проектов
• Испытания C#: 49 открытых проектов
• Пример V501
• Пример V640
33. V501.
Опасным считается инфиксная операция, если
совпадает левый и правый операнд.
while (X < X)
if (A == B || A == B)
www.viva64.com
34. V501. Дьявол кроется в деталях
• X*X
• while (*p++ == *a++ && *p++ == *a++)
• Слева и справа находятся числовые литералы
if (0 == 0)
… 15 | 15 …
• #define M1 100
#define M2 100
if (x == M1 || x == M2)
• float x = foo();
if (x == x)
www.viva64.com
35. V501. Дьявол кроется в деталях
• / или - применяются к числовым константам: 1./1.
• Строка из Zlib:
if (opaque) items += size - size; / * make compiler happy * /
• rand() - rand()
rand() % N - rand() % N
• Слева и справа от '|', '&', '^', '%' находятся классы.
if (str == str) – ищем
if (vect ^ vect) – лучше пропускать
• sizeof(__int64) < sizeof(__int64)
www.viva64.com
36. V501. Дьявол кроется в деталях
• 0 << 31 | 0 << 30 | ...
(0 << 6) | (0 << 3) | …
• '0' == 0x30 && 'A' == 0x41 && 'a' == 0x61
• Эта шаблонная функция для определения NaN чисел.
• Read(x) && Read(x)
• #define USEDPARAM(p) ((&p) == (&p)) и прочие
• Слева и справа расположен вызов функций с такими
именами как pop, _pop.
• И так далее
www.viva64.com
37. V640.
Текст оформлен так, как будто
выполняются все действия.
if (a==1)
b = c; foo(b);
if (a==1)
b = c;
foo(b);
www.viva64.com
38. Дьявол кроется в деталях
• Все выражения находятся на 1 строке: if (a==1) b = c; F(b);
• В разных строках не совпадает количество символов
табуляции
• if (a)
return x;
foo();
• else else else for(..;..;..)
foo(); foo(); foo();
foo(); foo(); foo();
• Присутствуют #if, #else, #endif, #pragma и т.п.
• И так далее.
www.viva64.com
39. Редкие ключевые слова и расширения
• Приходится поддерживать редкое или нестандартное
• Это скрыто от глаз пользователей
• Примеры
www.viva64.com
42. GCC
• struct { int a; int b; } X = { a : 0, b : 1 };
• func c_open(name *byte, mode int, perm int) int __asm__
("open");
• __is_same (type1, type2)
• Some_Class A __attribute__ ((init_priority (2000)));
Some_Class B __attribute__ ((init_priority (543)));
www.viva64.com
43. GCC (есть даже полезное для простых
программистов, но лучше не использовать)
При использовании оператора "switch", вы можете задать диапазон значений
для "case".
switch (x)
{
case 1 ... 5: Foo1(); break;
case 'A' ... 'Z': Foo1(); break;
}
a = x ?: y; a = x ? x : y;
www.viva64.com
44. GCC. Аналог
анонимных
функций для Си
int Foo(int a) {
int x = Foo1(a);
Foo2();
return x;
}
// Можно записать так:
int Foo(int a) {
return ({ int x = Foo1(a); Foo2(); x; });
}
www.viva64.com
45. Наличие подобных конструкций связано с попыткой помочь делать макросы
более безопасными. Вспомним классическую ошибку при использовании
макроса max:
#define max(a,b) ((a) > (b) ? (a) : (b))
x = max(a++, b);
Операция "++" будет выполнена 2 раза, что не соответствует задуманному
программистом. Чтобы этого избежать, предлагается написать так:
#define maxint(a,b)
({int _a = (a), _b = (b); _a > _b ? _a : _b; })
www.viva64.com
46. GCC. Вложенные функции
double square_sum (double a, double b)
{
double square (double z)
{
return z * z;
}
return square(a) + square(b);
}
www.viva64.com
47. • Ещё один пример. Перенос строки в конце файла.
• Когда люди говорят «да это максимум на неделю дел то», они
просто не понимают, о чём говорят.
• 500 т.р., это дорого! Будем писать сами.
• Программисты часто не различают «поделку» и программный
продукт.
• Работа в разных условиях
• Масштабируемость
• Интерфейс
• Документация
• И т.д.
Редкие ключевые слова и расширения
www.viva64.com
48. Разное
• Почему нет цен на сайте?
• Программисты не умеют считать
• Почему сайт www.viva64.com такой страшный?
• Зато он приносит нам деньги
• Вы ещё сайт Abraxas Software не видели http://www.abxsoft.com/
• Почему единорог?
• А вы что думаете?
www.viva64.com