Pull to refresh

Comments 29

> Даже не знаю, что тут можно посоветовать. Поэтому просто пожелаю разработчикам Amazon Lumberyard успехов в исправлении ошибок, а программисту Игорю – удачи!

Ну судя по этому коду товарищ Игорь Лобанчиков в целом широко отметился в этом проекте. Но — не оценили его комрады…
Amazon Lumberyard разрабатывается как кроссплатформенный движок. Поэтому разработчики стараются поддерживать как можно больше компиляторов.


Я надеюсь, было понятно, что это не С или С++ компилятор, так что странно читать о кроссплатформенности. Тут речь идет о компилятореGLSL для шейдеров, код которых и формирует та самая функция.

Программа, которая например умеет работать с ATI и NVidia драйверами одновременно, вовсе не обязательно является кроссплатформенной.
Вот еще прекрасное из кода библиотеки zlib 1.2.11, которая используется в миллионе разных проектов:
#define CLEAR_HASH(s) \
    s->head[s->hash_size-1] = NIL; \
    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));

...

if (s->matches == 1)
    slide_hash(s);
else
    CLEAR_HASH(s);

Т.к. скобки угловые забыли поставить и в макросе, и в else, то внутрь попадает только первая строка макроса, а вторая исполняется независимо от условия s->matches == 1.
Случай там довольно редкий (поэтому оно и ехало незаметно до 2018 года), но отлаживать такое было бы весьма забавно, и когда тебе о таком вот сообщает программа — это приятно.
Это всё от злоупотреблений макросами :). Пример прям в тему. Как раз сегодня гостевой пост "Macro Evil in C++ Code" на эту тему опубликовал. Скоро на русском на Хабр его выложу.
Ну и еще от злоупотребления опусканием скобок потому что «там одна строка всего, а тут два символа дополнительно набирать!!!». Понаступаешь на эти грабли любовно разложенные лет 5, и начинаешь ставить по три пары скобок — на всякий случай. :)
Ну в общем то товарищи Страуструп и Саттер например тут уже все сказали на эту тему :)
За пятое место срабатывание «просто повезло», т.к CursorShowing могла быть и 4.

Должна быть другая диагностика — обнаружение минимально отличающихся именами переменных/типов/классов/enum…

Я про это писал в «Надежном программировании»
Подскажите, пожалуйста, по 4 месту, может я чего-то не знаю.
fgets возвращает строку с \n или возвращает null, если поток больше не содержит данных (в нашем случае stdin перенаправлен на файл), получается записывает в command_buf как минимум один символ (перевод строки). Т.е. strlen(command_buf) будет всегда >= 1, если fgets вернул не указатель на command_buf. Разве нет?
Если ты ничего не введешь и нажмешь Enter, то fgets считает символ переноса строки и вернет указатель на "\n\0".
Но в примере, который демонстрируется на видео, вводится не пустая строка, а строка, которая начинается с символа '\0'. Можно подробнее почитать об этом по ссылке на оригинальную статью.
UFO just landed and posted this here
Файл вполне может содержать терминальные нули. Например, это может использоваться, чтобы удобно разбить содержимое буфера на строки.
Да, содержимое файла может быть длиннее буфера. Именно поэтому вторым параметром функция fgets принимает число, ограничивающее количество считываемых символов.
UFO just landed and posted this here

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


Похоже, код просто не предполагает то, что stdin перенаправлен на файл. А если поток перенаправлен и в нем в последней строке не окажется \n, то последний символ удалится — больше похоже на note, чем ошибку.
Это и удивило, что такой пустяк на 4й позиции ошибок оказался, хотя это и вовсе не ошибка, если код предназначен для работы исключительно из консоли и исключительно для ввода текстовых команд. Думаю, не стоит брать в счёт то, что интерпретатор cmd умеет такую дичь делать, как на видео. Если такое брать во внимание, то вообще fgets для stdin использовать нельзя окажется.


Полагаю, время писать статью "Как VasRedDoor и zhekov оказлись внимательнее PVS Studio и 3х с половиной человек".


Всем спасибо за ответы!

UFO just landed and posted this here
Недавно 8-е место было для меня первым :).

Только изменение контейнера при его обходе происходило не так явно, а на 5-6-м уровне глубины обработки… Мозг сломал, пока нашел бяку…

Ну и 10-е место надо держать в голове. Обычно я так не делаю (конструктор из конструктора), поэтому пока и не натыкался, но все-таки интересная ситуация…

А макросы, если сложнее одной строки, это зло.

На месте предыдущей строки кода Святослав обнаружил вот это:
(((targ)->sv_flags & 0x00000400) && (!((targ)->sv_flags & 0x00200000) ||
S_sv_only_taint_gmagic(targ)) ? (mg)->mg_len = ((prog->offs)[0].end),
(mg)->mg_flags |= 0x40 : ((mg)->mg_len = (((targ)->sv_flags & 0x20000000)
&& !__builtin_expect(((((PL_curcop)->cop_hints + 0) & 0x00000008) ?
(_Bool)1 :(_Bool)0),(0))) ? (ssize_t)Perl_utf8_length( (U8 *)(truebase),
(U8 *)(truebase)+((prog->offs)[0].end)) : (ssize_t)((prog->offs)[0].end),
(mg)->mg_flags &= ~0x40));

Выглядит как Перл… Батюшки, да это ж и есть Перл!
Я эту штуку целый час разглядываю. Надо сказать, что остальные варианты весьма банальны.
Если вводится не текст, то это уже не текстовый поток, использовать fgets с ним нельзя в принципе. Ожидается текст, а анализатор предлагает костыли писать к fgets, как то странно. Если анализатор предполагает, что stdin может быть бинарный потоком, то уже предлагать менять fgets на fread надо.
Спасибо за ответ.
У меня недавано пару раз проскакивала ошибка, вызов resize вместо reserve для контейнеров, как так поулчалось, ума не приложу, видимо подсказка на «r». Такая ошибка может жить довольно долго.
std::vector<int> v;
v.resize(some_value);
8-е место явно какая-то невалидная проблема. В случае с foreach у меня никаких проблем не возникает, но даже в случае с итераторами, если место под vector зарезервировать, проблем возникнуть не должно. Т.е. если вектор пересоздается, по логике вещей должны быть проблемы, если нет — почему проблема должна возникнуть?

А как на этапе написания кода гарантировать, что он не пересоздастся?

UFO just landed and posted this here
Не может быть, чтобы константа CursorShowing равнялась 0, ведь буквально парой строк выше она инициализирована значением 1.

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


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

PVS-Studio умеет встраиваться в различные IDE. Например, в виде плагина для Visual Studio или IntelliJ IDEA. Поэтому искать ошибки и просматривать отчет можно не выходя из IDE. Поэтому перейти к определению символа можно без всяких проблем.

Почему же пришлось так долго разбираться с этой проблемой?

Sign up to leave a comment.