![]() |
Модераторы: Daevaorn |
![]() ![]() ![]() |
|
Thunderbolt |
|
||||||||||||||||||||||||||||||||||||||||||
![]() DevRel ![]() Профиль Группа: Участник Сообщений: 122 Регистрация: 7.11.2007 Где: Тула Репутация: 10 Всего: 16 |
Я изучил множество ошибок, возникающих в результате копирования кода. И утверждаю, что чаще всего ошибки допускают в позднем фрагменте однотипного кода. Ранее я не встречал в книгах описания этого феномена, поэтому и решил о нём написать. Я назвал его "эффектом последней строки".
![]() Введение Меня зовут Андрей Карпов. Я занимаюсь необычным занятием. Я исследую программный код приложений с помощью статических анализаторов и описываю найденные ошибки и недочёты. Занимаюсь я этим из-за прагматичных и корыстных побуждений. Так наша компания рекламирует инструменты PVS-Studio и CppCat. Нашли ошибки. Описали в статье. Привлекли внимание. Profit. Но сегодня статья не про анализаторы. В процессе анализа проектов я сохраняю замеченные ошибки и соответствующие фрагменты кода в специальной базе. Кстати, с содержимым этой базы может познакомиться любой желающий. Мы превращаем её в набор html-страниц и выкладываем их на сайте в разделе "Выявленные ошибки". База уникальна! Сейчас она содержит около 1500 фрагментов кода с ошибками. Она ждёт людей, которые смогут выявить в этих ошибках закономерности. Это может послужить основой многих исследований и материалов для книг и статей. Специально я не делал никаких исследований накопленного материала. Тем не менее одна закономерность так явно проявляет себя, что я решил изучить её подробнее. В статьях мне часто приходится писать "обратите внимание на последнюю строку". Я решил, что это не спроста. Эффект последней строки В исходном коде программы часто нужно написать несколько однотипных конструкций, идущих подряд. Набирать несколько раз однотипный код скучно и не эффективно. Поэтому используется метод Copy-Paste. Фрагмент кода копируется несколько раз, после чего делаются правки. Все знают, что этот метод плох. Легко забыть что-то поменять и в результате код будет содержать ошибку. К сожалению, часто хорошей альтернативы нет. Теперь о закономерности. Я выяснил, что чаще всего ошибка допускается в самом последнем скопированном блоке кода. Простой короткий пример:
Обратите внимание на строчку "z += other.y;". В ней забыли поменять 'y' на 'z'. Пример кажется искусственным. Но этот код взят из реального приложения. Далее я убедительно покажу, что это очень частая и распространенная ситуация. Именно так и выглядит "эффект последней строки". Человек чаще всего допускает ошибку в самом конце внесения однотипных исправлений. Я где-то слышал, что часто альпинисты срываются на последних десятках метрах подъема. Не потому, что они устали. Просто они радуются, что осталось совсем немного. Они предвкушают сладкий вкус победы над вершиной. В результате они ослабляют внимание и допускают роковую ошибку. Видимо, что-то такое случается и с программистами. Теперь немного цифр. Изучив базу ошибок, я выявил 84 фрагмента кода, которые, как мне кажется, были написаны с использованием Copy-Paste. Из них 41 фрагмент содержит ошибку где-то в середине скопированных блоков. Вот пример:
Длина строки "THREADS=" не 6, а 8 символов. В остальных 43 случаях ошибка была найдена в самом последнем скопированном блоке. На первый взгляд, число 43 совсем немного больше 41. Но учтите, что однотипных блоков бывает достаточно много. И ошибка может быть в первом, втором, пятом или даже в десятом блоке. Получаем относительно ровное распределение ошибок в блоках и резкий скачок в конце. В среднем я взял, что количество однотипных блоков равно 5. Получается, что на первые 4 блока приходится 41 ошибка. Или около 10 ошибок на один блок. На последний пятый блок приходится 43 ошибки! Для наглядности можно построить вот такой приблизительный график: ![]() Рисунок 1. Приблизительный график количества ошибок в пяти блоках похожего кода. Получается: Вероятность допустить ошибку в последнем скопированном блоке в 4 раза больше, чем в любом другом. Каких-то грандиозных выводов я из этого не делаю. Просто интересное наблюдение. С практической точки зрения полезно знать о нём. Тогда можно заставить себя не расслабляться в самом конце. Примеры Осталось убедить читателей, что это не мои фантазии, а реалии жизни. Для этого я продемонстрирую примеры, которые подтвердят мои слова. Все примеры я, конечно, приводить не буду. Ограничусь наиболее простыми или показательными. Source Engine SDK
В конце нужно было вызвать функцию SetW(). Chromium
Совпадает последний и предпоследний блок. ReactOS
Multi Theft Auto
Последняя строчка написана по инерции и является лишней. В массиве всего 3 элемента. Source Engine SDK
В последней строке забыли заменить "BackgroundColor.y" на "BackgroundColor.z". Trans-Proteomic Pipeline
В последнем условии забыли заменить "prob > max6" на "prob > max7". SeqAn
SlimDX
В последней строке надо было использовать массив rglFSlider. Qt
В самом последнем блоке забыли про 'patternRepeatX'. Должно быть:
ReactOS
Переменная 'mjstride' будет всегда равна единицы. Последняя строчка должна быть такой:
Mozilla Firefox
Подозрительная строка "ftp" в конце. С этой строкой уже сравнивали. Quake-III-Arena
Не проверили значение из ячейки dir[2]. Clang
В самом конце выражение "SM.getExpansionColumnNumber(ContainerREnd)" сравнивается само с собой. MongoDB
Забыли в самом конце про "r.". Unreal Engine 4
В последней строке забыли сделать 2 правки. Во-первых, надо заменить ">=" на "<=. Во-вторых, заменить минус на плюс. Qt
В самом последнем вызове функции qIsFinite, нужно использовать в качестве аргумента переменную 'h'. OpenSSL
Длина строки "BITLIST" не 3, а 7 символов. На этом остановимся. Думаю, приведённых примеров более чем достаточно. Заключение В этой статье вы узнали, что при использовании Copy-Paste вероятность допустить ошибку в последнем скопированном блоке в 4 раза выше, чем в любом другом. Эта особенность психологии человека, а не его профессиональных навыков. В статье мы увидели, что к тяге к ошибкам в конце склонны даже высококвалифицированные разработчики таких проектов, как Clang или Qt. Надеюсь моё наблюдение окажется полезным. И, возможно, подтолкнет людей к исследованию накопленной базы ошибок. Думаю, она позволит найти много интересных закономерностей и сформулировать новые рекомендации для программистов. --------------------
Карпов Андрей, DevRel в PVS-Studio. |
||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
NoviceF |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 313 Регистрация: 13.3.2012 Где: Ростов-на-Дону Репутация: нет Всего: 2 |
Интересное наблюдение, спасибо.
|
|||
|
||||
TarasProger |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 104 Регистрация: 5.8.2015 Репутация: нет Всего: нет |
Я иногда допускаю подобные ошибки. Причём, все случаи, которые помню, содержали ошибку в середине последней строки, содержащей сразу много операций.
|
|||
|
||||
![]() ![]() ![]() |
Правила форума "С++:Общие вопросы" | |
|
Добро пожаловать!
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |