Поверь в статический анализ кода!

Решил кратко сформулировать 3 причины, по которым embedded-разработчикам полезны инструменты статического анализа кода программ.

Первая причина: не надо тратить время на мучительный поиск некоторых ошибок


Статический анализ кода == удешевление процесса тестирования и отладки устройства. Чем раньше ошибка найдена, тем дешевле её исправление. Статический анализ находит ошибки ещё на этапе написания кода или, по крайней мере, во время ночных запусков на сервере. В итоге, поиск и исправление многих ошибок обходится гораздо дешевле.

Особенно полезен статический анализ может быть при отладке embedded-систем. В таких проектах разработчики сталкиваются не только с ошибками в программах, но и с ошибками в самом устройстве или с некачественным изготовлением макета (плохой контакт и т.п.). В результате, процесс поиска ошибки может сильно затянуться, так как часто непонятно, где её искать. Если программист посчитает, что код написан правильно, то это может повлечь долгие изыскания с привлечением схемотехников и других коллег, отвечающих за hardware-часть. Тем неприятней будет позже вернуться к коду программы и обнаружить, наконец, дурацкую опечатку. Колоссально неэффективный расход сил и времени коллектива. Великолепно, если такую ошибку найдёт статический анализатор.

Вот как один знакомый описывал мне подобную ситуацию:

«Еще будучи магистром, я начал работать в компании, занимающейся изготовлением на заказ различных мелкосерийных устройств. Например, автоматизация парников или сбор информации с датчиков на предприятии, что нигде ничего не протекло и не перегрелось.

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

И я забываю про это дело. Есть другие большие и более интересные задачи. Тем более, раз они не пришли, значит всё хорошо.

А они пришли. Но только через неделю. Говорят, ничего не понимаем. Всю голову сломали. Не работает наш стенд. Вернее, работает, но не совсем как надо. Мы уже перепаяли его заново и исполнительные электромеханические детали заменили. Не работает… Может, посмотришь? Может, всё-таки, в программе что-то не так…

Открываю код и сразу вижу ошибку в духе:

uchar A[3];
....
for (uchar i = 0; i != 4; i++)
  average += A[i];
average /= 3;

За основу был взят другой мой проект, и код во многом написан методом Copy-Paste. В одном месте я забыл заменить 4 на 3. Мне так стыдно было, что я двух людей заставил проработать впустую неделю.»

Вторая причина: обновить программу дорого, невозможно или поздно


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

  1. Смириться и получать негативные отзывы клиентов на различных сайтах, портя свою репутацию. Можно, конечно, выпустить и разослать к инструкции приложение «не делайте вот так», но тоже слабый вариант.
  2. Отозвать серию и заняться обновлением прошивок. Дорогое развлечение.

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

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

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

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

Третья причина: программист может не знать, что делает что-то неправильно


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

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

Другим примером может служить неопределённое поведение программы, которое возникает из-за неправильного использования операторов сдвига <</>>. Эти операторы очень широко используются в коде микроконтроллеров. К сожалению, программисты часто используют эти операторы крайне безалаберно, делая программы ненадёжными и зависимыми от версии и настроек компилятора. При этом программа вполне себе может работать, но вовсе не потому, что написана правильно, а потому, что везёт.

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

Заключение


Есть ещё одна причина в обязательном порядке использовать статический анализатор кода. Это когда проект должен соответствовать определённому стандарту разработки программного обеспечения на языке, например, MISRA C. Однако это, скорее, относится к административным мерам, и лежит немного в стороне от обсуждаемой темы.

Я хотел показать, что использование статического анализатора однозначно целесообразно для любого встраиваемого проекта. Используйте эту методологию, и вы сможете:

  1. Сократить время на поиск и устранение ошибок (пример);
  2. Уменьшить вероятность критических ошибок;
  3. Уменьшить вероятность необходимости обновления прошивок;
  4. Контролировать общее качество кода (рекомендуем дополнительно посмотреть в сторону SonarQube);
  5. Контролировать качество работы новых членов команды;
  6. Контролировать качество кода сторонних модулей/библиотек.

Нет причин не использовать статический анализ кода, кроме лени и иллюзии превосходства.

Нам некогда использовать статический анализ кода


Используйте статические анализаторы кода! Их много.

Мы, естественно, предлагаем обратить внимание на наш анализатор кода PVS-Studio, который недавно начал поддерживать ряд ARM-компиляторов.


Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. Why embedded developers should use static code analysis.

Комментарии (47)


  1. igor_suhorukov
    12.03.2018 23:04

    Вот, наконец-то SonarQube упомянули! Когда можно будет сравнить ваш java анализатор с его бесплатным и встроенным?


    1. panchmp
      12.03.2018 23:23

      PVS-Studio не анализирует Java код


      1. igor_suhorukov
        12.03.2018 23:42

        Да, вы правы. Но была публикация «PVS-Studio 2018: CWE, Java, RPG, macOS, Keil, IAR, MISRA» где анонсировали возможное появление анализатора для java. Мы дискутировали в комментариях, что эта ниша уже насыщена достаточно качественными бесплатными анализаторами.


        1. Andrey2008 Автор
          12.03.2018 23:47

          Так ведь и ниша C++ насыщена бесплатными анализаторами (Cppcheck, Clang, ...). :)


          1. igor_suhorukov
            13.03.2018 00:44

            Надо пообщаться с коллегами, почему они остановили свой выбор именно на pvs-studio для C++. Возможно причина не только в хорошем маркетинге. Статьи тут очень часто отличные!


    1. Andrey2008 Автор
      12.03.2018 23:39
      +2

      К сожалению, на Java направление у нас пока выделено очень мало ресурсов. Так что ничего пока сказать не могу. Сейчас мы сосредоточены над C++: поддержкой macOS, классификацией предупреждений согласно CERT, внедрением в ядро символьных вычислений, embedded.


      1. igor_suhorukov
        12.03.2018 23:44

        Уверен, что с embedded вашей компании лучше удастся продавать анализатор! Рынок важен с точки зрения надежности и высокой стоимости доставки обновлений. В области статического анализа Java кода достаточно высокая конкуренция…


        1. Andrey2008 Автор
          12.03.2018 23:51

          Как будто в области статичекого анализа для C++ конкуренции нет… :)
          List of tools for static code analysis: C, C++; Multi-language.


    1. Andrey2008 Автор
      12.03.2018 23:46

      1. igor_suhorukov
        13.03.2018 00:40

        Это же отлично, что упоминаете удобный инструмент статического анализа кода и continuous code quality! По прочтению этой публикации почему-то вспомнил ваш комментарий:

        sonar — использовали в одном из проектов. Не впечатлил.


  1. alexxisr
    13.03.2018 05:40
    +1

    Может быть вы объясните это острое нежелание показывать цены у отечественных производителей? Почему надо обязательно писать менеджеру, чтобы просто узнать цену?


    1. Andrey2008 Автор
      13.03.2018 09:48

      Отечественность производителя здесь ни при чём, тем более, что мы ориентированы на иностранных клиентов. Есть классы продуктов, где невозможно просто написать цену, так как она формируется из количества лицензий, скидок при покупке на несколько лет, наценки реселлера и так далее. По этой же причине, вы не увидите цены для таких статических анализаторов как Coverity, Klocwork, Parasoft.

      Ориентировочную же цену вполне можно прикинуть, изучив страницу "Купить".


  1. staticlab
    13.03.2018 08:43

    Сможет ли PVS-Studio выловить баг при копипасте, если код будет следующим?


    uchar A[3];
    ....
    for (uchar i = 0; i != 3; i++)
      average += A[i];
    average /= 4;

    То есть изначально код был на 4 элемента, его скопипастили и в одном месте забыли заменить 4 на 3.


    1. Andrey2008 Автор
      13.03.2018 09:37

      Нет. Непонятно, к чему придраться. Можно конечно сделать жуткое эмпирическое правило, основанное на названии переменной в которой есть «average», но на практике, это всё равно не будет работать. Динамический анализ здесь тоже не поможет. Подобные ошибки следует выявлять юнит-тестами. Именно из-за разнообразия ошибок, все эти методологии поиска ошибок не конкурируют, а дополняют друг друга.

      Профилактикой может быть правило в стандарте кодирования компании или просто любовь к написанию качественного кода, что не позволяет использовать такие явные константы как 3, 4. Использовалась бы именованная константа, и беды не было.

      P.S. Есть ещё доказательство корректности программ и соответствующие инструменты, но это слишком медленно и дорого для подавляющего большинства проектов. Формализация (описание) как работает функция занимает больше, чем сама функция. Плюс там есть масса сложностей, с такими языками как C++.


      1. staticlab
        13.03.2018 10:59

        А какова вероятность, что разработчики, у которых не принята культура юнит-тестирования (а если бы это было не так, то и оригинальный баг из статьи скорее всего отловили бы), приобретут и будут систематически пользоваться (не говоря уже о CI) статическим анализатором?


        1. Andrey2008 Автор
          13.03.2018 11:09

          Нерационально обсуждать тему, что всё плохо и что всё тлен. Давайте лучше по возможности доносить до программистов мысль, что качество кода — это очень важно, особенно когда речь идёт о управлении чем-то внешним. Я, например, в статьях рассказываю об одной из методологий — статическом анализе кода. Предлагаю и другим нести свет в царство тьмы. Особенно бывает темно как раз в мире embedded-систем. Кстати, у меня уже была заметка на эту тему: "Заземлённые указатели".


        1. da-nie
          13.03.2018 16:18
          +1

          Кто бы рассказал, как это самое unit-тестирование сделать для встраиваемых проектов (для обычного PC, как я понимаю, это может делать некий фреймворк или IDE (я нифига не нашёл в инете unit-тестирование не с помощью фреймворков и не для абстрактной простой программы. Так оно для меня и осталось загадкой...)? Иначе протестировать сотню функций, делая по программе-тесту на каждую (100 штук!) я даже не представляю как! )? Вот есть у меня программа для микроконтроллера, которая, вообще говоря, нифиговая в принципе и в логике работы и в размерах (потому что это прошивка одного из 4 взаимодействующих между собой через ДОЗУ контроллеров платы прибора). Я бы с радостью тестировал бы всё это, но как? Там даже не Си++, а просто Си.


          1. F0iL
            13.03.2018 19:36

            Разделять программу на обособленные блоки (как вариант, в ООП-языках — классы, на Си, в принципе, тоже можно писать в объектном стиле с некоторыми ухищрениями), разделять логику работы и слой взаимодействия непосредственно с железом (HAL)…
            … и писать портируемый код, чтобы обособленные блоки, реализующие логику, можно было компилировать и запускать на ПК по тестовым сценариям используя любой тестовый фреймворк.
            Ну или гонять тесты на самой борде, но это может оказаться уже сложнее.
            В принципе, если для вас «архитектура» и «стандарты написания кода» — не пустой звук, то скорее всего пункты из первого абзаца у вас уже выполняются (надеюсь, у вас не проект с божественными объектами и сущности на сотни строк).

            А самый простой тестовый фреймворк — набор макросов для запуска выбранной функции с определенными комбинациямиаргументамов и проверки, что получилось на выходе или что поменялось в окружении после работы.


            1. da-nie
              14.03.2018 09:13
              +2

              Я не понимаю вот что: если я каждую функцию/класс обложу тестами с окружением всего, что ей нужно, то тест окажется больше самой программы в несколько раз/десятки рах! А привязку к HAL придётся делать указателями на функции HAL (иначе их не подменить).
              Вот сейчас для проекта у меня ПО микроконтроллера как раз и пишется на обычном PC (под QNX) на Си с обвязкой на Си++ с имитацией драйверами всего окружения аппаратуры и имитацией HAL. Всё вроде бы хорошо, но один из процессоров необычный (отечественный аналог TMS середины 80-х) — у него байт 32 бита (да, и sizeof(char)=1 и char=32 бита), компилятор с Си 89 (а может и раньше) и глючной работой кое-чего в Си ( скажем, int a[2]={1,2}; компилируется, но инициализация не выполняется :) ). Как это всё поведёт себя на реальном процессоре — вопрос.

              А что касается тестов вообще, я не понимаю, как всё это обложить тестами правильно. Писать ли для каждой функции/класса отдельную программу? Тогда программ будет масса — ведь каждому классу нужно имитировать окружение. Или тест надо встраивать в саму программу — это приведёт к тому, что программа резко распухнет и будет сложно разделить тестовую часть от штатной. А проверка логики работы будет заключаться во взаимодействии классов и потребует имитации этой же логики в тесте (иначе как проверить, что логика отработала верно). Вот всего этого я и не понимаю.


              1. F0iL
                14.03.2018 11:59
                +2

                Я не понимаю вот что: если я каждую функцию/класс обложу тестами с окружением всего, что ей нужно, то тест окажется больше самой программы в несколько раз/десятки рах!

                Да, это нормально, так и должно быть.
                Посмотрите, например, на проект SQLite: у них 125 тысяч строк кода непосредственно библиотеки, и 91 миллион строк кода тестов — то есть тесты занимают примерно в 750 раз больше по объему, чем сама либа :)
                А привязку к HAL придётся делать указателями на функции HAL (иначе их не подменить).

                Да, как один из вариантов. Или более тупой и менее красивый, просто include'ить другой файл с теми же сигнатурами методов в зависимости от define'а при сборке.
                А что касается тестов вообще, я не понимаю, как всё это обложить тестами правильно. Писать ли для каждой функции/класса отдельную программу?

                Не совсем понял, что вы понимаете под «отдельной программой», я бы сказал, для каждой функции или класса — отдельный тестовый метод. Вообще, про тестирование есть довольно много литературы и обучающих курсов, загляните, если интересно.
                А проверка логики работы будет заключаться во взаимодействии классов и потребует имитации этой же логики в тесте (иначе как проверить, что логика отработала верно).

                А тут работают буквы S и D в принципе SOLID: single responsibility и dependency injection. У одной единицы — одна ответственность, следовательно, ее можно протестировать не затрагивая и не задействуя другие сущности. Ну и всегда есть mock- и stub-объекты, которые можно использовать вместо реальных, чтобы не воспроизводить окружение целиком.


                1. da-nie
                  14.03.2018 13:00

                  Да, это нормально, так и должно быть.


                  Я ж застрелюсь. :)

                  Не совсем понял, что вы понимаете под «отдельной программой»,


                  Я имею в виду, что нужно ли делать отдельный тестовый стенд (программу) для каждого класса. Тесты же нужно откуда-то вызывать.

                  Вообще, про тестирование есть довольно много литературы и обучающих курсов, загляните, если интересно.


                  Спасибо за информацию. :) Статьи я читал, но всё равно масса непоняток для реального проекта остаётся.

                  У одной единицы — одна ответственность,


                  Часто при таком подходе трудно запомнить связи между сущностями. Скажем, у меня есть менеджер управления электропитанием, менеджер времени, менеджер имитации ошибок, менеджер обмена с аппаратурой и так далее. Так вот, удобно их связывать воедино с помощью одного класса-системы. Иначе связь между этими менеджерами трудно отследить (а через класс системы — просто — все обращаются к нему и он переадресует).


                  1. F0iL
                    14.03.2018 13:22

                    Я ж застрелюсь. :)

                    Не надо :) Никто не говорит, что нужно бросаться и сразу же писать тесты вообще для всего. Как вариант, начать или с критически важных функций, или с тех мест, в которые часто вносятся изменения (чтобы тестами следить, не появились ли регрессии после правок).
                    Я имею в виду, что нужно ли делать отдельный тестовый стенд (программу) для каждого класса. Тесты же нужно откуда-то вызывать.

                    Это уже как вам удобнее. Можно делать отдельную «программу» на группу классов, объединенных по какому-либо признаку.
                    . Так вот, удобно их связывать воедино с помощью одного класса-системы. Иначе связь между этими менеджерами трудно отследить (а через класс системы — просто — все обращаются к нему и он переадресует).

                    Как я понял, вы описываете что-то вроде шаблона "Фасад".
                    Тут вариантов несколько, или при написании тестов реально держать связи в голове, или же дергать также за «класс-систему», но отдельные части системы подменять на фейковые, или не подменять вообще. Это уже не юнит-тесты, а интеграционные получаются.


                    1. da-nie
                      14.03.2018 16:10

                      Спасибо, стало понятнее. :)


              1. igor_suhorukov
                15.03.2018 09:14

                Если выполняете юнит тестирования то почитайте про fake, mock, stub, dummy. К тому же код должен быть организован с использованием dependency injection что упростит тестирование.


                1. da-nie
                  15.03.2018 09:50

                  К сожалению, всё это всего лишь общие фразы. Без внятного объяснения реализации на настоящем примере ( без каких-либо вспомогательных фреймворков и программ ) это бесполезно. Потому я и не понимаю, как это реально делать.


        1. Xop
          13.03.2018 22:08

          Ну кстати шанс как раз есть — юнит тесты мало того что писать надо, но ещё и код делать тестируемым, а с анализатором нужно просто запустить его.


      1. WinPooh73
        13.03.2018 14:51

        > Использовалась бы именованная константа, и беды не было.

        Даже с именованной константой никто не застрахует от возможности написать что-то вроде:
        for (int i = 0; i <= NUM_ELEMENTS; ++i) {… }


        1. Andrey2008 Автор
          13.03.2018 14:54

          Да. Однако, думаю вы согласитесь, что вероятность сделать ошибку уменьшается. Собственно, любые рекомендации написания кода не гарантируют отсутствие ошибок, но снижают вероятность их появления.


    1. eao197
      13.03.2018 14:30

      Статические анализаторы здесь могли бы помочь, если бы они выдавали предупреждения на код, написанный в таком «классическом» стиле. Чтобы разработчик задумался и переписал бы код, например, так:

      array<uchar, 3> A;
      ...
      const auto average = accumulate(begin(A), end(A), 0) / A.size();


  1. KodyWiremane
    13.03.2018 09:24
    +1

    В общем, «потому же, что и не-embedded разработчикам, но с поправкой на embedded специфику».

    По делу: ваш блог стоит читать уже хотя бы ради КДПВ!) Ну и образцы косяков культуру кода поднимают.


  1. multiprogramm
    13.03.2018 12:10
    +1

    Отличная иллюстрация зла магических чисел получилась.


    1. technic93
      13.03.2018 15:45

      Да надо было


      const size_t ASize = 3;
      uchar A[ASize];
      ...

      Или enum, что лучше? Можно и sizeof но с ним легко выстрелить себе в голову.


  1. Falstaff
    13.03.2018 14:02

    Примером может служить использование 32-битного типа time_t

    Просто ради полноты картины, компиляторы поновее уже могут определять time_t как 64-битный тип (gcc 7.x для arm-none-eabi, например, определяет как __int_least64_t).


  1. Whuthering
    13.03.2018 14:52
    +1

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

    "… многие «железячники» считают, что производство устройства — это искусство, подвластное избранным, а вот написать к нему код он сможет сам, так, на коленке. Это ж вообще мелочь. Получается работающий тихий ужас. Они очень обижаются, когда им на пальцах объясняют, почему их код дурно пахнет, потому что… ну… они ж железку сделали, че тут, программа какая-то."


  1. Ryppka
    13.03.2018 15:13

    for (uchar i = 0; i != SOME_ARRAY_SIZE; i++)…

    Такой цикл у меня одного вызывает сомнение?


    1. technic93
      13.03.2018 15:48

      Выглядит странно. А если ещё вдруг захочиться пропустить несколько элементов в теле цикла сделав там i += x то всё.


    1. Andrey2008 Автор
      13.03.2018 15:53

      Отчасти, это дело вкуса. Например, я обычно пишу как раз !=. Причина: единообразие циклов, использующих итераторы или простые целочисленные типы. Тем не менее, я понимаю, что это плохо и наверное откажусь от такой практике. Например, CERT однозначно не рекомендует использовать != (MSC21-C).


  1. sibnick
    13.03.2018 19:25

    Спрашивал как-то одного товарища из авионики про анализаторы. Ответ был простой: не используем поскольку все использованные инструменты также должны быть сертифицированны.


    1. Andrey2008 Автор
      13.03.2018 19:26

      Странный ответ. А я думал, что это царство DO-178C, Misra и подобных стандартов. И они просто обязаны использовать инструментарий для статического анализа кода.


      1. F0iL
        13.03.2018 19:44
        +1

        Есть опыт разработки в авиации, да, все верно, стандарты там весьма серьезные.
        Товарищ, видимо, имел в виду, что при разработке проекта все используемые библиотеки и фреймворки тоже должны быть сертифицированы и верифицированы, и это действительно часто так,
        но причем здесь упомянут статический анализатор кода, который никакого непосредственного влияния на результат не оказывает и в кодогенерации не участвует, для меня тоже осталось непонятным. Мы cppcheck, clang-analyzer и valgrind (да, я знаю, что он динамический, но тоже анализатор и тоже не сертифицированный) без проблем использовали.


  1. IronHead
    14.03.2018 14:31

    Наконец то свершилось, PVS для IAR.
    Но
    Сразу же всплыла ложка дегтя
    В моем проекте нашлась ошибка:

    V512. A call of the 'Foo' function will lead to a buffer overflow or underflow.

    Перехожу в описательную часть с примерами таких ошибок и вижу пример от PVS-Studio как делать не надо:
    Sample N2.
    #define CONT_MAP_MAX 50
    int _iContMap[CONT_MAP_MAX];
    memset(_iContMap, -1, CONT_MAP_MAX);
    In this sample, the size of the buffer to be filled is also defined incorrectly.

    Вместо этого предлагается такой код (как «надо делать» по версии PVS):
    This is the correct version:
    #define CONT_MAP_MAX 50
    int _iContMap[CONT_MAP_MAX];
    memset(_iContMap, -1, CONT_MAP_MAX * sizeof(int));

    Вот тут то и закралось в голове: а что, если завтра в таком проекте мне необходимо будет исправить
    int _iContMap[CONT_MAP_MAX];
    на
    char _iContMap[CONT_MAP_MAX];
    ???

    Выводы для сотрудников PVS-Studio:
    Проверяйте примеры, которые выдаете за эталон «Как надо»

    Выводы для всех остальных:
    Не копируйте бездумно то решение, которое выдают за эталон.


    1. xi-tauw
      14.03.2018 15:00
      +2

      С sizeof это уже довольно древний спор на тему того что правильнее — sizeof(тип) или sizeof(переменная).

      Если кодстайл принуждает к sizeof(тип), то приводят примеры как у вас.
      int a;
      memset(&a, 0, sizeof(int));

      Меняем тип в объявлении переменной
      char a;
      memset(&a, 0, sizeof(int));

      и теперь memset пишет вне переменной.

      А если кодстайл принуждает к sizeof(переменная), то пример такой:
      Было:
      int a[100];
      memset(a, 0, sizeof(a));

      Делаем выделение памяти динамическим:
      int* a = (int*)malloc(100);
      memset(a, 0, sizeof(a));

      Теперь память недозаполняется.

      Получается как не пиши код, можно придраться.


      1. Falstaff
        14.03.2018 16:35

        Есть, кстати, трюк, который позволяет хоть немного, но защититься от проблемы со взятием sizeof() от указателя вместо массива. У гугла в коде такое можно встретить:


        template <typename T, size_t N>
        char (&ArraySizeHelper(T (&array)[N]))[N];
        
        #define arraysize(array) (sizeof(ArraySizeHelper(array)))

        И дальше можно использовать arraysize() — он не скомпилируется, если ему дать указатель.


        1. Andrey2008 Автор
          14.03.2018 17:02

          Подробнее про эту тему см. "Главный вопрос программирования, рефакторинга и всего такого", глава — 23. Вычисляйте длину строкового литерала автоматически.


    1. Andrey2008 Автор
      14.03.2018 16:03

      (del)


    1. Andrey2008 Автор
      14.03.2018 16:04

      Как уже отметили выше, не так просто предложить красивый вариант исправления ошибки. И на самом деле, такой задачи не ставится. Для написания красивого и надёжного кода, есть такие книги как «Совершенный код». Задача документации — просто показать какой-то вариант исправления. :)


  1. l0rda
    15.03.2018 18:23

    Embedded разработчики очень хотят pvs-studio, но разработчки pvs-studio не хотят собирать софт под x86, а на x86_64 не работает сборка. Замкнутый круг.